debug territory count at endgame
This commit is contained in:
parent
11c17f6d0e
commit
201407b8ea
4 changed files with 125 additions and 48 deletions
|
@ -15,7 +15,8 @@ a working game of go for a 9x9 board that
|
||||||
- [x] ends game upon 2 consecutive passes
|
- [x] ends game upon 2 consecutive passes
|
||||||
- [ ] calculates estimated score at game end
|
- [ ] calculates estimated score at game end
|
||||||
- [ ] compares board groups to most common dead shapes
|
- [ ] compares board groups to most common dead shapes
|
||||||
- [ ] allows users to override dead group estimates and submit finalized score to game record
|
- [x] allows users to override dead group estimates
|
||||||
|
- [ ] allows users to submit finalized score to game record
|
||||||
- [ ] displays game record as string
|
- [ ] displays game record as string
|
||||||
|
|
||||||
stretch goals
|
stretch goals
|
||||||
|
|
26
css/main.css
26
css/main.css
|
@ -1,5 +1,9 @@
|
||||||
@import url('https://fonts.googleapis.com/css?family=La+Belle+Aurore|Raleway:300|Raleway:600');
|
@import url('https://fonts.googleapis.com/css?family=La+Belle+Aurore|Raleway:300|Raleway:600');
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -39,7 +43,7 @@ body {
|
||||||
"player"
|
"player"
|
||||||
"record"
|
"record"
|
||||||
"submit";
|
"submit";
|
||||||
font: 14px 'La Belle Aurore', cursive;
|
font-family: 'La Belle Aurore', cursive;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +87,6 @@ h4 {
|
||||||
|
|
||||||
#player-meta input[type="button"] {
|
#player-meta input[type="button"] {
|
||||||
margin: .25em;
|
margin: .25em;
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div[data-player-meta] {
|
div[data-player-meta] {
|
||||||
|
@ -176,6 +179,14 @@ content {
|
||||||
|
|
||||||
#game-hud p {
|
#game-hud p {
|
||||||
font-size: 130%;
|
font-size: 130%;
|
||||||
|
width: 50%;
|
||||||
|
order: 0;
|
||||||
|
width: 10vh;
|
||||||
|
background-color: rgba(0,0,0,0.3);
|
||||||
|
padding: 1vh;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-pos#black-pos {
|
.player-pos#black-pos {
|
||||||
|
@ -222,7 +233,7 @@ content {
|
||||||
}
|
}
|
||||||
|
|
||||||
.bowl[data-turn] {
|
.bowl[data-turn] {
|
||||||
box-shadow: 0 0 3vh 3vh rgb(31, 255, 2);
|
box-shadow: 0 0 3vh 3vh rgb(255, 175, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.caps-space {
|
.caps-space {
|
||||||
|
@ -401,20 +412,27 @@ td .dot .seki {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 560px) {
|
@media only screen and (min-width: 560px) {
|
||||||
|
|
||||||
#player-meta {
|
#player-meta {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
div[data-player-meta] {
|
div[data-player-meta] {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
grid-template-columns: 60vw;
|
grid-template-columns: 60vw;
|
||||||
grid-template-rows: auto auto 60vw auto;
|
grid-template-rows: auto auto 60vw auto;
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 500px) {
|
@media only screen and (min-width: 500px) {
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.player-pos {
|
.player-pos {
|
||||||
height: 14vh;
|
height: 14vh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,8 @@
|
||||||
To begin a game enter player names and ranks above, then click "Suggest Komi" Browser Go will calculate the appropriate komi based on AGA guidelines.
|
To begin a game enter player names and ranks above, then click "Suggest Komi" Browser Go will calculate the appropriate komi based on AGA guidelines.
|
||||||
To override Browser Go's suggestion, use the sliders above. Be sure to check the 'rank certainty' box if you're club-rated.<br><br>
|
To override Browser Go's suggestion, use the sliders above. Be sure to check the 'rank certainty' box if you're club-rated.<br><br>
|
||||||
When the game begins, click on a legal point on the board to make a move. The active player's bowl will be highlighted. To pass, click on your bowl.
|
When the game begins, click on a legal point on the board to make a move. The active player's bowl will be highlighted. To pass, click on your bowl.
|
||||||
This will only be possible on your turn. To resign click on your capture tray. To view the game record or adjust player settings, click the kifu on black's side.<br><br>
|
This will only be possible on your turn. To resign click on your capture tray. After the game ends, groups and territory will display Browser Go's estimate for final state.
|
||||||
For now, Browser Go only supports games on a small board.
|
Simply click on a group to change a group between live and dead, or a point between territory and dame.
|
||||||
I've got great things planned for the future, though! Lookout for new releases on <a href="https://github.com/sorrelbri/browser-go">the GitHub page!</a></p>
|
I've got great things planned for the future, though! Lookout for new releases on <a href="https://github.com/sorrelbri/browser-go">the GitHub page!</a></p>
|
||||||
<p id="game-record"></p>
|
<p id="game-record"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
126
js/main.js
126
js/main.js
|
@ -12,7 +12,6 @@ const DOTS_DATA = {
|
||||||
'0': 'none',
|
'0': 'none',
|
||||||
'1': 'black',
|
'1': 'black',
|
||||||
'd': 'dame',
|
'd': 'dame',
|
||||||
's': 'seki'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RANKS = [
|
const RANKS = [
|
||||||
|
@ -49,13 +48,15 @@ const HANDI_REC = {
|
||||||
const gameState = {
|
const gameState = {
|
||||||
winner: null,
|
winner: null,
|
||||||
turn: 1, // turn logic depends on handicap stones
|
turn: 1, // turn logic depends on handicap stones
|
||||||
pass: null,
|
pass: null, // -1 represents state in which resignation has been submitted, not confirmed
|
||||||
komi: null, // komi depends on handicap stones + player rank
|
komi: null, // komi depends on handicap stones + player rank
|
||||||
handicap: null,
|
handicap: null,
|
||||||
boardSize: 9,
|
boardSize: 9,
|
||||||
playerState: {
|
playerState: {
|
||||||
bCaptures: null,
|
bCaptures: null,
|
||||||
wCaptures: null
|
wCaptures: null,
|
||||||
|
bScore: null,
|
||||||
|
wScore: null
|
||||||
},
|
},
|
||||||
gameMeta: { // declared at game start and not editable after
|
gameMeta: { // declared at game start and not editable after
|
||||||
date: null // contains metadata
|
date: null // contains metadata
|
||||||
|
@ -188,6 +189,10 @@ class Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cycleTerritory = () => {
|
cycleTerritory = () => {
|
||||||
|
console.log(this);
|
||||||
|
if (this.stone) {
|
||||||
|
this.groupMembers.forEach(pt => pt.territory = pt.territory * -1);
|
||||||
|
} else {
|
||||||
this.groupMembers.forEach(pt => {
|
this.groupMembers.forEach(pt => {
|
||||||
switch (pt.territory) {
|
switch (pt.territory) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -200,7 +205,8 @@ class Point {
|
||||||
pt.territory = 1;
|
pt.territory = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// could use this Array to iterate through and create
|
// could use this Array to iterate through and create
|
||||||
|
@ -235,15 +241,8 @@ const gameHudEl = document.querySelector('#game-hud p');
|
||||||
|
|
||||||
|
|
||||||
/*----- event listeners -----*/
|
/*----- event listeners -----*/
|
||||||
// input listeners for player names, ranks, rank certainty (editable during game)
|
|
||||||
//input lister for handicap + komi (only editable pre-game)
|
|
||||||
// ::hover-over on board to preview move (with legal move logic)
|
|
||||||
document.getElementById('board').addEventListener('mousemove', hoverPreview);
|
document.getElementById('board').addEventListener('mousemove', hoverPreview);
|
||||||
// click on board to play move
|
|
||||||
document.getElementById('board').addEventListener('click', clickBoard);
|
document.getElementById('board').addEventListener('click', clickBoard);
|
||||||
// ::hover-over on either bowl for pass, one-level undo options (CSS implementation)
|
|
||||||
// click on menu items
|
|
||||||
// click on kifu to display game menu
|
|
||||||
document.getElementById('white-bowl').addEventListener('click',clickPass);
|
document.getElementById('white-bowl').addEventListener('click',clickPass);
|
||||||
document.getElementById('black-bowl').addEventListener('click',clickPass);
|
document.getElementById('black-bowl').addEventListener('click',clickPass);
|
||||||
document.getElementById('kifu').addEventListener('click', clickMenu);
|
document.getElementById('kifu').addEventListener('click', clickMenu);
|
||||||
|
@ -255,7 +254,7 @@ handiSliderEl.addEventListener('change', changeUpdateHandicap);
|
||||||
document.getElementById('player-meta').addEventListener('click', clickUpdatePlayerMeta);
|
document.getElementById('player-meta').addEventListener('click', clickUpdatePlayerMeta);
|
||||||
document.getElementById('player-meta').addEventListener('change', clickUpdatePlayerMeta);
|
document.getElementById('player-meta').addEventListener('change', clickUpdatePlayerMeta);
|
||||||
document.querySelector('input[name="komi-suggest"]').addEventListener('click', clickKomiSuggestion);
|
document.querySelector('input[name="komi-suggest"]').addEventListener('click', clickKomiSuggestion);
|
||||||
gameHudEl.addEventListener('click', clickSubmitScore);
|
gameHudEl.addEventListener('click', clickGameHud);
|
||||||
|
|
||||||
|
|
||||||
/*----- functions -----*/
|
/*----- functions -----*/
|
||||||
|
@ -303,12 +302,12 @@ function clickKomiSuggestion() {
|
||||||
renderMenu();
|
renderMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickSubmitScore() {
|
function clickGameHud() {
|
||||||
if (gameState.pass > 1 && !gameState.winner) calculateWinner();
|
if (gameState.pass > 1 && !gameState.winner) calculateWinner();
|
||||||
|
if (gameState.pass < 0) confirmResign();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickSubmitStart() {
|
function clickSubmitStart() {
|
||||||
|
|
||||||
gameState.playerMeta.b.name = blackNameInputEl.value;
|
gameState.playerMeta.b.name = blackNameInputEl.value;
|
||||||
gameState.playerMeta.w.name = whiteNameInputEl.value;
|
gameState.playerMeta.w.name = whiteNameInputEl.value;
|
||||||
initGame();
|
initGame();
|
||||||
|
@ -344,6 +343,14 @@ function clickMenu() {
|
||||||
clickUpdatePlayerMeta();
|
clickUpdatePlayerMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startMenu() {
|
||||||
|
modalEl.style.visibility = 'visible';
|
||||||
|
changeUpdateKomi();
|
||||||
|
changeUpdateHandicap();
|
||||||
|
clickUpdatePlayerMeta();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function clickCloseMenu(evt) {
|
function clickCloseMenu(evt) {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
if (evt.target.className === "modal") modalEl.style.visibility = 'hidden';
|
if (evt.target.className === "modal") modalEl.style.visibility = 'hidden';
|
||||||
|
@ -354,14 +361,19 @@ function clickResign(evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function playerResign() {
|
function playerResign() {
|
||||||
// display confirmation message
|
// display confirmation message\
|
||||||
if (!confirm('Do you want to resign?')) return;
|
gameState.pass = -1;
|
||||||
|
gameHudEl.style.visibility = "visible";
|
||||||
|
gameHudEl.textContent = "Do you want to resign?";
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmResign() {
|
||||||
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: resign`);
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: resign`);
|
||||||
gameState.winner = STONES_DATA[gameState.turn * -1];
|
gameState.winner = STONES_DATA[gameState.turn * -1];
|
||||||
endGame();
|
endGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function hoverPreview(evt) {
|
function hoverPreview(evt) {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
if (gameState.pass > 1 || gameState.winner) return;
|
if (gameState.pass > 1 || gameState.winner) return;
|
||||||
|
@ -477,15 +489,16 @@ function getDate() {
|
||||||
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)}`
|
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() {
|
function init() {
|
||||||
|
gameState.gameMeta.date = getDate();
|
||||||
|
gameState.komi = 5.5; // get komi from player input
|
||||||
|
// startMenu();
|
||||||
gameState.winner = null;
|
gameState.winner = null;
|
||||||
gameState.pass = null;
|
gameState.pass = null;
|
||||||
// gameState.komi = ; // get komi from player input
|
|
||||||
// gameState.handicap = ; // get handicap from player input
|
// gameState.handicap = ; // get handicap from player input
|
||||||
gameState.turn = gameState.handicap ? -1 : 1;
|
gameState.turn = gameState.handicap ? -1 : 1;
|
||||||
gameState.boardSize = 9;
|
gameState.boardSize = 9;
|
||||||
gameState.playerState.bCaptures = 0;
|
gameState.playerState.bCaptures = 0;
|
||||||
gameState.playerState.wCaptures = 0;
|
gameState.playerState.wCaptures = 0;
|
||||||
gameState.gameMeta.date = getDate();
|
|
||||||
// get any future meta from player input
|
// get any future meta from player input
|
||||||
// gameState.playerMeta.b // get from player input
|
// gameState.playerMeta.b // get from player input
|
||||||
// gameState.playerMeta.w // get from player input
|
// gameState.playerMeta.w // get from player input
|
||||||
|
@ -511,12 +524,19 @@ function render() {
|
||||||
|
|
||||||
function renderMessage() {
|
function renderMessage() {
|
||||||
if (gameState.winner && gameState.pass < 2) {
|
if (gameState.winner && gameState.pass < 2) {
|
||||||
|
gameHudEl.style.visibility = 'visible';
|
||||||
|
gameHudEl.style.cursor = 'default';
|
||||||
gameHudEl.textContent = `${gameState.playerMeta[gameState.winner === 1 ? 'b' : 'w'].name} won by resignation`;
|
gameHudEl.textContent = `${gameState.playerMeta[gameState.winner === 1 ? 'b' : 'w'].name} won by resignation`;
|
||||||
}
|
}
|
||||||
else if (gameState.winner && gameState.pass > 1) {
|
else if (gameState.winner && gameState.pass > 1) {
|
||||||
gameHudEl.textContent = `${gameState.playerMeta[gameState.winner === 1 ? 'b' : 'w'].name} won by `;
|
gameHudEl.style.visibility = 'visible';
|
||||||
} else {
|
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';
|
||||||
gameHudEl.textContent = 'click to finalize game'
|
gameHudEl.textContent = 'click to finalize game'
|
||||||
|
} else {
|
||||||
|
gameHudEl.style.visibility = 'hidden';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,31 +580,69 @@ function renderPreview(hoverPoint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateWinner() {
|
function calculateWinner() {
|
||||||
|
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
function endGameSetTerritory() {
|
function endGameSetTerritory() {
|
||||||
boardState.forEach(pt => {
|
// boardState.forEach(pt => {
|
||||||
pt.territory = pt.stone ? pt.stone : 'd'
|
// pt.territory = pt.stone ? pt.stone : 'd'
|
||||||
});
|
// });
|
||||||
let emptyPoints = boardState.filter(pt => !pt.stone);
|
let emptyPoints = boardState.filter(pt => !pt.stone);
|
||||||
emptyPoints.forEach(pt => pt.joinGroup());
|
emptyPoints.forEach(pt => pt.joinGroup());
|
||||||
emptyPointSetTerritory(emptyPoints);
|
emptyPointSetTerritory(emptyPoints);
|
||||||
boardState.filter(pt => {
|
// boardState.filter(pt => {
|
||||||
return pt.groupMembers.length < 6 && pt.stone
|
// return pt.groupMembers.length < 6 && pt.stone
|
||||||
}).forEach(pt => pt.territory = pt.stone * -1);
|
// }).forEach(pt => pt.territory = pt.stone * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emptyPointSetTerritory(emptyPoints) {
|
function emptyPointSetTerritory(emptyPoints) {
|
||||||
// let dame = emptyPoints.filter(pt => pt.checkNeighbors().some(nbr => nbr.territory === 1)
|
emptyPoints.filter(pt => !pt.territory && pt.checkNeighbors().filter(nbr => nbr.stone !== 0))
|
||||||
// && pt.checkNeighbors().some(nbr => nbr.territory === -1));
|
.forEach(pt => {
|
||||||
// dame.forEach(pt => pt.territory = 'd');
|
console.log(pt);
|
||||||
// emptyPoints.filter(pt => pt.territory = 0)
|
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;
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function endGame() {
|
function endGame() {
|
||||||
if (!gameState.winner)
|
if (!gameState.winner) endGameSetTerritory()
|
||||||
endGameSetTerritory()
|
|
||||||
|
|
||||||
// join all remaining groups
|
// join all remaining groups
|
||||||
// check remaining groups life
|
// check remaining groups life
|
||||||
|
@ -593,7 +651,6 @@ function endGame() {
|
||||||
// compare spaces to rotations of deadShapes[...]
|
// compare spaces to rotations of deadShapes[...]
|
||||||
// 'd' if empty spaces
|
// 'd' if empty spaces
|
||||||
|
|
||||||
render();
|
|
||||||
// return dead group suggestion
|
// return dead group suggestion
|
||||||
// users can flip status of any dead group overlay( 1, -1 )
|
// users can flip status of any dead group overlay( 1, -1 )
|
||||||
// confirm state
|
// confirm state
|
||||||
|
@ -602,6 +659,7 @@ function endGame() {
|
||||||
// log game record
|
// log game record
|
||||||
// stringify according to .sgf format
|
// stringify according to .sgf format
|
||||||
// log as text
|
// log as text
|
||||||
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
// game-end
|
// game-end
|
||||||
|
|
Loading…
Reference in a new issue