add audio for stone placement, fix menu overflow bug, fix turn marker bug
This commit is contained in:
parent
45dfa9a36c
commit
165e2d17ea
19 changed files with 410 additions and 331 deletions
54
README.md
54
README.md
|
@ -1,11 +1,57 @@
|
||||||
# Browser Go
|
# Browser Go
|
||||||
|
##### 0.8.1
|
||||||
|
A two-player [in-browser Go application](https://sorrelbri.github.io/browser-go/) developed by Sorrel June.
|
||||||
|
|
||||||
<!-- Screenshot(s): Images of your actual game. -->
|
<!-- Screenshot(s): Images of your actual game. -->
|
||||||
|
|
||||||
<!-- background info on Go -->
|
---
|
||||||
|
## About Go
|
||||||
|
---
|
||||||
|
[skip to application details](#how-browser-go-was-built)
|
||||||
|
|
||||||
<!-- ☐ Technologies Used: List of the technologies used, e.g., JavaScript, HTML, CSS... -->
|
Go is the oldest continuously played game on Earth. The Go ruleset can be understood in an afternoon while offering a depth and complexity that inspires for a lifetime.
|
||||||
|
|
||||||
<!-- ☐ Getting Started: In this section include the link to your deployed game and any instructions you deem important. -->
|
|
||||||
|
|
||||||
<!-- ☐ Next Steps: Planned future enhancements (icebox items). -->
|
|
||||||
|
---
|
||||||
|
## How Browser Go was Built
|
||||||
|
---
|
||||||
|
Browser Go was originally developed for the Software Engineering Immersive at General Assembly in August, 2019.
|
||||||
|
Technologies used inclue:
|
||||||
|
* HTML5
|
||||||
|
* CSS3
|
||||||
|
* JavaScript (ES6)
|
||||||
|
|
||||||
|
Assets acquired from:
|
||||||
|
* subtlepatterns.com
|
||||||
|
* freesound.org
|
||||||
|
* the developer's photos of her go equipment
|
||||||
|
|
||||||
|
---
|
||||||
|
## Using Browser Go
|
||||||
|
---
|
||||||
|
[Play Browser Go Here](https://github.com/sorrelbri/browser-go)
|
||||||
|
|
||||||
|
### Starting a Game
|
||||||
|
![image of game menu at start](images/gameplay-images/browser-go-new-game-screen.png/browser-go-new-game-screen.png){ width=200px }
|
||||||
|
|
||||||
|
Upon initiation of a new game session, Browser Go will display the new game menu. Here, you will be asked to confirm the size of
|
||||||
|
|
||||||
|
### Gameplay Elements
|
||||||
|
|
||||||
|
### Ending a Game
|
||||||
|
|
||||||
|
---
|
||||||
|
## The Future of Browser Go
|
||||||
|
---
|
||||||
|
Browser Go's functionality will evolve as it transitions out of it's current client-side architecture.
|
||||||
|
|
||||||
|
Additional features in development include:
|
||||||
|
* game timer
|
||||||
|
* smart game format support with
|
||||||
|
* read/write .sgf files
|
||||||
|
* tsumego (go problems) support
|
||||||
|
* support for multiple game lines
|
||||||
|
* toggleable 'misclick' undo feature
|
||||||
|
* improved touch screen support
|
||||||
|
* board animations and expanded sound library
|
||||||
|
|
BIN
audio/go_loud.wav
Normal file
BIN
audio/go_loud.wav
Normal file
Binary file not shown.
BIN
audio/go_loud_2.wav
Normal file
BIN
audio/go_loud_2.wav
Normal file
Binary file not shown.
BIN
audio/go_loud_3.wav
Normal file
BIN
audio/go_loud_3.wav
Normal file
Binary file not shown.
BIN
audio/go_loud_4.wav
Normal file
BIN
audio/go_loud_4.wav
Normal file
Binary file not shown.
BIN
audio/go_soft.wav
Normal file
BIN
audio/go_soft.wav
Normal file
Binary file not shown.
BIN
audio/go_soft_2.wav
Normal file
BIN
audio/go_soft_2.wav
Normal file
Binary file not shown.
BIN
audio/go_soft_3.wav
Normal file
BIN
audio/go_soft_3.wav
Normal file
Binary file not shown.
BIN
audio/go_soft_4.wav
Normal file
BIN
audio/go_soft_4.wav
Normal file
Binary file not shown.
BIN
audio/go_soft_5.wav
Normal file
BIN
audio/go_soft_5.wav
Normal file
Binary file not shown.
BIN
audio/go_soft_6.wav
Normal file
BIN
audio/go_soft_6.wav
Normal file
Binary file not shown.
BIN
audio/go_soft_7.wav
Normal file
BIN
audio/go_soft_7.wav
Normal file
Binary file not shown.
25
css/main.css
25
css/main.css
|
@ -48,6 +48,7 @@ body {
|
||||||
"submit";
|
"submit";
|
||||||
font-family: 'La Belle Aurore', cursive;
|
font-family: 'La Belle Aurore', cursive;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
max-height: 100vh;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +202,7 @@ content {
|
||||||
/* border: solid black; */
|
/* border: solid black; */
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: rgb(116, 48, 17);
|
background-color: rgb(116, 48, 17);
|
||||||
background: radial-gradient(closest-corner at 52% 46%, rgba(30, 5, 0, 0.5) 0%, rgb(0,0,0,0.5)35%, rgb(116,48,17) 48%, rgb(140, 60, 40) 52%, rgb(116, 48, 17) 65%, rgb(100,40,5) 70%, rgb(80, 20, 0) 80%);
|
background: radial-gradient(farthest-corner at 48% 54%, rgba(30, 5, 0, 0.25) 0%, rgba(30, 5, 0, 0.45) 2%, rgba(30, 5, 0, 0.75) 32%, rgb(0,0,0,0.85)35%, rgb(116,48,17) 48%, rgb(140, 60, 40) 52%, rgb(100, 40, 5) 55%, rgb(116, 48, 17) 58%, rgb(140,60,40) 65%, rgb(100, 40, 5) 80%, rgb(80, 20, 0) 90%);
|
||||||
box-shadow: -1vmin 2vmin 1.5vmin rgba(83, 53, 35, 0.61);
|
box-shadow: -1vmin 2vmin 1.5vmin rgba(83, 53, 35, 0.61);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -216,9 +217,18 @@ content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
background-size: cover;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#white-stone-image {
|
||||||
|
background-image: url(../images/white-stones-bowl.jpg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#black-stone-image {
|
||||||
|
background-image: url(../images/black-stones-bowl.jpg);
|
||||||
|
}
|
||||||
|
|
||||||
.bowl[data-turn]:hover p {
|
.bowl[data-turn]:hover p {
|
||||||
display: block;
|
display: block;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
|
@ -495,17 +505,8 @@ td .dot[data-dot="dame"] {
|
||||||
@media only screen and (min-width: 900px) {
|
@media only screen and (min-width: 900px) {
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
grid-template-columns: 40vw;
|
grid-template-columns: 55vh;
|
||||||
grid-template-rows: auto auto 40vw auto;
|
grid-template-rows: auto auto 55vh auto;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 1200px) {
|
|
||||||
|
|
||||||
#menu {
|
|
||||||
grid-template-columns: 35vw;
|
|
||||||
grid-template-rows: auto auto 35vw auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
BIN
images/black-stones-bowl.jpg
Normal file
BIN
images/black-stones-bowl.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
BIN
images/gameplay-images/browser-go-new-game-screen.png
Normal file
BIN
images/gameplay-images/browser-go-new-game-screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 276 KiB |
BIN
images/white-stones-bowl.jpg
Normal file
BIN
images/white-stones-bowl.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
24
index.html
24
index.html
|
@ -1,16 +1,16 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
|
||||||
<!-- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" type="stylesheet"> -->
|
<!-- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" type="stylesheet"> -->
|
||||||
<link rel="stylesheet" href="css/reset.css" type="text/css">
|
<link rel="stylesheet" href="css/reset.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/main.css" type="text/css" />
|
<link rel="stylesheet" href="css/main.css" type="text/css" />
|
||||||
<!-- <script defer src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script> -->
|
<!-- <script defer src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script> -->
|
||||||
<script defer src="js/main.js"></script>
|
<script defer src="js/main.js"></script>
|
||||||
<title>Browser Go</title>
|
<title>Browser Go</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
|
@ -25,9 +25,9 @@
|
||||||
<input type="range" min="0" max="9" step="1" value="0" name="handicap-slider">
|
<input type="range" min="0" max="9" step="1" value="0" name="handicap-slider">
|
||||||
<div id="board-size-radio" class="menu-subblock">
|
<div id="board-size-radio" class="menu-subblock">
|
||||||
<p class="menu-heading">Board Size</p>
|
<p class="menu-heading">Board Size</p>
|
||||||
<input type="radio" name="board-size" value="9" checked>9 x 9<br>
|
<input type="radio" name="board-size" value="9">9 x 9<br>
|
||||||
<input type="radio" name="board-size" value="13">13 x 13<br>
|
<input type="radio" name="board-size" value="13">13 x 13<br>
|
||||||
<input type="radio" name="board-size" value="19">19 x 19
|
<input type="radio" name="board-size" value="19" checked>19 x 19
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="player-meta">
|
<div id="player-meta">
|
||||||
|
@ -2293,7 +2293,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="black-pos" class="player-pos">
|
<div id="black-pos" class="player-pos">
|
||||||
<div id="black-bowl" class="bowl"><p>Pass?</p><div id="white-stone-image" class="stone-image"></div></div>
|
<div id="black-bowl" class="bowl"><p>Pass?</p><div id="black-stone-image" class="stone-image"></div></div>
|
||||||
<div id="black-player-space" class="name-space">
|
<div id="black-player-space" class="name-space">
|
||||||
<h4 id="black-player-name">by Sorrel June</h4>
|
<h4 id="black-player-name">by Sorrel June</h4>
|
||||||
<div id="black-caps-space" class="caps-space"><p>Resign?</p><p id="black-caps"></p></div>
|
<div id="black-caps-space" class="caps-space"><p>Resign?</p><p id="black-caps"></p></div>
|
||||||
|
|
612
js/main.js
612
js/main.js
|
@ -44,13 +44,31 @@ const HANDI_REC = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PLACEMENT_SOUNDS = {
|
||||||
|
soft: [
|
||||||
|
'audio/go_soft.wav',
|
||||||
|
'audio/go_soft_2.wav',
|
||||||
|
'audio/go_soft_3.wav',
|
||||||
|
'audio/go_soft_4.wav',
|
||||||
|
'audio/go_soft_5.wav',
|
||||||
|
'audio/go_soft_6.wav',
|
||||||
|
'audio/go_soft_7.wav'
|
||||||
|
],
|
||||||
|
loud: [
|
||||||
|
'audio/go_loud.wav',
|
||||||
|
'audio/go_loud_2.wav',
|
||||||
|
'audio/go_loud_3.wav',
|
||||||
|
'audio/go_loud_4.wav',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const gameState = { // pre-init values (render prior to any player input)
|
const gameState = { // pre-init values (render prior to any player input)
|
||||||
winner: null,
|
winner: null,
|
||||||
turn: 1, // turn logic depends on handicap stones
|
turn: null, // turn logic depends on handicap stones
|
||||||
pass: null, // -1 represents state in which resignation has been submitted, not confirmed
|
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: null,
|
||||||
playerState: {
|
playerState: {
|
||||||
bCaptures: null,
|
bCaptures: null,
|
||||||
wCaptures: null,
|
wCaptures: null,
|
||||||
|
@ -64,12 +82,12 @@ const gameState = { // pre-init values (render prior to any player input)
|
||||||
playerMeta: { // editable during game
|
playerMeta: { // editable during game
|
||||||
b: {
|
b: {
|
||||||
name: null,
|
name: null,
|
||||||
rank: 21,
|
rank: null,
|
||||||
rankCertain: false
|
rankCertain: false
|
||||||
},
|
},
|
||||||
w: {
|
w: {
|
||||||
name: null,
|
name: null,
|
||||||
rank: 21,
|
rank: null,
|
||||||
rankCertain: false
|
rankCertain: false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -77,9 +95,6 @@ const gameState = { // pre-init values (render prior to any player input)
|
||||||
gameRecord : []
|
gameRecord : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// deadShapes{}
|
|
||||||
|
|
||||||
// index represents handicap placement for different board-sizes, eg handiPlace['9][1] = { (3, 3), (7, 7) }
|
// index represents handicap placement for different board-sizes, eg handiPlace['9][1] = { (3, 3), (7, 7) }
|
||||||
// last array in each property also used for hoshi rendering
|
// last array in each property also used for hoshi rendering
|
||||||
const HANDI_PLACE = {
|
const HANDI_PLACE = {
|
||||||
|
@ -113,16 +128,6 @@ const HANDI_PLACE = {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const BOARD_POINT_SIZE = {
|
|
||||||
'9' : '9vmin',
|
|
||||||
'13': '6vmin',
|
|
||||||
'19' : '4vmin'
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----- app's state (variables) -----*/
|
|
||||||
|
|
||||||
let boardState = [];
|
|
||||||
|
|
||||||
class Point {
|
class Point {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
this.pos = [ x, y ]
|
this.pos = [ x, y ]
|
||||||
|
@ -191,27 +196,31 @@ class Point {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cycleTerritory = () => {
|
cycleTerritory = () => {
|
||||||
if (this.stone) {
|
if (this.stone) {
|
||||||
this.groupMembers.forEach(pt => pt.territory = pt.territory * -1);
|
this.groupMembers.forEach(pt => pt.territory = pt.territory * -1);
|
||||||
} else {
|
} else {
|
||||||
this.groupMembers.forEach(pt => {
|
this.groupMembers.forEach(pt => {
|
||||||
switch (pt.territory) {
|
switch (pt.territory) {
|
||||||
case 1:
|
case 1:
|
||||||
pt.territory = -1;
|
pt.territory = -1;
|
||||||
break;
|
break;
|
||||||
case -1:
|
case -1:
|
||||||
pt.territory = 'd';
|
pt.territory = 'd';
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
pt.territory = 1;
|
pt.territory = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----- app's state (variables) -----*/
|
||||||
|
let boardState = [];
|
||||||
|
|
||||||
|
|
||||||
/*----- cached element references -----*/
|
/*----- cached element references -----*/
|
||||||
const whiteCapsEl = document.getElementById('white-caps');
|
const whiteCapsEl = document.getElementById('white-caps');
|
||||||
const blackCapsEl = document.getElementById('black-caps');
|
const blackCapsEl = document.getElementById('black-caps');
|
||||||
|
@ -236,6 +245,7 @@ const handiDisplayEl = document.getElementById('handicap');
|
||||||
const boardEl = document.querySelector('#board tbody');
|
const boardEl = document.querySelector('#board tbody');
|
||||||
const gameStartEl = document.querySelector('input[name="game-start"]');
|
const gameStartEl = document.querySelector('input[name="game-start"]');
|
||||||
const komiSuggestEl = document.querySelector('input[name="komi-suggest"]');
|
const komiSuggestEl = document.querySelector('input[name="komi-suggest"]');
|
||||||
|
const soundPlayerEl = new Audio();
|
||||||
const boardSizeRadioEls = [
|
const boardSizeRadioEls = [
|
||||||
document.querySelectorAll('input[name="board-size"')[0],
|
document.querySelectorAll('input[name="board-size"')[0],
|
||||||
document.querySelectorAll('input[name="board-size"')[1],
|
document.querySelectorAll('input[name="board-size"')[1],
|
||||||
|
@ -260,11 +270,80 @@ gameHudEl.addEventListener('click', clickGameHud);
|
||||||
boardSizeEl.addEventListener('click', clickBoardSize);
|
boardSizeEl.addEventListener('click', clickBoardSize);
|
||||||
gameStartEl.addEventListener('click', clickSubmitStart);
|
gameStartEl.addEventListener('click', clickSubmitStart);
|
||||||
|
|
||||||
/*----- functions -----*/
|
/*----- FUNCTIONS ----------------------------------*/
|
||||||
|
/*----- init functions -----*/
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
gameState.gameMeta.date = getDate();
|
||||||
|
gameState.komi = 5.5;
|
||||||
|
gameState.handicap = 0;
|
||||||
|
gameState.winner = null;
|
||||||
|
gameState.pass = null;
|
||||||
|
gameState.boardSize = 19;
|
||||||
|
gameState.playerState.bCaptures = 0;
|
||||||
|
gameState.playerMeta.b.rank = 21;
|
||||||
|
gameState.playerState.wCaptures = 0;
|
||||||
|
gameState.playerMeta.w.rank = 21;
|
||||||
|
gameState.gameRecord = [];
|
||||||
|
boardState = [];
|
||||||
|
gameState.gameMeta.start = false;
|
||||||
|
startMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
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 startMenu() {
|
||||||
|
modalEl.style.visibility = 'visible';
|
||||||
|
renderMenu();
|
||||||
|
}
|
||||||
|
function clickSubmitStart(evt) {
|
||||||
|
if (gameState.gameMeta.start) return init();
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
gameState.playerMeta.b.name = blackNameInputEl.value || 'black';
|
||||||
|
gameState.playerMeta.w.name = whiteNameInputEl.value || 'white';
|
||||||
|
modalEl.style.visibility = 'hidden';
|
||||||
|
initGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGame() {
|
||||||
|
gameState.winner = null;
|
||||||
|
gameState.pass = null;
|
||||||
|
gameState.turn = gameState.handicap ? -1 : 1;
|
||||||
|
gameState.gameMeta.start = true;
|
||||||
|
initBoard();
|
||||||
|
renderBoardInit();
|
||||||
|
renderGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if (gameState.handicap < 2) return;
|
||||||
|
HANDI_PLACE[gameState.boardSize][gameState.handicap].forEach(pt => {
|
||||||
|
if (!pt) return;
|
||||||
|
let handi = findPointFromIdx(pt);
|
||||||
|
handi.stone = 1;
|
||||||
|
handi.joinGroup();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----- meta functions -----*/
|
||||||
|
// plus general purpose
|
||||||
|
|
||||||
function findPointFromIdx(arr) {
|
function findPointFromIdx(arr) {
|
||||||
console.log(arr);
|
|
||||||
return pointFromIdx = boardState.find( point => point.pos[0] === arr[0] && point.pos[1] === arr[1] );
|
return pointFromIdx = boardState.find( point => point.pos[0] === arr[0] && point.pos[1] === arr[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,16 +366,16 @@ function clickUpdatePlayerMeta(evt) {
|
||||||
if (evt.target.id) {
|
if (evt.target.id) {
|
||||||
switch (evt.target.id) {
|
switch (evt.target.id) {
|
||||||
case 'black-rank-up':
|
case 'black-rank-up':
|
||||||
gameState.playerMeta.b.rank++;
|
if (gameState.playerMeta.b.rank < RANKS.length - 1) gameState.playerMeta.b.rank++;
|
||||||
break;
|
break;
|
||||||
case 'black-rank-down':
|
case 'black-rank-down':
|
||||||
gameState.playerMeta.b.rank--;
|
if (gameState.playerMeta.b.rank > 0) gameState.playerMeta.b.rank--;
|
||||||
break;
|
break;
|
||||||
case 'white-rank-up':
|
case 'white-rank-up':
|
||||||
gameState.playerMeta.w.rank++;
|
if (gameState.playerMeta.w.rank < RANKS.length - 1) gameState.playerMeta.w.rank++;
|
||||||
break;
|
break;
|
||||||
case 'white-rank-down':
|
case 'white-rank-down':
|
||||||
gameState.playerMeta.w.rank--;
|
if (gameState.playerMeta.w.rank > 0) gameState.playerMeta.w.rank--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,109 +407,54 @@ function clickKomiSuggestion(evt) {
|
||||||
renderMenu();
|
renderMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickGameHud() {
|
|
||||||
if (gameState.pass > 1 && !gameState.winner) calculateWinner();
|
|
||||||
if (gameState.pass < 0) confirmResign();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickSubmitStart(evt) {
|
|
||||||
if (gameState.gameMeta.start) return init();
|
|
||||||
evt.preventDefault();
|
|
||||||
evt.stopPropagation();
|
|
||||||
gameState.playerMeta.b.name = blackNameInputEl.value || 'black';
|
|
||||||
gameState.playerMeta.w.name = whiteNameInputEl.value || 'white';
|
|
||||||
modalEl.style.visibility = 'hidden';
|
|
||||||
initGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderKomi() {
|
|
||||||
komiSliderEl.value = gameState.komi;
|
|
||||||
komiDisplayEl.textContent = gameState.komi;
|
|
||||||
if (gameState.gameMeta.start) komiSliderEl.setAttribute('disabled', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderHandiSlider() {
|
|
||||||
handiSliderEl.value = gameState.handicap;
|
|
||||||
handiDisplayEl.textContent = gameState.handicap;
|
|
||||||
if (gameState.gameMeta.start) handiSliderEl.setAttribute('disabled', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderBoardSizeRadio() {
|
|
||||||
boardSizeEl.value = gameState.boardSize;
|
|
||||||
if (gameState.gameMeta.start) boardSizeRadioEls.forEach(el => el.setAttribute('disabled', true));
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMenu() {
|
|
||||||
dateEl.textContent = gameState.gameMeta.date;
|
|
||||||
if (gameState.gameMeta.start) {
|
|
||||||
gameStartEl.value = "New Game";
|
|
||||||
komiSuggestEl.value = "Close Menu";
|
|
||||||
}
|
|
||||||
renderKomi()
|
|
||||||
renderHandiSlider();
|
|
||||||
renderBoardSizeRadio();
|
|
||||||
blackRankEl.textContent = RANKS[gameState.playerMeta.b.rank];
|
|
||||||
whiteRankEl.textContent = RANKS[gameState.playerMeta.w.rank];
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
renderGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickMenuOpen() {
|
|
||||||
modalEl.style.visibility = 'visible';
|
|
||||||
renderMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
function startMenu() {
|
|
||||||
modalEl.style.visibility = 'visible';
|
|
||||||
renderMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickCloseMenu(evt) {
|
function clickCloseMenu(evt) {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
if (evt.target.className === "modal" && gameState.gameMeta.start) modalEl.style.visibility = 'hidden';
|
if (evt.target.className === "modal" && gameState.gameMeta.start) modalEl.style.visibility = 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickResign(evt) {
|
/*----- gameplay functions -----*/
|
||||||
if (evt.target.parentElement.id === `${STONES_DATA[gameState.turn]}-caps-space`) playerResign();
|
|
||||||
}
|
|
||||||
|
|
||||||
function playerResign() {
|
function clickBoard(evt) {
|
||||||
// display confirmation message
|
|
||||||
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.winner = STONES_DATA[gameState.turn * -1];
|
|
||||||
endGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hoverPreview(evt) {
|
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
if (gameState.pass > 1 || gameState.winner) return;
|
if (gameState.pass > 1 || gameState.winner) return editTerritory(evt);
|
||||||
// renders preview stone if move is legal
|
// checks for placement and pushes to cell
|
||||||
let hover = evt.target.closest('td').id.split('-');
|
let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ];
|
||||||
hover = [parseInt(hover[0]), parseInt(hover[1])]
|
let point = findPointFromIdx(placement);
|
||||||
let point = findPointFromIdx(hover);
|
//checks that this placement was marked as legal
|
||||||
if (checkLegal(point)) {
|
if ( !checkLegal(point) ) return;
|
||||||
point.legal = true; // legal
|
clearKo();
|
||||||
renderPreview(point);
|
clearPass();
|
||||||
|
resolveCaptures(point);
|
||||||
|
point.stone = gameState.turn;
|
||||||
|
point.joinGroup();
|
||||||
|
playSound(point);
|
||||||
|
clearCaptures();
|
||||||
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: ${point.pos}`)
|
||||||
|
gameState.turn*= -1;
|
||||||
|
renderGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearKo() {
|
||||||
|
for (let point in boardState) {
|
||||||
|
point = boardState[point];
|
||||||
|
point.stone = point.stone === 'k' ? 0 : point.stone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearPass() {
|
||||||
|
gameState.pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveCaptures(point) {
|
||||||
|
if(!point.capturing.length) {
|
||||||
|
point.checkCapture();
|
||||||
|
}
|
||||||
|
if(point.capturing.length) {
|
||||||
|
point.capturing.forEach(cap => {
|
||||||
|
gameState.playerState[gameState.turn > 0 ? 'bCaptures' : 'wCaptures']++;
|
||||||
|
cap.groupMembers = [];
|
||||||
|
cap.stone = checkKo(point, cap) ? 'k' : 0;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,61 +480,20 @@ function clearOverlay() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveCaptures(point) {
|
|
||||||
if(!point.capturing.length) {
|
|
||||||
point.checkCapture();
|
|
||||||
}
|
|
||||||
if(point.capturing.length) {
|
|
||||||
point.capturing.forEach(cap => {
|
|
||||||
gameState.playerState[gameState.turn > 0 ? 'bCaptures' : 'wCaptures']++;
|
|
||||||
cap.groupMembers = [];
|
|
||||||
cap.stone = checkKo(point, cap) ? 'k' : 0;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function editTerritory(evt) {
|
|
||||||
let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ];
|
|
||||||
let point = findPointFromIdx(placement);
|
|
||||||
point.cycleTerritory();
|
|
||||||
renderGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkKo(point, cap) {
|
function checkKo(point, cap) {
|
||||||
if (!point.getLiberties().length && cap.checkNeighbors().filter(stone => stone.stone === gameState.turn * -1)
|
if (!point.getLiberties().length && cap.checkNeighbors().filter(stone => stone.stone === gameState.turn * -1)
|
||||||
&& point.capturing.length === 1) return true;
|
&& point.capturing.length === 1) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickBoard(evt) {
|
function playSound(point) { //plays louder sounds for tenuki and for captures
|
||||||
evt.stopPropagation();
|
if (point.capturing.length || (gameState.boardSize === 19 && gameState.gameRecord.length > 90 && point.groupMembers.length === 1)
|
||||||
if (gameState.pass > 1 || gameState.winner) return editTerritory(evt);
|
|| (gameState.boardSize === 13 && gameState.gameRecord.length > 40 && point.groupMembers.length === 1)) {
|
||||||
// checks for placement and pushes to cell
|
soundPlayerEl.src = PLACEMENT_SOUNDS.loud[Math.floor(Math.random() * 5)];
|
||||||
let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ];
|
soundPlayerEl.play();
|
||||||
console.log(placement);
|
} else {
|
||||||
console.log(evt);
|
soundPlayerEl.src = PLACEMENT_SOUNDS.soft[Math.floor(Math.random() * 8)];
|
||||||
let point = findPointFromIdx(placement);
|
soundPlayerEl.play();
|
||||||
//checks that this placement was marked as legal
|
}
|
||||||
if ( !checkLegal(point) ) return;
|
|
||||||
clearKo();
|
|
||||||
clearPass();
|
|
||||||
resolveCaptures(point);
|
|
||||||
point.stone = gameState.turn;
|
|
||||||
point.joinGroup();
|
|
||||||
clearCaptures();
|
|
||||||
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: ${point.pos}`)
|
|
||||||
gameState.turn*= -1;
|
|
||||||
renderGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearKo() {
|
|
||||||
for (let point in boardState) {
|
|
||||||
point = boardState[point];
|
|
||||||
point.stone = point.stone === 'k' ? 0 : point.stone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearPass() {
|
|
||||||
gameState.pass = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearCaptures() {
|
function clearCaptures() {
|
||||||
|
@ -520,71 +503,74 @@ function clearCaptures() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initBoard() {
|
function clickPass(evt) {
|
||||||
let i = 0;
|
if (evt.target.parentElement.id === `${STONES_DATA[gameState.turn]}-bowl`) playerPass();
|
||||||
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() {
|
function playerPass() {
|
||||||
if (gameState.handicap < 2) return;
|
// display confirmation message
|
||||||
HANDI_PLACE[gameState.boardSize][gameState.handicap].forEach(pt => {
|
clearKo();
|
||||||
if (!pt) return;
|
clearCaptures();
|
||||||
let handi = findPointFromIdx(pt);
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: pass`)
|
||||||
handi.stone = 1;
|
gameState.pass++;
|
||||||
handi.joinGroup();
|
if (gameState.pass === 2) return endGame();
|
||||||
})
|
gameState.turn*= -1;
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
gameState.gameMeta.date = getDate();
|
|
||||||
gameState.komi = 5.5;
|
|
||||||
gameState.handicap = 0;
|
|
||||||
gameState.winner = null;
|
|
||||||
gameState.pass = null;
|
|
||||||
gameState.boardSize = 9;
|
|
||||||
gameState.playerState.bCaptures = 0;
|
|
||||||
gameState.playerState.wCaptures = 0;
|
|
||||||
gameState.gameRecord = [];
|
|
||||||
boardState = [];
|
|
||||||
gameState.gameMeta.start = false;
|
|
||||||
startMenu();
|
|
||||||
};
|
|
||||||
|
|
||||||
function initGame() {
|
|
||||||
gameState.winner = null;
|
|
||||||
gameState.pass = null;
|
|
||||||
gameState.turn = gameState.handicap ? -1 : 1;
|
|
||||||
gameState.gameMeta.start = true;
|
|
||||||
initBoard();
|
|
||||||
renderBoardInit();
|
|
||||||
renderGame();
|
renderGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGame() {
|
function clickMenuOpen() {
|
||||||
if (gameState.winner || gameState.pass > 1) {
|
modalEl.style.visibility = 'visible';
|
||||||
renderTerritory();
|
renderMenu();
|
||||||
renderMessage();
|
|
||||||
}
|
|
||||||
blackNameDisplayEl.textContent =
|
|
||||||
`${gameState.playerMeta.b.name},
|
|
||||||
${gameState.playerMeta.b.rank}`;
|
|
||||||
whiteNameDisplayEl.textContent =
|
|
||||||
`${gameState.playerMeta.w.name},
|
|
||||||
${gameState.playerMeta.w.rank}`;
|
|
||||||
gameState.gameRecord.length? renderTurn() : renderFirstTurn();
|
|
||||||
renderBoardState();
|
|
||||||
renderCaps();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hoverPreview(evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
if (gameState.pass > 1 || gameState.winner) return;
|
||||||
|
// renders preview stone if move is legal
|
||||||
|
let hover = evt.target.closest('td').id.split('-');
|
||||||
|
hover = [parseInt(hover[0]), parseInt(hover[1])]
|
||||||
|
let point = findPointFromIdx(hover);
|
||||||
|
if (checkLegal(point)) {
|
||||||
|
point.legal = true; // legal
|
||||||
|
renderPreview(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----- render functions ----------------------*/
|
||||||
|
/*----- meta render -----*/
|
||||||
|
|
||||||
|
function renderMenu() {
|
||||||
|
dateEl.textContent = gameState.gameMeta.date;
|
||||||
|
if (gameState.gameMeta.start) {
|
||||||
|
gameStartEl.value = "New Game";
|
||||||
|
komiSuggestEl.value = "Close Menu";
|
||||||
|
}
|
||||||
|
renderKomiSlider()
|
||||||
|
renderHandiSlider();
|
||||||
|
renderBoardSizeRadio();
|
||||||
|
blackRankEl.textContent = RANKS[gameState.playerMeta.b.rank];
|
||||||
|
whiteRankEl.textContent = RANKS[gameState.playerMeta.w.rank];
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderKomiSlider() {
|
||||||
|
komiSliderEl.value = gameState.komi;
|
||||||
|
komiDisplayEl.textContent = gameState.komi;
|
||||||
|
if (gameState.gameMeta.start) komiSliderEl.setAttribute('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHandiSlider() {
|
||||||
|
handiSliderEl.value = gameState.handicap;
|
||||||
|
handiDisplayEl.textContent = gameState.handicap;
|
||||||
|
if (gameState.gameMeta.start) handiSliderEl.setAttribute('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBoardSizeRadio() {
|
||||||
|
boardSizeEl.value = gameState.boardSize;
|
||||||
|
if (gameState.gameMeta.start) boardSizeRadioEls.forEach(el => el.setAttribute('disabled', true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----- game render -----*/
|
||||||
|
|
||||||
function renderBoardInit() {
|
function renderBoardInit() {
|
||||||
renderClearBoard();
|
renderClearBoard();
|
||||||
renderBoardTableRows();
|
renderBoardTableRows();
|
||||||
|
@ -592,30 +578,11 @@ function renderBoardInit() {
|
||||||
renderBoardTableStyle();
|
renderBoardTableStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderHoshi() { // gets hoshi placement from handiplace const and adds a class to dot elem
|
|
||||||
let hoshi = HANDI_PLACE[gameState.boardSize].slice(-1);
|
|
||||||
hoshi = hoshi[0]
|
|
||||||
hoshi.forEach(star => {
|
|
||||||
console.log(hoshi);
|
|
||||||
console.log(`star: ${star[0][0]}
|
|
||||||
${star[0][1]} end star`)
|
|
||||||
let boardPt = document.getElementById(`${star[0]}-${star[1]}`).getElementsByClassName('stone')[0];
|
|
||||||
console.log(boardPt);
|
|
||||||
boardPt.className += ' hoshi' });
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderClearBoard() {
|
function renderClearBoard() {
|
||||||
boardEl.innerHTML = '';
|
boardEl.innerHTML = '';
|
||||||
boardEl.classList = '';
|
boardEl.classList = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBoardTableStyle() {
|
|
||||||
document.querySelectorAll('#board-space td[id^="1-"]').forEach(pt => pt.className += 'top ');
|
|
||||||
document.querySelectorAll(`#board-space td[id^="${gameState.boardSize}-"]`).forEach(pt => pt.className += 'btm ');
|
|
||||||
document.querySelectorAll('#board-space td[id$="-1"]').forEach(pt => pt.className += 'lft ');
|
|
||||||
document.querySelectorAll(`#board-space td[id$="-${gameState.boardSize}"]`).forEach(pt => pt.className += 'rgt ');
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderBoardTableRows() {
|
function renderBoardTableRows() {
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (i <= gameState.boardSize) {
|
while (i <= gameState.boardSize) {
|
||||||
|
@ -628,6 +595,7 @@ function renderBoardTableRows() {
|
||||||
boardEl.classList = `board-${gameState.boardSize}x${gameState.boardSize}`;
|
boardEl.classList = `board-${gameState.boardSize}x${gameState.boardSize}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iterator ^ becomes x index ̌
|
||||||
function renderBoardTableCells(x) {
|
function renderBoardTableCells(x) {
|
||||||
let y = 1
|
let y = 1
|
||||||
let cells = '';
|
let cells = '';
|
||||||
|
@ -645,6 +613,69 @@ function renderBoardTableCells(x) {
|
||||||
return cells;
|
return cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderHoshi() { // gets hoshi placement from handiplace const and adds a class to dot elem
|
||||||
|
let hoshi = HANDI_PLACE[gameState.boardSize].slice(-1);
|
||||||
|
hoshi = hoshi[0]
|
||||||
|
hoshi.forEach(star => {
|
||||||
|
let boardPt = document.getElementById(`${star[0]}-${star[1]}`).getElementsByClassName('stone')[0];
|
||||||
|
boardPt.className += ' hoshi' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBoardTableStyle() {
|
||||||
|
document.querySelectorAll('#board-space td[id^="1-"]').forEach(pt => pt.className += 'top ');
|
||||||
|
document.querySelectorAll(`#board-space td[id^="${gameState.boardSize}-"]`).forEach(pt => pt.className += 'btm ');
|
||||||
|
document.querySelectorAll('#board-space td[id$="-1"]').forEach(pt => pt.className += 'lft ');
|
||||||
|
document.querySelectorAll(`#board-space td[id$="-${gameState.boardSize}"]`).forEach(pt => pt.className += 'rgt ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGame() {
|
||||||
|
if (gameState.winner || gameState.pass > 1) {
|
||||||
|
renderTerritory();
|
||||||
|
renderMessage();
|
||||||
|
}
|
||||||
|
blackNameDisplayEl.textContent =
|
||||||
|
`${gameState.playerMeta.b.name},
|
||||||
|
${RANKS[gameState.playerMeta.b.rank]}`;
|
||||||
|
whiteNameDisplayEl.textContent =
|
||||||
|
`${gameState.playerMeta.w.name},
|
||||||
|
${RANKS[gameState.playerMeta.w.rank]}`;
|
||||||
|
gameState.gameRecord.length ? renderTurn() : renderFirstTurn();
|
||||||
|
renderBoardState();
|
||||||
|
renderCaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFirstTurn() {
|
||||||
|
document.getElementById(`${STONES_DATA[gameState.turn]}-bowl`).toggleAttribute('data-turn');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTurn() {
|
||||||
|
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 renderBoardState() {
|
||||||
|
boardState.forEach(val => {
|
||||||
|
let stoneElem = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).getElementsByClassName('stone')[0];
|
||||||
|
stoneElem.setAttribute("data-stone", STONES_DATA[val.stone]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCaps() {
|
||||||
|
blackCapsEl.textContent = gameState.playerState.bCaptures;
|
||||||
|
whiteCapsEl.textContent = gameState.playerState.wCaptures;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPreview(hoverPoint) {
|
||||||
|
boardState.forEach(val => {
|
||||||
|
let dot = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).getElementsByClassName('dot')[0];
|
||||||
|
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]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function renderMessage() {
|
function renderMessage() {
|
||||||
if (gameState.winner && gameState.pass < 2) {
|
if (gameState.winner && gameState.pass < 2) {
|
||||||
gameHudEl.style.visibility = 'visible';
|
gameHudEl.style.visibility = 'visible';
|
||||||
|
@ -670,36 +701,40 @@ function renderTerritory() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFirstTurn() {
|
/*----- endgame functions -----*/
|
||||||
document.getElementById(`${STONES_DATA[gameState.turn]}-bowl`).toggleAttribute('data-turn');
|
|
||||||
}
|
|
||||||
function renderTurn() {
|
|
||||||
if (gameState.winner || gameState.pass > 1) document.querySelectorAll(`.bowl`).forEach(bowl => {
|
|
||||||
bowl.removeAttribute('data-turn');
|
|
||||||
bowl.toggleAttribute('data-turn');
|
|
||||||
|
|
||||||
});
|
function clickResign(evt) {
|
||||||
document.querySelectorAll(`.bowl`).forEach(bowl => bowl.toggleAttribute('data-turn'));
|
if (evt.target.parentElement.id === `${STONES_DATA[gameState.turn]}-caps-space`) playerResign();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBoardState() {
|
function playerResign() {
|
||||||
boardState.forEach(val => {
|
// display confirmation message
|
||||||
let stoneElem = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).getElementsByClassName('stone')[0];
|
gameState.pass = -1;
|
||||||
stoneElem.setAttribute("data-stone", STONES_DATA[val.stone]);
|
gameHudEl.style.visibility = "visible";
|
||||||
})
|
gameHudEl.textContent = "Do you want to resign?";
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCaps() {
|
function clickGameHud() {
|
||||||
blackCapsEl.textContent = gameState.playerState.bCaptures;
|
if (gameState.pass > 1 && !gameState.winner) calculateWinner();
|
||||||
whiteCapsEl.textContent = gameState.playerState.wCaptures;
|
if (gameState.pass < 0) confirmResign();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPreview(hoverPoint) {
|
function confirmResign() {
|
||||||
boardState.forEach(val => {
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: resign`);
|
||||||
let dot = document.getElementById(`${val.pos[0]}-${val.pos[1]}`).getElementsByClassName('dot')[0];
|
gameState.winner = STONES_DATA[gameState.turn * -1];
|
||||||
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]);
|
endGame();
|
||||||
|
}
|
||||||
|
|
||||||
})
|
function endGame() {
|
||||||
|
if (!gameState.winner) endGameSetTerritory()
|
||||||
|
renderGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function editTerritory(evt) {
|
||||||
|
let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ];
|
||||||
|
let point = findPointFromIdx(placement);
|
||||||
|
point.cycleTerritory();
|
||||||
|
renderGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateWinner() {
|
function calculateWinner() {
|
||||||
|
@ -766,8 +801,3 @@ function emptyPointSetTerritory(emptyPoints) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function endGame() {
|
|
||||||
if (!gameState.winner) endGameSetTerritory()
|
|
||||||
renderGame();
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Browser Go
|
# Browser Go
|
||||||
#### Minimum Deliverable Product
|
#### Version 1 Requirements
|
||||||
|
|
||||||
a working game of go for a 9x9 board that
|
a working game of go that
|
||||||
- [x] displays well on mobile or desktop
|
- [x] displays well on mobile or desktop
|
||||||
- [x] initiates a game with suggested handicap and komi according to rank input
|
- [x] initiates a game with suggested handicap and komi according to rank input
|
||||||
- [x] displays how to play in open screen
|
- [x] displays how to play in open screen
|
||||||
|
@ -19,19 +19,21 @@ a working game of go for a 9x9 board that
|
||||||
- [x] allows users to submit finalized score to game record
|
- [x] allows users to submit finalized score to game record
|
||||||
- [ ] displays game record as string
|
- [ ] displays game record as string
|
||||||
|
|
||||||
stretch goals
|
additional features
|
||||||
- [x] uses stone placement GUI for resign and pass
|
- [x] uses stone placement GUI for resign and pass
|
||||||
- [ ] maintains a one move game state history for 'undo mismove'
|
- [ ] maintains a one move game state history for 'undo mismove'
|
||||||
- [ ] converts string to .sgf format
|
- [ ] converts string to .sgf format
|
||||||
- [x] allows users to edit game info mid game
|
- [x] allows users to edit game info mid game
|
||||||
- [ ] add stone placement sounds
|
- [x] add stone placement sounds
|
||||||
|
|
||||||
superstretch goals
|
|
||||||
- [x] allows users to select board size (9x9, 13x13, 19x19)
|
- [x] allows users to select board size (9x9, 13x13, 19x19)
|
||||||
- [ ] allows users to load .sgf main lines
|
- [x] 9x9 games simply stretch with screen size
|
||||||
- [ ] allow for responsivity in the form of
|
- [ ] timed game functionality
|
||||||
- - [x] 9x9 games simply stretch with screen size
|
- [ ] larger games allow small displays one click to zoom before running legal move calculations and move placement
|
||||||
- - [ ] larger games allow small displays one click to zoom before running legal move calculations and move placement
|
|
||||||
|
later version features
|
||||||
|
- [ ] allows users to read/write .sgf files
|
||||||
|
- [ ] allow users to edit multiple game lines
|
||||||
|
- [ ] allow users to play and generate tsumego
|
||||||
|
|
||||||
<!-- describe go with images of game-->
|
<!-- describe go with images of game-->
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue