plan project

This commit is contained in:
Sorrel Bri 2019-08-01 17:10:56 -07:00
parent e2b5173957
commit d5b56f25c9
7 changed files with 512 additions and 234 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View file

@ -1,9 +1,41 @@
# SEI-CC-4 - Build a Browser Game # Go
## Connect Four #### Minimum Deliverable Product
After this repo is cloned, you will be able to syncronize with its most recent commit by running the following two commands: a working game of go for a 9x9 board that
* displays well on mobile
* initiates a game with suggested handicap and komi according to rank input
* * displays how to play in open screen
* lets the user know whose turn it is
* lets the user know which moves are legal and calculates those accordingly
* * logs ko
* * implement a search algorithm to avoid moving into dead space
* correctly removes captured stones and adds them to capturing player's score
* logs game record
* maintains a one move game state history for 'undo mismove'
* allows players to pass or resign
* * ends game upon 2 consecutive passes
* calculates estimated score at game end
* * compares board groups to most common dead shapes
* * allows users to override dead group estimates and submit finalized score to game record
* displays game record as string
1. `git fetch --all` stretch goals
2. `git reset --hard origin/master` * uses stone placement GUI for resign and pass
* converts string to .sgf format
* allows users to edit game info mid game
* add stone placement sounds
superstretch goals
* allows users to select board size (9x9, 13x13, 19x19)
* allows users to load .sgf main lines
* allow for responsivity in the form of
* * 9x9 games simply stretch with screen size
* * larger games allow small displays one click to zoom before running legal move calculations and move placement
Note that running the above commands will completely replace all of the files with those in the repo - **ANY CODE YOU HAVE WRITTEN WILL BE REPLACED**. <!-- describe go with images of game-->
<!-- List of technologies used -->
<!-- How to play, link to deploy -->
<!-- roadmap -->

View file

