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
## Connect Four
# Go
#### 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`
2. `git reset --hard origin/master`
stretch goals
* 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
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**.
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
<!-- 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;
}
body {
font-family: Montserrat;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
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;
.full-screen {
/* positioning will be absolue */
/* will take up whole screen */
/* background will be ~0.5 opacity */
/* grid-areas
"game-info *4" <-date, komi, handicap
"b-player-info*2 w-player-info*2" <- name, rank, rank-certainty
"record record record options" <- displays numbered record (stretch!), new game, get game record(stretch!)
pre-game record will display instructions
*/
}

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

@ -4,71 +4,27 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Connect Four</title>
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">
<link rel="stylesheet" href="css/main.css">
<script defer src="js/main.js"></script>
<link rel="stylesheet" href="css/reset.css" type="stylesheet">
<link rel="stylesheet" href="css/main.css" type="stylesheet">
<script src="js/main.js"></script>
<title>Go Cats</title>
</head>
<body>
<h1>CONNECT FOUR</h1>
<h2 id="msg"></h2>
<section class="markers">
<div id="col0"></div>
<div id="col1"></div>
<div id="col2"></div>
<div id="col3"></div>
<div id="col4"></div>
<div id="col5"></div>
<div id="col6"></div>
</section>
<section class="board">
<div id="c0r5"></div>
<div id="c1r5"></div>
<div id="c2r5"></div>
<div id="c3r5"></div>
<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>
<div id="menu" class="full-screen">
<!-- for displaying new game screen and status during game -->
</div>
<!-- may need add'l .full-screen or other div for board overlay -->
<content>
<div id="white-pos" class="player-pos">
<div id="white-bowl" class="bowl"></div>
<div id="white-caps" class="caps"></div>
</div>
<div id="board"></div>
<div id="black-pos" class="player-pos">
<div id="black-bowl" class="bowl"></div>
<div id="black-caps" class="caps"></div>
<div id="kifu"></div>
</div>
</content>
</body>
</html>

View file

@ -1,132 +1,83 @@
/*----- constants -----*/
const COLORS = {
'0': 'white',
'1': 'purple',
'-1': 'lime'
};
// global constants declared
/*----- app's state (variables) -----*/
let board, turn, winner;
// boardState, lastState, overlay (copy of boardState holding pre-move object, game end group state)
// bCaptures, wCaptures, gameInfo object, playerInfo object, turn, pass, gameRecord,
// handicapStones, deadShapes{}
/*----- cached element references -----*/
const msgEl = document.getElementById('msg');
/*----- event listeners -----*/
document.querySelector('section.markers')
.addEventListener('click', handleClick);
// define initial game state
/*----- functions -----*/
init();
// define boardState and overlay as 2d 9x9 arrays
// 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() {
// Render the board
board.forEach(function(colArr, colIdx) {
// 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`;
}
}
// cached elements
// store #menu for displaying game info
// store
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() {
// return the winner, 'T' or null
let winner = null;
// using a for loop because we want to stop looping if we find a winner
for (let colIdx = 0; colIdx < board.length; colIdx++) {
// check if any cells in the col lead to a winner
winner = checkCol(colIdx);
// done if winner is found, no reason to keep looking
if (winner) break;
}
return winner;
}
// set 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)
// click on board to play move
// ::hover-over on either bowl for pass, one-level undo options (CSS implementation)
// click on menu items
// click on kifu to display game menu
function checkCol(colIdx) {
let winner = null;
for (let rowIdx = 0; rowIdx < board[colIdx].length; rowIdx++) {
// using the logical OR operator (||) prevents the checks to the right
// from ever running if a winner is found. For example, if checkUp returns
// a truthy value, checkRight and the checkDiag will never be called
winner = checkUp(colIdx, rowIdx) || checkRight(colIdx, rowIdx) || checkDiag(colIdx, rowIdx, 1) || checkDiag(colIdx, rowIdx, -1);
if (winner) break;
}
return winner;
}
function checkUp(colIdx, rowIdx) {
// boundary check (can't check up if rowIdx is greater than 2)
if (rowIdx > 2) return null;
const colArr = board[colIdx];
// ternary expression deluxe!
return ( Math.abs(colArr[rowIdx] + colArr[rowIdx + 1] + colArr[rowIdx + 2] + colArr[rowIdx + 3]) === 4 ) ? colArr[rowIdx] : null;
}
function checkRight(colIdx, rowIdx) {
if (colIdx > 3) return null;
return ( Math.abs(board[colIdx][rowIdx] + board[colIdx + 1][rowIdx] + board[colIdx + 2][rowIdx] + board[colIdx + 3][rowIdx]) === 4 ) ? board[colIdx][rowIdx] : null;
}
// Notice the extra vertOffset parameter for determining whether checking up or down vertically
function checkDiag(colIdx, rowIdx, vertOffset) {
// lot's of boundaries to check
if (colIdx > 3 || (vertOffset > 0 && rowIdx > 2) || (vertOffset < 0 && rowIdx < 3)) return null;
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;
}
// functions
// initialize game
// set handicap stones
// render
// render board
//render moves
//render preview
// render captures
// render player turn marker
// game-end
// render dead group suggestion
// 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
// 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