browser-go-proto/js/main.js

707 lines
23 KiB
JavaScript
Raw Normal View History

2019-08-02 21:14:23 +00:00
/*----- constants -----*/
// game state object {gameMeta object, playerMeta object, turn, pass, gameRecord, bCaptures, wCaptures}
const STONES_DATA = {
'-1': 'white',
'0': 'none',
'1': 'black',
'k': 'ko'
}
const DOTS_DATA = {
'-1': 'white',
'0': 'none',
'1': 'black',
'd': 'dame',
}
2019-08-07 06:13:23 +00:00
const RANKS = [
'30k', '29k', '28k', '27k', '26k', '25k', '24k', '23k', '22k', '21k', '20k',
2019-08-06 21:13:51 +00:00
'19k', '18k', '17k', '16k', '15k', '14k', '13k', '12k', '11k', '10k',
'9k', '8k', '7k', '6k', '5k', '4k', '3k', '2k', '1k',
2019-08-07 06:13:23 +00:00
'1d', '2d', '3d', '4d', '5d', '6d', '7d', '8d', '9d'
];
// index corresponds to difference in player rank
const KOMI_REC = {
'9': [
5.5, 2.5, -0.5, -3.5, -6.5, -9.5, 12.5, 15.5, 18.5, 21.5
],
'13': [
5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5
],
'19': [
7.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5
]
}
const HANDI_REC = {
'9': [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
'13': [
0, 0, 0, 2, 2, 3, 3, 4, 4, 5
],
'19': [
0, 0, 2, 3, 4, 5, 6, 7, 8, 9
]
}
2019-08-06 21:13:51 +00:00
2019-08-02 21:14:23 +00:00
const gameState = {
winner: null,
turn: 1, // turn logic depends on handicap stones
2019-08-07 19:45:58 +00:00
pass: null, // -1 represents state in which resignation has been submitted, not confirmed
komi: null, // komi depends on handicap stones + player rank
2019-08-02 21:14:23 +00:00
handicap: null,
boardSize: 9,
2019-08-02 21:14:23 +00:00
playerState: {
bCaptures: null,
2019-08-07 19:45:58 +00:00
wCaptures: null,
bScore: null,
wScore: null
2019-08-02 21:14:23 +00:00
},
gameMeta: { // declared at game start and not editable after
date: null // contains metadata
2019-08-02 21:14:23 +00:00
},
playerMeta: { // editable during game
b: {
name: null,
2019-08-06 22:25:58 +00:00
rank: 21,
rankCertain: false
2019-08-02 21:14:23 +00:00
},
w: {
name: null,
2019-08-06 22:25:58 +00:00
rank: 21,
2019-08-02 21:14:23 +00:00
rankCertain: false
},
},
groups: {},
2019-08-02 21:14:23 +00:00
gameRecord : []
}
2019-07-26 17:17:23 +00:00
// deadShapes{}
2019-07-26 17:17:23 +00:00
2019-08-02 21:14:23 +00:00
// index represents handicap placement, eg handiPlace[1] = { (3, 3), (7, 7) }
2019-08-07 06:13:23 +00:00
const HANDI_PLACE = {
'9' : [
0, 0,
[ [ 7, 3 ], [ 3, 7 ] ],
[ [ 7, 7 ], [ 7, 3 ], [ 3, 7 ] ],
[ [ 3, 3 ], [ 7, 7 ], [ 3, 7 ], [ 7, 3 ] ]
],
'13' : [
0, 0,
[ [ 4, 9 ], [ 9, 4 ] ],
[ [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
[ [ 4, 4 ], [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
[ [ 7, 7 ], [ 4, 4 ], [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
[ [ 7, 4 ], [ 4, 7 ], [ 4, 4 ], [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
[ [ 7, 7 ], [ 7, 4 ], [ 4, 7 ], [ 4, 4 ], [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
[ [ 9, 7 ], [ 7, 4 ], [ 7, 9 ], [ 4, 7 ], [ 4, 4 ], [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
[ [ 7, 7 ], [ 9, 7 ], [ 7, 4 ], [ 7, 9 ], [ 4, 7 ], [ 4, 4 ], [ 9, 9 ], [ 4, 9 ], [ 9, 4] ],
],
'19' : [
0, 0,
[ [ 4, 16 ], [ 16, 4 ] ],
[ [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
[ [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
[ [ 9, 9 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
[ [ 9, 4 ], [ 4, 9 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
[ [ 9, 9 ], [ 9, 4 ], [ 4, 9 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
[ [ 16, 9 ], [ 9, 4 ], [ 9, 16 ], [ 4, 9 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
[ [ 9, 9 ], [ 16, 9 ], [ 9, 4 ], [ 9, 16 ], [ 4, 9 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
]
};
/*----- app's state (variables) -----*/
2019-08-07 06:13:23 +00:00
let boardState = [];
// define initial game state
class Point {
constructor(x, y) {
this.pos = [ x, y ]
this.stone = 0; // this is where move placement will go 0, 1, -1 'k'
2019-08-04 02:21:02 +00:00
this.legal;
2019-08-06 21:13:51 +00:00
this.territory;
2019-08-05 17:50:06 +00:00
this.capturing = [];
2019-08-07 02:11:51 +00:00
this.groupMembers = [ this ];
this.neighbors = {
top: {},
btm: {},
lft: {},
rgt: {}
}
this.neighbors.top = x > 1 ? [ x - 1, y ] : null;
this.neighbors.btm = x < gameState.boardSize ? [ x + 1, y ] : null;
2019-08-07 02:11:51 +00:00
this.neighbors.rgt = y < gameState.boardSize ? [ x, y + 1 ] : null;
this.neighbors.lft = y > 1 ? [ x, y - 1 ] : null;
}
checkNeighbors = () => {
let neighborsArr = [];
2019-08-07 02:11:51 +00:00
for (let neighbor in this.neighbors) {
let nbr = this.neighbors[neighbor];
// neighbor exists it's point is stored as { rPos, cPos}
if ( nbr !== null ) {
2019-08-07 02:11:51 +00:00
neighborsArr.push(boardState.find(pt => pt.pos[0] === nbr[0] && pt.pos[1] === nbr[1]))
}
};
// returns array of existing neighbors to calling function
return neighborsArr;
}
2019-08-05 17:50:06 +00:00
getLiberties = () => {
let neighborsArr = this.checkNeighbors().filter(pt => pt.stone === 0);
2019-08-05 17:50:06 +00:00
return neighborsArr; //checked
// return all liberties;
}
joinGroup = () => {
2019-08-07 02:11:51 +00:00
this.groupMembers = this.groupMembers.filter(grp => grp.stone === this.stone);
this.groupMembers.push(this);
let frns = this.checkNeighbors().filter(nbr => nbr.stone === this.stone);
for (let frn of frns) {
this.groupMembers.push(frn);
}
2019-08-07 02:11:51 +00:00
this.groupMembers = Array.from(new Set(this.groupMembers));
for (let grpMem in this.groupMembers) {
this.groupMembers = Array.from(new Set(this.groupMembers.concat(this.groupMembers[grpMem].groupMembers)));
}
for (let grpMem in this.groupMembers) {
this.groupMembers[grpMem].groupMembers = Array.from(new Set(this.groupMembers[grpMem].groupMembers.concat(this.groupMembers)));
2019-08-05 17:50:06 +00:00
}
}
checkCapture = () => {
let tempCaptures = [];
let opps = this.checkNeighbors().filter(nbr => nbr.stone === gameState.turn * -1
&& nbr.getLiberties().every(liberty => liberty === this));
2019-08-05 20:56:54 +00:00
for (let opp of opps) {
if (opp.groupMembers.every(stone => stone.getLiberties().filter(liberty => liberty !== this).length === 0)) {
this.capturing = this.capturing.concat(opp.groupMembers);
};
2019-08-05 20:56:54 +00:00
}
2019-08-06 21:13:51 +00:00
this.capturing = Array.from(new Set(this.capturing));
2019-08-05 17:50:06 +00:00
return this.capturing;
}
2019-08-05 17:50:06 +00:00
checkGroup = () => { // liberty is true when called by move false when called by check Capture
let frns = this.checkNeighbors().filter(nbr => nbr.stone === gameState.turn);
for (let frn in frns) {
2019-08-07 02:11:51 +00:00
if (frns[frn].groupMembers.find(stone => stone.getLiberties().find(liberty => liberty !== this))) return true;
continue;
}
}
2019-08-07 06:13:23 +00:00
cycleTerritory = () => {
2019-08-07 19:45:58 +00:00
console.log(this);
if (this.stone) {
this.groupMembers.forEach(pt => pt.territory = pt.territory * -1);
} else {
this.groupMembers.forEach(pt => {
switch (pt.territory) {
2019-08-07 06:13:23 +00:00
case 1:
pt.territory = -1;
break;
case -1:
pt.territory = 'd';
break;
case 'd':
pt.territory = 1;
break;
2019-08-07 19:45:58 +00:00
}
});
}
2019-08-07 06:13:23 +00:00
}
}
2019-08-04 03:43:57 +00:00
// could use this Array to iterate through and create
// let boardCreator = new Array(gameState.boardSize).fill(gameState.boardSize);
// boardState [point objects-contain overlay] lastState (created from boardState)
// 'k' represents komi, in-game integers represent move previews,
// 'chk', 'hold', 'x' and 'l' represent points checked during checkLegalMove run
// game-end integer represent points of territory, 'd' represents dame,
/*----- cached element references -----*/
2019-08-07 01:13:34 +00:00
const whiteCapsEl = document.getElementById('white-caps');
const blackCapsEl = document.getElementById('black-caps');
2019-08-05 23:13:23 +00:00
const modalEl = document.querySelector('.modal');
const komiSliderEl = document.querySelector('input[name="komi-slider"]');
const handiSliderEl = document.querySelector('input[name="handicap-slider"]');
2019-08-07 01:13:34 +00:00
const blackRankEl = document.getElementById('black-rank');
const blackRankUpEl = document.getElementById('black-rank-up');
const blackRankDownEl = document.getElementById('black-rank-down');
const whiteRankEl = document.getElementById('white-rank');
const whiteRankUpEl = document.getElementById('black-rank-up');
const whiteRankDownEl = document.getElementById('black-rank-down');
const blackNameInputEl = document.querySelector('input[name="black-name"]')
const whiteNameInputEl = document.querySelector('input[name="white-name"]')
const blackNameDisplayEl = document.querySelector('h4#black-player-name');
const whiteNameDisplayEl = document.querySelector('h4#white-player-name');
const gameHudEl = document.querySelector('#game-hud p');
2019-08-06 22:25:58 +00:00
2019-08-04 03:43:57 +00:00
// store modal #menu for displaying game info
// store
/*----- event listeners -----*/
document.getElementById('board').addEventListener('mousemove', hoverPreview);
2019-08-06 21:13:51 +00:00
document.getElementById('board').addEventListener('click', clickBoard);
2019-08-05 23:13:23 +00:00
document.getElementById('white-bowl').addEventListener('click',clickPass);
document.getElementById('black-bowl').addEventListener('click',clickPass);
document.getElementById('kifu').addEventListener('click', clickMenu);
document.getElementById('white-caps-space').addEventListener('click', clickResign);
document.getElementById('black-caps-space').addEventListener('click', clickResign);
modalEl.addEventListener('click', clickCloseMenu);
komiSliderEl.addEventListener('change', changeUpdateKomi);
handiSliderEl.addEventListener('change', changeUpdateHandicap);
2019-08-06 22:25:58 +00:00
document.getElementById('player-meta').addEventListener('click', clickUpdatePlayerMeta);
document.getElementById('player-meta').addEventListener('change', clickUpdatePlayerMeta);
2019-08-07 06:13:23 +00:00
document.querySelector('input[name="komi-suggest"]').addEventListener('click', clickKomiSuggestion);
2019-08-07 19:45:58 +00:00
gameHudEl.addEventListener('click', clickGameHud);
2019-08-05 23:13:23 +00:00
/*----- functions -----*/
init();
let findPointFromIdx = (arr) => boardState.find( point => point.pos[0] === arr[0] && point.pos[1] === arr[1] );
function changeUpdateKomi() {
document.getElementById('komi').textContent = komiSliderEl.value;
}
function changeUpdateHandicap() {
document.getElementById('handicap').textContent = handiSliderEl.value;
}
2019-08-06 22:25:58 +00:00
function clickUpdatePlayerMeta(evt) {
2019-08-07 06:13:23 +00:00
if (evt.target.id) {
switch (evt.target.id) {
case 'black-rank-up':
gameState.playerMeta.b.rank++;
break;
case 'black-rank-down':
2019-08-06 22:25:58 +00:00
gameState.playerMeta.b.rank--;
break;
2019-08-07 06:13:23 +00:00
case 'white-rank-up':
gameState.playerMeta.w.rank++;
break;
case 'white-rank-down':
gameState.playerMeta.w.rank--;
break;
}
2019-08-06 22:25:58 +00:00
}
if (evt.target.name = 'black-rank-certain') gameState.playerMeta.b.rankCertain = !gameState.playerMeta.b.rankCertain;
if (evt.target.name = 'white-rank-certain') gameState.playerMeta.w.rankCertain = !gameState.playerMeta.w.rankCertain;
blackRankEl.textContent = RANKS[gameState.playerMeta.b.rank];
whiteRankEl.textContent = RANKS[gameState.playerMeta.w.rank];
2019-08-06 23:36:39 +00:00
}
function clickKomiSuggestion() {
2019-08-07 06:13:23 +00:00
let sugg = KOMI_REC[Math.abs(gameState.playerMeta.w.rank - gameState.playerMeta.b.rank)];
let handi = HANDI_REC[Math.abs(gameState.playerMeta.w.rank - gameState.playerMeta.b.rank)];
gameState.komi = sugg;
gameState.handicap = handi;
renderMenu();
2019-08-06 22:25:58 +00:00
}
2019-08-07 19:45:58 +00:00
function clickGameHud() {
2019-08-07 06:13:23 +00:00
if (gameState.pass > 1 && !gameState.winner) calculateWinner();
2019-08-07 19:45:58 +00:00
if (gameState.pass < 0) confirmResign();
2019-08-07 06:13:23 +00:00
}
2019-08-06 23:36:39 +00:00
2019-08-07 06:13:23 +00:00
function clickSubmitStart() {
2019-08-06 23:36:39 +00:00
gameState.playerMeta.b.name = blackNameInputEl.value;
gameState.playerMeta.w.name = whiteNameInputEl.value;
2019-08-07 06:13:23 +00:00
initGame();
}
function renderMenu() {
komiSliderEl.value = sugg;
2019-08-06 23:36:39 +00:00
blackNameDisplayEl.textContent = gameState.playerMeta.b.name;
whiteNameDisplayEl.textContent = gameState.playerMeta.w.name;
2019-08-07 06:13:23 +00:00
blackRankEl.textContent = RANKS[gameState.playerMeta.b.rank];
whiteRankEl.textContent = RANKS[gameState.playerMeta.w.rank];
2019-08-06 23:36:39 +00:00
}
2019-08-07 06:13:23 +00:00
2019-08-05 23:13:23 +00:00
function clickPass(evt) {
if (evt.target.parentElement.id === `${STONES_DATA[gameState.turn]}-bowl`) playerPass();
}
function playerPass() {
// display confirmation message
clearKo();
clearCaptures();
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: pass`)
gameState.pass++;
if (gameState.pass === 2) return endGame();
gameState.turn*= -1;
render();
}
function clickMenu() {
modalEl.style.visibility = 'visible';
changeUpdateKomi();
changeUpdateHandicap();
2019-08-06 22:25:58 +00:00
clickUpdatePlayerMeta();
2019-08-05 23:13:23 +00:00
}
2019-08-07 19:45:58 +00:00
function startMenu() {
modalEl.style.visibility = 'visible';
changeUpdateKomi();
changeUpdateHandicap();
clickUpdatePlayerMeta();
}
2019-08-05 23:13:23 +00:00
function clickCloseMenu(evt) {
evt.stopPropagation();
if (evt.target.className === "modal") modalEl.style.visibility = 'hidden';
2019-08-05 23:13:23 +00:00
}
function clickResign(evt) {
if (evt.target.parentElement.id === `${STONES_DATA[gameState.turn]}-caps-space`) playerResign();
}
function playerResign() {
2019-08-07 19:45:58 +00:00
// display confirmation message\
gameState.pass = -1;
gameHudEl.style.visibility = "visible";
gameHudEl.textContent = "Do you want to resign?";
}
2019-08-06 23:36:39 +00:00
2019-08-07 19:45:58 +00:00
function confirmResign() {
2019-08-06 23:36:39 +00:00
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: resign`);
gameState.winner = STONES_DATA[gameState.turn * -1];
2019-08-05 23:13:23 +00:00
endGame();
}
2019-08-07 19:45:58 +00:00
function hoverPreview(evt) {
evt.stopPropagation();
2019-08-06 21:13:51 +00:00
if (gameState.pass > 1 || gameState.winner) return;
// renders preview stone if move is legal
let hover = [ parseInt(evt.target.closest('td').id[0]), parseInt(evt.target.closest('td').id[2]) ];
let point = findPointFromIdx(hover);
if (checkLegal(point)) {
2019-08-04 02:21:02 +00:00
point.legal = true; // legal
renderPreview(point);
}
}
function checkLegal(point) {
2019-08-04 02:21:02 +00:00
clearOverlay();
// first step in logic: is point occupied, or in ko
if (point.stone) return false;
2019-08-05 17:50:06 +00:00
// if point is not empty check if liberties
if (point.getLiberties().length < 1) {
2019-08-05 17:50:06 +00:00
//if no liberties check if enemy group has liberties
if ( point.checkCapture().length ) return true;
//if neighboring point is not empty check if friendly group is alive
if (point.checkGroup()) return true;
2019-08-04 02:21:02 +00:00
return false;
}
return true;
}
2019-08-04 02:21:02 +00:00
function clearOverlay() { //legal and check
for (let point in boardState) {
2019-08-04 04:17:23 +00:00
point = boardState[point];
point.legal = false;
2019-08-04 02:21:02 +00:00
}
}
2019-08-04 03:43:57 +00:00
function resolveCaptures(point) {
2019-08-06 07:00:00 +00:00
if(!point.capturing.length) {
2019-08-05 23:13:23 +00:00
point.checkCapture();
}
if(point.capturing.length) {
2019-08-05 17:50:06 +00:00
point.capturing.forEach(cap => {
2019-08-05 23:13:23 +00:00
gameState.playerState[gameState.turn > 0 ? 'bCaptures' : 'wCaptures']++;
cap.groupMembers = [];
2019-08-06 07:00:00 +00:00
cap.stone = checkKo(point, cap) ? 'k' : 0;
2019-08-05 17:50:06 +00:00
})
2019-08-04 03:43:57 +00:00
}
}
2019-08-07 06:13:23 +00:00
function editTerritory(evt) {
let placement = [ parseInt(evt.target.closest('td').id[0]), parseInt(evt.target.closest('td').id[2]) ];
let point = findPointFromIdx(placement);
point.cycleTerritory();
render();
}
2019-08-06 07:00:00 +00:00
function checkKo(point, cap) {
2019-08-06 21:13:51 +00:00
if (!point.getLiberties().length && cap.checkNeighbors().filter(stone => stone.stone === gameState.turn * -1)
&& point.capturing.length === 1) return true;
2019-08-04 04:17:23 +00:00
}
2019-08-06 21:13:51 +00:00
function clickBoard(evt) {
evt.stopPropagation();
2019-08-07 06:13:23 +00:00
if (gameState.pass > 1 || gameState.winner) return editTerritory(evt);
// checks for placement and pushes to cell
let placement = [ parseInt(evt.target.closest('td').id[0]), parseInt(evt.target.closest('td').id[2]) ];
let point = findPointFromIdx(placement);
//checks that this placement was marked as legal
if ( !checkLegal(point) ) return;
2019-08-05 23:13:23 +00:00
clearKo();
clearPass();
2019-08-04 03:43:57 +00:00
resolveCaptures(point);
2019-08-05 23:13:23 +00:00
point.stone = gameState.turn;
point.joinGroup();
clearCaptures();
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: ${point.pos}`)
gameState.turn*= -1;
render();
}
2019-08-04 04:17:23 +00:00
2019-08-05 23:13:23 +00:00
function clearKo() {
2019-08-04 04:17:23 +00:00
for (let point in boardState) {
point = boardState[point];
point.stone = point.stone === 'k' ? 0 : point.stone;
}
}
2019-08-05 23:13:23 +00:00
function clearPass() {
gameState.pass = 0;
}
2019-08-05 17:50:06 +00:00
function clearCaptures() {
for (let point in boardState) {
point = boardState[point];
point.capturing = [];
}
}
2019-08-07 06:13:23 +00:00
function initBoard() {
let i = 0;
while (i < gameState.boardSize * gameState.boardSize) {
let point = new Point( Math.floor(i / gameState.boardSize) + 1, i % gameState.boardSize + 1)
boardState.push(point);
i++;
}
// initHandi();
}
function initHandi() {
// HANDI_PLACE[gameState.handicap]
}
2019-08-06 22:25:58 +00:00
function getDate() {
let d = new Date;
return `${d.getFullYear()}-${String(d.getMonth()+1).charAt(-1)||0}${String(d.getMonth()+1).charAt(-0)}-${String(d.getDate()).charAt(-1)||0}${String(d.getDate()+1).charAt(-0)}`
}
function init() {
2019-08-07 19:45:58 +00:00
gameState.gameMeta.date = getDate();
gameState.komi = 5.5; // get komi from player input
// startMenu();
gameState.winner = null;
gameState.pass = null;
// gameState.handicap = ; // get handicap from player input
2019-08-07 06:13:23 +00:00
gameState.turn = gameState.handicap ? -1 : 1;
gameState.boardSize = 9;
gameState.playerState.bCaptures = 0;
gameState.playerState.wCaptures = 0;
// get any future meta from player input
// gameState.playerMeta.b // get from player input
// gameState.playerMeta.w // get from player input
gameState.gameRecord = []; // clear game record from previous game
// gameState.boardState // create board from user input
2019-08-07 06:13:23 +00:00
//need init player meta
2019-08-07 06:13:23 +00:00
initBoard();
2019-08-05 17:50:06 +00:00
// testing board state for moves at [32]
gameState.turn = 1;
render();
};
2019-08-02 21:14:23 +00:00
2019-08-07 01:13:34 +00:00
function render() {
2019-08-06 21:13:51 +00:00
if (gameState.winner || gameState.pass > 1) {
renderTerritory();
2019-08-06 23:36:39 +00:00
renderMessage();
2019-08-06 21:13:51 +00:00
}
gameState.gameRecord.length? renderTurn() : renderFirstTurn();
renderBoard();
2019-08-04 04:38:31 +00:00
renderCaps();
}
2019-08-06 23:36:39 +00:00
function renderMessage() {
2019-08-07 06:13:23 +00:00
if (gameState.winner && gameState.pass < 2) {
2019-08-07 19:45:58 +00:00
gameHudEl.style.visibility = 'visible';
gameHudEl.style.cursor = 'default';
2019-08-07 06:13:23 +00:00
gameHudEl.textContent = `${gameState.playerMeta[gameState.winner === 1 ? 'b' : 'w'].name} won by resignation`;
}
else if (gameState.winner && gameState.pass > 1) {
2019-08-07 19:45:58 +00:00
gameHudEl.style.visibility = 'visible';
gameHudEl.style.cursor = 'default';
gameHudEl.textContent = `${gameState.playerMeta[gameState.winner === 1 ? 'b' : 'w'].name} won by ${Math.abs(gameState.playerState.wScore - gameState.playerState.bScore)}`;
} else if (gameState.pass > 1) {
gameHudEl.style.visibility = 'visible';
2019-08-07 06:13:23 +00:00
gameHudEl.textContent = 'click to finalize game'
2019-08-07 19:45:58 +00:00
} else {
gameHudEl.style.visibility = 'hidden';
2019-08-07 06:13:23 +00:00
}
2019-08-06 23:36:39 +00:00
}
2019-08-06 21:13:51 +00:00
function renderTerritory() {
boardState.forEach(val => {
let stoneElem = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).childNodes[1].childNodes[0];
stoneElem.setAttribute("data-dot", DOTS_DATA[val.territory]);
})
}
function renderFirstTurn() {
document.getElementById(`${STONES_DATA[gameState.turn]}-bowl`).toggleAttribute('data-turn');
}
function renderTurn() {
2019-08-06 21:13:51 +00:00
if (gameState.winner || gameState.pass > 1) document.querySelectorAll(`.bowl`).forEach(bowl => {
bowl.removeAttribute('data-turn');
bowl.toggleAttribute('data-turn');
});
document.querySelectorAll(`.bowl`).forEach(bowl => bowl.toggleAttribute('data-turn'));
}
function renderBoard() {
boardState.forEach(val => {
2019-08-05 23:13:23 +00:00
let stoneElem = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).childNodes[1];
2019-08-05 17:50:06 +00:00
stoneElem.setAttribute("data-stone", STONES_DATA[val.stone]);
})
}
2019-08-04 04:38:31 +00:00
function renderCaps() {
2019-08-05 23:13:23 +00:00
blackCapsEl.textContent = gameState.playerState.bCaptures;
whiteCapsEl.textContent = gameState.playerState.wCaptures;
2019-08-04 04:38:31 +00:00
}
function renderPreview(hoverPoint) {
boardState.forEach(val => {
2019-08-05 23:13:23 +00:00
let dot = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).childNodes[1].childNodes[0];
2019-08-04 02:21:02 +00:00
dot.setAttribute("data-dot", val.legal === true && val.pos[0] === hoverPoint.pos[0] && val.pos[1] === hoverPoint.pos[1] ? DOTS_DATA[gameState.turn] : DOTS_DATA[0]);
})
2019-08-05 23:13:23 +00:00
}
2019-08-07 06:13:23 +00:00
function calculateWinner() {
2019-08-07 19:45:58 +00:00
// debugger;
let whiteTerritory = boardState.reduce((acc, pt) => {
if (pt.territory === -1 && pt.stone !== -1) {
return acc = acc + (pt.stone === 0 ? 1 : 2);
}
return acc;
}, 0);
console.log(whiteTerritory);
let blackTerritory = boardState.reduce((acc, pt) => {
if (pt.territory === 1 && pt.stone !== 1) {
return acc + (pt.stone === 0 ? 1 : 2);
}
return acc;
}, 0);
console.log(blackTerritory);
gameState.playerState.wScore =
gameState.playerState.wCaptures
+ (gameState.komi < 0 ? gameState.komi * -1 : 0)
+ whiteTerritory;
gameState.playerState.bScore =
gameState.playerState.bCaptures
+ (gameState.komi > 0 ? gameState.komi : 0)
+ blackTerritory;
gameState.winner = gameState.playerState.wScore > gameState.playerState.bScore ? -1 : 1;
gameState.gameRecord.push(`${STONES_DATA[gameState.winner]}: +${Math.abs(gameState.playerState.wScore - gameState.playerState.bScore)}`)
render();
2019-08-07 06:13:23 +00:00
}
2019-08-06 21:13:51 +00:00
function endGameSetTerritory() {
2019-08-07 19:45:58 +00:00
// boardState.forEach(pt => {
// pt.territory = pt.stone ? pt.stone : 'd'
// });
2019-08-07 06:13:23 +00:00
let emptyPoints = boardState.filter(pt => !pt.stone);
emptyPoints.forEach(pt => pt.joinGroup());
emptyPointSetTerritory(emptyPoints);
2019-08-07 19:45:58 +00:00
// boardState.filter(pt => {
// return pt.groupMembers.length < 6 && pt.stone
// }).forEach(pt => pt.territory = pt.stone * -1);
2019-08-06 21:13:51 +00:00
}
2019-08-07 06:13:23 +00:00
function emptyPointSetTerritory(emptyPoints) {
2019-08-07 19:45:58 +00:00
emptyPoints.filter(pt => !pt.territory && pt.checkNeighbors().filter(nbr => nbr.stone !== 0))
.forEach(pt => {
console.log(pt);
let b = pt.groupMembers.reduce((acc, rdcPt) => {
let bNbr = rdcPt.checkNeighbors().filter(nbr => nbr.stone === 1).length;
return acc + bNbr;
}, 0);
let w = pt.groupMembers.reduce((acc, rdcPt) => {
debugger;
let wNbr = rdcPt.checkNeighbors().filter(nbr => nbr.stone === -1).length;
return acc + wNbr;
}, 0);
pt.groupMembers.forEach(grp => {
if (Math.abs(b - w) < 4) grp.territory = 'd'
else grp.territory = b > w ? 1 : -1;
})
});
2019-08-07 06:13:23 +00:00
}
2019-08-05 23:13:23 +00:00
function endGame() {
2019-08-07 19:45:58 +00:00
if (!gameState.winner) endGameSetTerritory()
2019-08-06 21:13:51 +00:00
// join all remaining groups
// check remaining groups life
2019-08-06 23:36:39 +00:00
2019-08-06 21:13:51 +00:00
// search empty spaces on board for deadShapes
// compare spaces to rotations of deadShapes[...]
// 'd' if empty spaces
2019-08-05 23:13:23 +00:00
2019-08-07 19:45:58 +00:00
// return dead group suggestion
// users can flip status of any dead group overlay( 1, -1 )
// confirm state
2019-08-06 21:13:51 +00:00
// calculate score = points in overlay for each player + captures
// render final board state with dead groups removed
2019-08-07 19:45:58 +00:00
// log game record
2019-08-06 21:13:51 +00:00
// stringify according to .sgf format
// log as text
2019-08-07 19:45:58 +00:00
render();
}
2019-07-26 17:17:23 +00:00
2019-08-02 21:14:23 +00:00
// game-end
// render territory counts
// checkLegalMove
// clear overlay
// if move is not '0', move is illegal (opposing player or 'k' for ko)
// iterate through neighboring points in clockwise order
// if anyone is '0' move is legal - call render preview
// if neighboring point is opposing player
// cycle through opposing player group marking points as overlay: 'chk' when checked and
// overlay: 'hold' if they are neighboring points of opposing player color
// if any neighboring point is '0' terminate cycle and move to next neighboring point of original move
// if there are unchecked points of 'hold' return
// if no boardState: 0 points, move is legal overlay: 'l'
// set all 'chk' to 'x' to represent stones that will be captured upon move
2019-08-02 00:10:56 +00:00
// if neighboring point is player's
// cycle through player group marking points as overlay: 'chk' || 'hold'
// if any neighboring point is '0' ternminate cycle and mark point as 'l'
// set move
// if checkLegalMove has returned '0' i2llegal move message?
// if move state is 'l'
// push boardState to lastState
// push 'l' move to boardState
// resolve captures
// for all 'x' in overlay
// count number and add to playerCaptures
// set boardState to '0'
// pass--
// push move to game record
// game record: [ 0: handicapStones Obj, 1: 1stMove([moveState[],moveState[][])]
// pass() pass++ and player turn to other player
// gameEnd when pass = 2
// search empty spaces on board for deadShapes
// compare spaces to rotations of deadShapes[...]
// 'd' if empty spaces
// return dead group suggestion
// users can flip status of any dead group overlay( 1, -1 )
// confirm state
// calculate score = points in overlay for each player + captures
// render final board state with dead groups removed
// log game record
// stringify according to .sgf format
// log as text