commit a0722c14d8ef205ab6740e3aaf05c79c9c0792a3 Author: Jesse McDonald Date: Tue Mar 10 01:54:08 2020 -0500 initial commit diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..432a1ff --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "jquery-ui"] + path = jquery-ui + url = https://github.com/jquery/jquery-ui.git +[submodule "jquery-ui-touch-punch"] + path = jquery-ui-touch-punch + url = https://github.com/furf/jquery-ui-touch-punch.git +[submodule "gun"] + path = gun + url = https://github.com/amark/gun diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..cec9c1b --- /dev/null +++ b/.htaccess @@ -0,0 +1 @@ +Header set Cache-Control "max-age=10,public" diff --git a/css/chess.css b/css/chess.css new file mode 100644 index 0000000..645bee7 --- /dev/null +++ b/css/chess.css @@ -0,0 +1,130 @@ +#cb_outer2 { + max-width: 80vmin; +} + +#cb_outer { + max-width: 6in; +} + +#cb_container { + position: relative; + width: 100%; + padding-top: 100%; +} + +#cb_inner { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +#cb_board { + position: relative; + width: 100%; + height: 100%; + border: 1px solid black; + border-collapse: collapse; + padding-top: 100%; +} + +#cb_board tr { + position: relative; +} + +.cb-square { + position: relative; + width: calc((100% - 16pt) / 8); + height: calc((100% - 16pt) / 8); + border: 1px solid black; + padding: 0; +} + +.cb-horiz-label { + position: relative; + width: calc((100% - 16pt) / 8); + height: 8pt; + border: 1px solid black; + padding: 0; +} + +.cb-horiz-label div { + position: absolute; + top: calc(50% - (1em / 2)); + left: 0; + bottom: 0; + right: 0; + font-size: 6pt; + line-height: 6pt; + text-align: center; +} + +.cb-vert-label { + position: relative; + width: 8pt; + height: calc((100% - 16pt) / 8); + border: 1px solid black; + padding: 0; +} + +.cb-vert-label div { + position: absolute; + top: calc(50% - (1em / 2)); + left: 0; + bottom: 0; + right: 0; + font-size: 6pt; + line-height: 6pt; + text-align: center; + vertical-align: middle; +} + +.cb-lt-bg { + background-color: #FFFFF0; +} + +.cb-lt-bg.cb-start, .cb-lt-bg.cb-end { + background-color: #CCFFC0; +} + +.cb-dk-bg { + background-color: #F5DEB3; +} + +.cb-dk-bg.cb-start, .cb-dk-bg.cb-end { + background-color: #D0E398; +} + +.cb-phantom { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(128,128,128,0.5); +} + +.cb-dk-piece { + position: absolute; + top: 17.5%; + left: 47.5%; + width: 55%; + height: 65%; +} + +#cb_phantom > .cb-dk-piece { + left: 22.5%; +} + +.cb-lt-piece { + position: absolute; + top: 17.5%; + left: -2.5%; + width: 55%; + height: 65%; +} + +#cb_phantom > .cb-lt-piece { + left: 22.5%; +} diff --git a/gun b/gun new file mode 160000 index 0000000..fbcb369 --- /dev/null +++ b/gun @@ -0,0 +1 @@ +Subproject commit fbcb369aa81b546d0f84cabe7b5a66721a65371b diff --git a/index.html b/index.html new file mode 100644 index 0000000..a6cbef7 --- /dev/null +++ b/index.html @@ -0,0 +1,216 @@ + + + Paco Ŝako + + + + + +

Paco Ŝako

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
A
B
C
D
E
F
G
H
8
8
7
7
6
6
5
5
4
4
3
3
2
2
1
1
A
B
C
D
E
F
G
H
+
+
+
+
+ +
+ + + + + Light player's move + Dark player's move +
+ vs.
+ + +