@ -1,42 +1,15 @@
*, *::before, *::after { * {
box-sizing: border-box; box-sizing: border-box;
} }
body { .full-screen {
font-family: Montserrat; /* positioning will be absolue */
height: 100vh; /* will take up whole screen */
margin: 0; /* background will be ~0.5 opacity */
display: flex; /* grid-areas
flex-direction: column; "game-info *4" <-date, komi, handicap
justify-content: center; "b-player-info*2 w-player-info*2" <- name, rank, rank-certainty
align-items: center; "record record record options" <- displays numbered record (stretch!), new game, get game record(stretch!)
} pre-game record will display instructions
*/
section.markers {
display: grid;
grid-template-columns: repeat(7, 10vmin);
grid-gap: 1vmin;
margin-bottom: 1vmin;
}
section.markers div {
border-top: 5vmin solid lightgrey;
border-left: 5vmin solid transparent;
border-right: 5vmin solid transparent;
}
section.markers div:hover {
border-top-color: grey;
}
section.board {
display: grid;
grid-template-columns: repeat(7, 10vmin);
grid-template-rows: repeat(6, 10vmin);
grid-gap: 1vmin;
}
section.board div {
border-radius: 50%;
border: 1px solid grey;
} }

366
css/reset.css Normal file
View file

@ -0,0 +1,366 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0-modified | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* make sure to set some focus styles for accessibility */
:focus {
outline: 0;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
input[type=search]::-webkit-search-cancel-button,
input[type=search]::-webkit-search-decoration,
input[type=search]::-webkit-search-results-button,
input[type=search]::-webkit-search-results-decoration {
-webkit-appearance: none;
-moz-appearance: none;
}
input[type=search] {
-webkit-appearance: none;
-moz-appearance: none;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
textarea {
overflow: auto;
vertical-align: top;
resize: vertical;
}
/**
* Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
max-width: 100%;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/**
* 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/**
* 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improve image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/**
* Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/**
* Correct margin displayed oddly in IE 6/7.
*/
form {
margin: 0;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct color not being inherited in IE 6/7/8/9.
* 2. Correct text not wrapping in Firefox 3.
* 3. Correct alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/**
* 1. Correct font size not being inherited in all browsers.
* 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improve appearance and consistency in all browsers.
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/**
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
* 4. Remove inner spacing in IE 7 without affecting normal text inputs.
* Known issue: inner spacing remains in IE 6.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
*overflow: visible; /* 4 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to content-box in IE 8/9.
* 2. Remove excess padding in IE 8/9.
* 3. Remove excess padding in IE 7.
* Known issue: excess padding remains in IE 6.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 6/7/8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
html,
button,
input,
select,
textarea {
color: #222;
}
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
::selection {
background: #b3d4fc;
text-shadow: none;
}
img {
vertical-align: middle;
}
fieldset {
border: 0;
margin: 0;
padding: 0;
}
textarea {
resize: vertical;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}

BIN
go wireframe.tif Normal file

Binary file not shown.

View file

@ -1,74 +1,30 @@
<!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">
<title>Connect Four</title> <link rel="stylesheet" href="css/reset.css" type="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet"> <link rel="stylesheet" href="css/main.css" type="stylesheet">
<link rel="stylesheet" href="css/main.css"> <script src="js/main.js"></script>
<script defer src="js/main.js"></script> <title>Go Cats</title>
</head> </head>
<body> <body>
<h1>CONNECT FOUR</h1> <div id="menu" class="full-screen">
<h2 id="msg"></h2> <!-- for displaying new game screen and status during game -->
<section class="markers"> </div>
<div id="col0"></div> <!-- may need add'l .full-screen or other div for board overlay -->
<div id="col1"></div> <content>
<div id="col2"></div> <div id="white-pos" class="player-pos">
<div id="col3"></div> <div id="white-bowl" class="bowl"></div>
<div id="col4"></div> <div id="white-caps" class="caps"></div>
<div id="col5"></div> </div>
<div id="col6"></div> <div id="board"></div>
</section> <div id="black-pos" class="player-pos">
<section class="board"> <div id="black-bowl" class="bowl"></div>
<div id="c0r5"></div> <div id="black-caps" class="caps"></div>
<div id="c1r5"></div> <div id="kifu"></div>
<div id="c2r5"></div> </div>
<div id="c3r5"></div> </content>
<div id="c4r5"></div>
<div id="c5r5"></div>
<div id="c6r5"></div>
<div id="c0r4"></div>
<div id="c1r4"></div>
<div id="c2r4"></div>
<div id="c3r4"></div>
<div id="c4r4"></div>
<div id="c5r4"></div>
<div id="c6r4"></div>
<div id="c0r3"></div>
<div id="c1r3"></div>
<div id="c2r3"></div>
<div id="c3r3"></div>
<div id="c4r3"></div>
<div id="c5r3"></div>
<div id="c6r3"></div>
<div id="c0r2"></div>
<div id="c1r2"></div>
<div id="c2r2"></div>
<div id="c3r2"></div>
<div id="c4r2"></div>
<div id="c5r2"></div>
<div id="c6r2"></div>
<div id="c0r1"></div>
<div id="c1r1"></div>
<div id="c2r1"></div>
<div id="c3r1"></div>
<div id="c4r1"></div>
<div id="c5r1"></div>
<div id="c6r1"></div>
<div id="c0r0"></div>
<div id="c1r0"></div>
<div id="c2r0"></div>
<div id="c3r0"></div>
<div id="c4r0"></div>
<div id="c5r0"></div>
<div id="c6r0"></div>
</section>
</body> </body>
</html> </html>

View file

@ -1,132 +1,83 @@
/*----- constants -----*/ // global constants declared
const COLORS = {
'0': 'white',
'1': 'purple',
'-1': 'lime'
};
/*----- app's state (variables) -----*/ // boardState, lastState, overlay (copy of boardState holding pre-move object, game end group state)
let board, turn, winner; // bCaptures, wCaptures, gameInfo object, playerInfo object, turn, pass, gameRecord,
// handicapStones, deadShapes{}
/*----- cached element references -----*/
const msgEl = document.getElementById('msg');
/*----- event listeners -----*/ // define initial game state
document.querySelector('section.markers')
.addEventListener('click', handleClick);
/*----- functions -----*/ // define boardState and overlay as 2d 9x9 arrays
init(); // boardState accepts values of 0, 1, -1
// overlay accepts values of 0, 1, -1, 'k', 'd', 'chk', 'hold', 'l', 'x'
// '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,
function init() {
board = [
[0, 0, 0, 0, 0, 0], // column 1 (index 0)
[0, 0, 0, 0, 0, 0], // column 2 (index 1)
[0, 0, 0, 0, 0, 0], // column 3 (index 2)
[0, 0, 0, 0, 0, 0], // column 4 (index 3)
[0, 0, 0, 0, 0, 0], // column 5 (index 4)
[0, 0, 0, 0, 0, 0], // column 6 (index 5)
[0, 0, 0, 0, 0, 0], // column 7 (index 6)
];
turn = 1;
winner = null; // 1, -1, null (no winner), 'T' (tie)
render();
}
function render() { // cached elements
// Render the board // store #menu for displaying game info
board.forEach(function(colArr, colIdx) { // store
// hide/show the column's marker depending if there are 0's or not
let marker = document.getElementById(`col${colIdx}`);
// <conditional exp> ? <truthy thing to return> : <falsey thing to return>;
// This is a ternary expression that replaces the if/else below it.
marker.style.visibility = colArr.indexOf(0) === -1 ? 'hidden' : 'visible';
// if (colArr.indexOf(0) === -1) {
// marker.style.visibility = 'hidden';
// } else {
// marker.style.visibility = 'visible';
// }
colArr.forEach(function(cell, rowIdx) {
let div = document.getElementById(`c${colIdx}r${rowIdx}`);
div.style.backgroundColor = COLORS[cell];
});
});
// Render the message
if (winner) {
if (winner === 'T') {
msgEl.textContent = "It's a Tie!";
} else {
msgEl.innerHTML = `<span style="color:${COLORS[winner]}">${COLORS[winner].toUpperCase()}</span> Wins!`;
}
} else {
msgEl.innerHTML = `<span style="color:${COLORS[turn]}">${COLORS[turn].toUpperCase()}</span>'s Turn`;
}
}
function handleClick(evt) {
// get index of column's marker clicked
let idx = parseInt(evt.target.id.replace('col', ''));
// make sure the MARKER was clicked
if (isNaN(idx) || winner) return;
// obtain the actual column array in board array
let colArr = board[idx];
// get the index of the first 0 in the col array
let rowIdx = colArr.indexOf(0);
// if the col is full, there are no zeroes, therefore
// indexOf returns -1.
// Do nothing if no zeroes available (col full)
if (rowIdx === -1) return;
// update the col array (within the board) with
// the player whose turn it is
colArr[rowIdx] = turn;
// flip turns from 1 to -1; -1 to 1
turn *= -1;
// update the winner
winner = getWinner();
render();
}
function getWinner() { // set event listeners
// return the winner, 'T' or null // input listeners for player names, ranks, rank certainty (editable during game)
let winner = null; //input lister for handicap + komi (only editable pre-game)
// using a for loop because we want to stop looping if we find a winner // ::hover-over on board to preview move (with legal move logic)
for (let colIdx = 0; colIdx < board.length; colIdx++) { // click on board to play move
// check if any cells in the col lead to a winner // ::hover-over on either bowl for pass, one-level undo options (CSS implementation)
winner = checkCol(colIdx); // click on menu items
// done if winner is found, no reason to keep looking // click on kifu to display game menu
if (winner) break;
}
return winner;
}
function checkCol(colIdx) { // functions
let winner = null; // initialize game
for (let rowIdx = 0; rowIdx < board[colIdx].length; rowIdx++) { // set handicap stones
// using the logical OR operator (||) prevents the checks to the right // render
// from ever running if a winner is found. For example, if checkUp returns // render board
// a truthy value, checkRight and the checkDiag will never be called //render moves
winner = checkUp(colIdx, rowIdx) || checkRight(colIdx, rowIdx) || checkDiag(colIdx, rowIdx, 1) || checkDiag(colIdx, rowIdx, -1); //render preview
if (winner) break; // render captures
} // render player turn marker
return winner; // game-end
} // render dead group suggestion
// render territory counts
function checkUp(colIdx, rowIdx) { // checkLegalMove
// boundary check (can't check up if rowIdx is greater than 2) // clear overlay
if (rowIdx > 2) return null; // if move is not '0', move is illegal (opposing player or 'k' for ko)
const colArr = board[colIdx]; // iterate through neighboring points in clockwise order
// ternary expression deluxe! // if anyone is '0' move is legal - call render preview
return ( Math.abs(colArr[rowIdx] + colArr[rowIdx + 1] + colArr[rowIdx + 2] + colArr[rowIdx + 3]) === 4 ) ? colArr[rowIdx] : null; // 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
function checkRight(colIdx, rowIdx) { // if any neighboring point is '0' terminate cycle and move to next neighboring point of original move
if (colIdx > 3) return null; // if there are unchecked points of 'hold' return
return ( Math.abs(board[colIdx][rowIdx] + board[colIdx + 1][rowIdx] + board[colIdx + 2][rowIdx] + board[colIdx + 3][rowIdx]) === 4 ) ? board[colIdx][rowIdx] : null; // if no boardState: 0 points, move is legal overlay: 'l'
} // set all 'chk' to 'x' to represent stones that will be captured upon move
// if neighboring point is player's
// Notice the extra vertOffset parameter for determining whether checking up or down vertically // cycle through player group marking points as overlay: 'chk' || 'hold'
function checkDiag(colIdx, rowIdx, vertOffset) { // if any neighboring point is '0' ternminate cycle and mark point as 'l'
// lot's of boundaries to check // set move
if (colIdx > 3 || (vertOffset > 0 && rowIdx > 2) || (vertOffset < 0 && rowIdx < 3)) return null; // if checkLegalMove has returned '0' i2llegal move message?
return ( Math.abs(board[colIdx][rowIdx] + board[colIdx + 1][rowIdx + vertOffset] + board[colIdx + 2][rowIdx + (vertOffset * 2)] + board[colIdx + 3][rowIdx + (vertOffset * 3)]) === 4 ) ? board[colIdx][rowIdx] : null; // 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