+
+
+ + + +
+
+ + + + + +
+ + +
+ + + + + + + + + + + + + + diff --git a/jquery-ui b/jquery-ui new file mode 160000 index 0000000..c6f1735 --- /dev/null +++ b/jquery-ui @@ -0,0 +1 @@ +Subproject commit c6f1735249295bb37153861aa97e5b3e38d46b02 diff --git a/jquery-ui-touch-punch b/jquery-ui-touch-punch new file mode 160000 index 0000000..4bc0091 --- /dev/null +++ b/jquery-ui-touch-punch @@ -0,0 +1 @@ +Subproject commit 4bc009145202d9c7483ba85f3a236a8f3470354d diff --git a/js/chess.js b/js/chess.js new file mode 100644 index 0000000..7f358c4 --- /dev/null +++ b/js/chess.js @@ -0,0 +1,473 @@ +'use strict'; +var gun = Gun(['https://jessemcdonald.info/gun']); + +let initialBoard = (function (){ + var init = JSON.stringify({ + 'light': [ + 'rnbkqbnr', + 'pppppppp', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ], + 'dark': [ + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + 'pppppppp', + 'rnbkqbnr', + ], + 'player': 'light', + }); + return function (){ return JSON.parse(init); } +})(); + +function cloneJSON(obj){ + return JSON.parse(JSON.stringify(obj)); +} + +function normalizeSide(side){ + return (side[0] === 'd') ? 'dark' : 'light'; +} + +function otherSide(side){ + return (side[0] === 'd') ? 'light' : 'dark'; +} + +function boardGet(board, where, side){ + side = normalizeSide(side); + if (where === 'phantom') { + if (!board['phantom'] || board['phantom']['type'][1] !== side[0]) { + return ' '; + } else { + return board['phantom']['type'][0]; + } + } else { + var column = 'abcdefgh'.indexOf(where[0]); + var row = Number(where[1]) - 1; + return board[side][row][column]; + } +} + +function boardPut(board, where, side, piece){ + side = (side[0] === 'd') ? 'dark' : 'light'; + var column = 'abcdefgh'.indexOf(where[0]); + var row = Number(where[1]) - 1; + var data = board[side][row]; + var prefix = data.substring(0, column); + var suffix = data.substring(column + 1, 8); + board[side][row] = prefix + piece + suffix; +} + +function movePiece(priorBoard, side, from, to){ + var other = otherSide(side); + var type = boardGet(priorBoard, from, side); + var took = boardGet(priorBoard, to, other); + var replaced = boardGet(priorBoard, to, side); + var alongside = (from === 'phantom') ? ' ' : boardGet(priorBoard, from, other); + + var undoBoard = priorBoard; + if (undoBoard['subsequent']) { + undoBoard = cloneJSON(undoBoard); + delete undoBoard['subsequent']; + } + + var board = cloneJSON(undoBoard); + board['prior'] = undoBoard; + + board['move'] = { + 'side': normalizeSide(side), + 'type': type, + 'from': from, + 'to': to + }; + if (took !== ' ') { + board['move']['took'] = took; + } + if (replaced !== ' ') { + board['move']['replaced'] = replaced; + } + if (alongside !== ' ') { + board['move']['alongside'] = alongside; + } + + if (from === 'phantom') { + delete board['phantom']; + } else { + boardPut(board, from, side, ' '); + boardPut(board, from, other, ' '); + } + + boardPut(board, to, side, type); + + if (alongside !== ' ') { + boardPut(board, to, other, alongside); + } + + if (replaced === ' ') { + board['player'] = otherSide(board['player']); + } else { + board['phantom'] = { 'from': to, 'type': replaced + side[0] }; + } + + return board; +} + +function renderHistory(board) { + var list = []; + + while (board && board['move']) { + list.push(board['move']); + board = board['prior']; + } + + var result = ''; + var n = 0; + + while (list.length > 0) { + var move = list.pop(); + if (move['from'] === 'phantom') { + var took = move['took'] ? 'x' : ''; + result += '*' + move['type'].toUpperCase() + took + move['to']; + } else { + if (n > 0 || move['side'] === 'dark') { + result += ' '; + } + + if (move['side'] === 'light') { + ++n; + result += String(n) + '. '; + } + + if (move['pass']) { + result += '...'; + } else if (move['alongside']) { + if (move['side'] === 'light') { + result += move['type'].toUpperCase() + move['alongside'].toUpperCase() + move['from'] + move['to']; + } else { + result += move['alongside'].toUpperCase() + move['type'].toUpperCase() + move['from'] + move['to']; + } + } else { + var took = move['took'] ? 'x' : ''; + result += move['type'].toUpperCase() + move['from'] + took + move['to']; + } + } + } + + return result; +} + +function pieceStartDrag(ev, ui){ + var board = $('#cb_board').data('board'); + var dragged = $(this); + var type = dragged.data('type'); + var from = dragged.data('location'); + if (from === 'phantom' || boardGet(board, from, otherSide(type[1])) === ' ') { + var clss = (type[1] === 'd') ? '.cb-dk-piece' : '.cb-lt-piece'; + var other = (type[1] === 'd') ? '.cb-lt-piece' : '.cb-dk-piece'; + $('.cb-square').not(':has('+clss+')').droppable('enable'); + $('.cb-square').filter(':has('+other+')').droppable('enable'); + } else { + /* moving together, must go to an empty square */ + $('.cb-square').not(':has(.cb-piece)').droppable('enable'); + } +}; + +function placePiece(where, type, count){ + var piece_id = 'cb_piece_' + type + '_' + count; + var piece = $($('#' + piece_id)[0] || $('#cb_piece_' + type + ' img').clone()); + piece.attr('style', ''); + piece.attr('id', piece_id); + piece.data({ 'type': type, 'location': where }); + piece.appendTo('#cb_' + where); + piece.draggable({ + disabled: true, + containment: '#cb_inner', + revert: 'invalid', + zIndex: 100, + start: pieceStartDrag, + }); + return piece; +} + +function renderBoard(board){ + $('#cb_board .cb-piece .ui-draggable').draggable('destroy'); + $('#cb_board .cb-piece').attr('style', '').appendTo('#cb_hidden'); + $('#cb_board .cb-start').removeClass('cb-start'); + $('#cb_board .cb-end').removeClass('cb-end'); + $('#cb_phantom').appendTo('#cb_hidden'); + + for (const side of ['light', 'dark']) { + var counters = {}; + for (var row = 0; row < 8; ++row) { + for (var column = 0; column < 8; ++column) { + var here = 'abcdefgh'[column] + String(row+1); + var type = board[side][row][column]; + if (type !== ' ') { + if (!counters[type]) { + counters[type] = 0; + } + var count = ++counters[type]; + placePiece(here, type + side[0], count); + } + } + } + } + + var clss = board['player'] === 'light' ? '.cb-lt-piece' : '.cb-dk-piece'; + + if (board['phantom']) { + var where = board['phantom']['from']; + placePiece('phantom', board['phantom']['type'], 'ph'); + $('#cb_phantom').appendTo('#cb_' + where); + $('#cb_board .ui-draggable').draggable('disable'); + $('#cb_phantom .ui-draggable-disabled').filter(clss).draggable('enable'); + } else { + $('#cb_board .ui-draggable-disabled').filter(clss).draggable('enable'); + } + + if (board['move']) { + if (board['move']['from'] === 'phantom') { + $('#cb_' + board['prior']['move']['to']).addClass('cb-start'); + } else { + $('#cb_' + board['move']['from']).addClass('cb-start'); + } + $('#cb_' + board['move']['to']).addClass('cb-end'); + } + + $('#cb_' + otherSide(board['player']) + '_move').hide(); + $('#cb_' + normalizeSide(board['player']) + '_move').show(); + + $('#cb_history').text(renderHistory(board)); + + $('#cb_undo').attr('disabled', board['prior'] ? false : true); + $('#cb_redo').attr('disabled', board['subsequent'] ? false : true); + $('#cb_pass').attr('disabled', board['phantom'] ? true : false); +} + +function randomId(){ + var res = ''; + for (var i = 0; i < 4; ++i) { + var part = Math.floor(Math.random() * 65536).toString(16); + res = res + ("0000".substring(part.length, 4) + part); + } + return res; +} + +var PacoSakoUUID = '7c38edd4-c931-49c8-9f1a-84de560815db'; + +function putState(board){ + var boardElem = $('#cb_board'); + var gameId = boardElem.data('gameId'); + var game = gun.get(PacoSakoUUID).get('games').get(gameId); + game.put({ 'board': JSON.stringify(board) }); + putMeta(); +} + +function putMeta(){ + var gameId = $('#cb_board').data('gameId'); + var lightName = $('#cb_light_name').val() || 'Light'; + var darkName = $('#cb_dark_name').val() || 'Dark'; + var meta = gun.get(PacoSakoUUID).get('meta').get(gameId); + meta.put({ 'gameId': gameId, 'lightName': lightName, 'darkName': darkName, 'timestamp': new Date().getTime() }); +} + +function switchGameId(newId){ + var boardElem = $('#cb_board'); + var gameId = boardElem.data('gameId'); + + if (newId == gameId) { + return; + } + + if (gameId) { + //gun.get(PacoSakoUUID).get('games').get(gameId).off(); + //gun.get(PacoSakoUUID).get('meta').get(gameId).off(); + } + + boardElem.data('gameId', newId); + location.hash = '#/' + newId; + + /* this will be the starting state if no data is received from peers */ + var newBoard = initialBoard(); + boardElem.data('board', newBoard); + renderBoard(newBoard); + + gun.get(PacoSakoUUID).get('games').get(newId).on(function(d){ + if (d && d['board'] && $('#cb_board').data('gameId') === newId) { + var board = JSON.parse(d['board']); + $('#cb_board').data('board', board); + renderBoard(board); + } + }); + + $('#cb_light_name').val(''); + $('#cb_dark_name').val(''); + + gun.get(PacoSakoUUID).get('meta').get(newId).on(function(d){ + if (d && $('#cb_board').data('gameId') === newId) { + if (d['lightName']) { + $('#cb_light_name').val(d['lightName']); + } + if (d['darkName']) { + $('#cb_dark_name').val(d['darkName']); + } + } + }); + + var selOpt = $('#cb_game_' + newId); + if (selOpt.length === 1) { + $('#cb_select_game')[0].selectedIndex = selOpt.index(); + } else { + $('#cb_select_game')[0].selectedIndex = -1; + } +} + +$(function (){ + $('.cb-square').droppable({ + accept: '.cb-piece', + disabled: true, + deactivate: function(ev, ui){ + $(this).droppable('disable'); + }, + drop: function(ev, ui) { + var dragged = ui.draggable; + var type = dragged.data('type'); + var from = dragged.data('location'); + var to = this.id.replace(/^cb_/, ''); + dragged.appendTo('#cb_hidden'); + + var newBoard = movePiece($('#cb_board').data('board'), type[1], from, to); + putState(newBoard); + }, + }); + + $('#cb_undo').on('click', function(){ + var board = $('#cb_board').data('board'); + if (board['prior']) { + var newBoard = cloneJSON(board['prior']); + var redoBoard = cloneJSON(board); + delete redoBoard['prior']; + newBoard['subsequent'] = redoBoard; + putState(newBoard); + } + }); + + $('#cb_redo').on('click', function(){ + var board = $('#cb_board').data('board'); + if (board['subsequent']) { + var newBoard = cloneJSON(board['subsequent']); + var undoBoard = cloneJSON(board); + delete undoBoard['subsequent']; + newBoard['prior'] = undoBoard; + putState(newBoard); + } + }); + + $('#cb_reset').on('click', function(){ + putState(initialBoard()); + }); + + $('#cb_pass').on('click', function(){ + var board = $('#cb_board').data('board'); + if (!board['phantom']) { + var newBoard = cloneJSON(board); + newBoard['prior'] = board; + newBoard['move'] = { 'side': board['player'], 'pass': true }; + newBoard['player'] = otherSide(board['player']); + putState(newBoard); + } + }); + + $('#cb_select_game').on('change', function(){ + var optIndex = $('#cb_select_game')[0].selectedIndex; + if (optIndex === 0) { + switchGameId(randomId()); + } else if (optIndex >= 1) { + var opt = $('#cb_select_game option')[optIndex]; + if (opt) { + switchGameId(opt.id.replace(/^cb_game_/, '')); + } + } + }); + + let updateMeta = function() { putMeta(); } + $('#cb_light_name').on('input', updateMeta); + $('#cb_dark_name').on('input', updateMeta); + + gun.get(PacoSakoUUID).get('meta').map().on(function(d){ + if (d && d['gameId'] && d['lightName'] && d['darkName']) { + function updateTitle(opt){ + const then = opt.data('then'); + const now = new Date().getTime(); + var age_str = ''; + if (then > now) { + age_str = ' (future)'; + } else if ((now - then) < 60*60*1000) { + age_str = ' (' + Math.floor((now - then) / (60*1000)) + 'm)'; + } else if ((now - then) < 24*60*60*1000) { + age_str = ' (' + Math.floor((now - then) / (60*60*1000)) + 'h)'; + } else if ((now - then) < 14*24*60*60*1000) { + age_str = ' (' + Math.floor((now - then) / (24*60*60*1000)) + 'd)'; + } else if (d['gameId'] !== $('#cb_board').data('gameId')) { + opt.remove(); + return; + } + opt.text(opt.data('title') + age_str); + } + + var opt = $('#cb_game_' + d['gameId']); + if (opt.length === 0) { + opt = $(''); + opt.attr('id', 'cb_game_' + d['gameId']); + + function refreshTitle(){ + updateTitle(opt); + window.setTimeout(refreshTitle, 15000); + }; + + window.setTimeout(refreshTitle, 0); + } + + opt.data('title', d['lightName'] + ' vs. ' + d['darkName']); + opt.data('then', d['timestamp'] || new Date().getTime()); + opt.addClass('cb-game-option'); + opt.appendTo('#cb_select_game'); + updateTitle(opt); + + var select = $('#cb_select_game'); + var list = select.children('.cb-game-option').get(); + list.sort(function(a,b) { + const then_a = $(a).data('then'); + const then_b = $(b).data('then'); + return (then_a < then_b) ? 1 : (then_a == then_b) ? 0 : -1; + }); + + for (const e of list) { + $(e).appendTo(select); + } + + var selOpt = $('#cb_game_' + $('#cb_board').data('gameId')); + if (selOpt.length === 1) { + $('#cb_select_game')[0].selectedIndex = selOpt.index(); + } else { + $('#cb_select_game')[0].selectedIndex = -1; + } + } + }); + + var gameId = location.hash.replace(/^#\//, ''); + if (gameId.length !== 16) { + gameId = randomId(); + } + + switchGameId(gameId); +}); + +/* vim:set expandtab sw=3 ts=8: */ diff --git a/js/todo.js b/js/todo.js new file mode 100644 index 0000000..eb309be --- /dev/null +++ b/js/todo.js @@ -0,0 +1,56 @@ +var user = gun.user(); + +function UI(say, id){ + var li = $('#' + id).get(0) || $('
  • ').attr('id', id).appendTo('ul'); + $(li).text(say); +}; + +function auth(alias, pass){ + $('#message').text('Looking up alias "' + alias + '"...').show(); + gun.get('~@' + alias).once(function(){ + $('#message').text('Signing in as "' + alias + '"...').show(); + user.auth(alias, pass, function (ack){ + if (ack.err) { + $('#message').text(ack.err).show(); + } else { + $('#message').hide(); + $('#pass').val(''); + $('#sign').hide(); + $('#todo').show(); + user.get('said').map().once(UI); + } + }); + }); +}; + +$('#up').on('click', function(e){ + var alias = $('#alias').val(); + var pass = $('#pass').val(); + $('#message').text('Creating alias "' + alias + '"...').show(); + user.create(alias, pass, function (ack){ + if (ack.err) { + $('#message').text(ack.err).show(); + } else { + auth(alias, pass); + } + }); +}); + +$('#sign').on('submit', function(e){ + e.preventDefault(); + auth($('#alias').val(), $('#pass').val()); +}); + +$('#said').on('submit', function(e){ + e.preventDefault(); + if(!user.is){ return } + user.get('said').set($('#say').val()); + $('#say').val(''); +}); + +$('#sign_out').on('click', function (){ + $('ul').empty(); + user.leave(); + $('#sign').show(); + $('#todo').hide(); +}); diff --git a/svg/Chess_bdt45.svg b/svg/Chess_bdt45.svg new file mode 100644 index 0000000..4ce248f --- /dev/null +++ b/svg/Chess_bdt45.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/svg/Chess_blt45.svg b/svg/Chess_blt45.svg new file mode 100644 index 0000000..0f28d08 --- /dev/null +++ b/svg/Chess_blt45.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/svg/Chess_kdt45.svg b/svg/Chess_kdt45.svg new file mode 100644 index 0000000..d90dc73 --- /dev/null +++ b/svg/Chess_kdt45.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/svg/Chess_klt45.svg b/svg/Chess_klt45.svg new file mode 100644 index 0000000..8b2d7b7 --- /dev/null +++ b/svg/Chess_klt45.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/svg/Chess_ndt45.svg b/svg/Chess_ndt45.svg new file mode 100644 index 0000000..7f73c43 --- /dev/null +++ b/svg/Chess_ndt45.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/svg/Chess_nlt45.svg b/svg/Chess_nlt45.svg new file mode 100644 index 0000000..8d51f47 --- /dev/null +++ b/svg/Chess_nlt45.svg @@ -0,0 +1,75 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/svg/Chess_pdt45.svg b/svg/Chess_pdt45.svg new file mode 100644 index 0000000..072f2fe --- /dev/null +++ b/svg/Chess_pdt45.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/svg/Chess_plt45.svg b/svg/Chess_plt45.svg new file mode 100644 index 0000000..1142516 --- /dev/null +++ b/svg/Chess_plt45.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/svg/Chess_qdt45.svg b/svg/Chess_qdt45.svg new file mode 100644 index 0000000..6f1e4a5 --- /dev/null +++ b/svg/Chess_qdt45.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/svg/Chess_qlt45.svg b/svg/Chess_qlt45.svg new file mode 100644 index 0000000..97043c5 --- /dev/null +++ b/svg/Chess_qlt45.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + diff --git a/svg/Chess_rdt45.svg b/svg/Chess_rdt45.svg new file mode 100644 index 0000000..337af8f --- /dev/null +++ b/svg/Chess_rdt45.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + diff --git a/svg/Chess_rlt45.svg b/svg/Chess_rlt45.svg new file mode 100644 index 0000000..8d2d932 --- /dev/null +++ b/svg/Chess_rlt45.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/todo.html b/todo.html new file mode 100644 index 0000000..5904f3b --- /dev/null +++ b/todo.html @@ -0,0 +1,54 @@ + + +

    Todo

    + +
    + + + + +
    + + + +
    + + +
    + + + + + + + +