HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<h1>··I··C··D··O··T··S··</h1>
<h2>– – and – – lines – –</h2>
<!-- Development history details at bottom of HTML code. -->
<p class="description">This game is for 2-7 players who will take turns selecting lines to form boxes. Scoring is 1 point per box pinned. Line colors have no distinction other than one is horizontal and one is vertical, and the alternating colors felt visually appealing.</p>
<form name="icdots">
<div class="row">
<label><span class="label-text">board size:</span>
<input type="number"
min="2" max="7" value="3"
class="input-size board-size"
title="board size can be between 2 and 7, inclusive." />
</label><br/>
</div>
<div class="row">
<label><span class="label-text">players <small>(names optional)</small>:</span></label>
<div class="buttons-up-down">
<input type="button" class="players-count players-count-down" value="-" />
<input type="button" class="players-count players-count-up" value="+" />
</div>
<div class="tooltip-container">
<tooltip class="tooltip tooltip-small">
<span class="tooltip-open">?</span>
<span class="tooltip-close">X</span>
<span class="tooltip-content">
number of players should not exceed the board's size.
board size can be between 2 and 7, inclusive.
</span>
</tooltip>
</div>
</div>
<div class="row">
<div class="player-names-list">
<input type="text" class="player-names" />
<input type="text" class="player-names" />
</div>
</div>
<div class="row">
<div class="start-it-container">
<button type="button" class="start-it-button" title="Click to Begin Game">Begin Game</button>
</div>
</div>
</form>
<aside>
<p>My interactive version of a game inspired by a Codewars kata</p>
<ul>
<li>
My 'React with TypeScript' Version of this same game:<br/>
<a href="https://codepen.io/KeithDC/details/OzdoLv/">https://codepen.io/KeithDC/details/OzdoLv/</a>
</li>
<li>
Original Codewars Kata:<br/>
<a href="https://www.codewars.com/kata/58fdcc51b4f81a0b1e00003e/train/javascript">Pigs in a Pen (dots-connect-lines game)</a>
</li>
<li>
Wikipedia (Game: Dots and Boxes):<br/>
<a href="https://en.wikipedia.org/wiki/Dots_and_Boxes">https://en.wikipedia.org/wiki/Dots_and_Boxes</a>
</li>
</ul>
<h3>@DONE: (2018-01)</h3>
<ul>
<li><a href="https://codepen.io/KeithDC/pen/OzdoLv/">Forked this Pen and completed 'React with TypeScript' version</a>.</li>
</ul>
<h3>Development Time</h3>
<p>
Conversion from 'kata style' to 'interactive style'.<br/>
[2018-01-13 - Saturday] --> [2018-01-21 - Sunday]
</p>
</aside>
<div class="game-board-mask"></div>
<div class="game-board">
<div class="welcome">Pick a line (gap)... Any line (gap)!</div>
<div class="main-board"></div>
<div class="main-board-stats"></div>
<div class="main-board-footer">
<div class="footer-left">
<label>Board Background:
<select class="main-board-background">
<option value="rgba(50,205,205,.5)">default</option>
<option value="aqua">aqua</option>
<option value="aquamarine">aquamarine</option>
<option value="black">black</option>
<option value="chartreuse">chartreuse</option>
<option value="darkblue">dark blue</option>
<option value="darkgreen">dark green</option>
<option value="darkred">dark red</option>
<option value="lightskyblue">light sky blue</option>
<option value="lightgreen">light green</option>
<option value="lime">lime</option>
<option value="limegreen">lime green</option>
<option value="midnightblue">midnight blue</option>
<option value="mistyrose">misty rose</option>
<option value="palegreen">pale green</option>
<option value="purple">purple</option>
<option value="skyblue">sky blue</option>
<option value="tan">tan</option>
<option value="turquoise">turquoise</option>
<option value="wheat">wheat</option>
</select>
</label>
</div>
<div class="footer-right">
<button class="main-board-close" type="button" title="Exit Game">Exit Game</button>
</div>
</div>
</div>
<svg class="hidden" width="50" height="50"><circle cx="4" cy="25" r="4" stroke="green" stroke-width="0" fill="yellow"></circle><circle cx="46" cy="25" r="4" stroke="green" stroke-width="0" fill="yellow"></circle><line x1="8" y1="25" x2="42" y2="25" style="stroke:rgb(255,0,0); stroke-width:2;" stroke-dasharray="5,5"></line></svg>
<!--
// History
2018-01-11 - Thursday
@6:40 PM - Codewars -> The Line Game (Box 'em In)
@8:15 PM - Back to looking for a better online Coding Notebook.
@12:30 AM
- Coding Notes have finally found a home!
- RightNote -> Export to Webbook -> Copy to kdcinfo.com (public)
@4:55 AM - Codewars Kata - Game: Dots and Boxes
- Still trying to figure out the calculations to get each 4 box outlines.
Once I figure out the calculation for each quadrant,
Just check to see if there are 3 of them in the list; if so, add 4th.
- I "think" I'm gonna have to do a recursive function;
On each pass to see if any boxes have 3 walls,
filling one in could close in one before or above it.
Just keep a track if any new lines were added to linesPlayed; if not, no more loopback.
// [2018-01-17] Completed algorithms and braided player progression (2 and 3 players).
// -This may also play with one person...? Albeit would be nice to add in a simulator.
2018-01-16 - Tuesday
The final hours; Started about 30% in
(from what I did and could use from my Codewars kata submission).
@10:00 PM - 3 hours
- Figured out the algorithms (pattern) for finding the boxes from line selections.
- Well, it's about 90-95% proofed out, but I can visualize the rest,
so it's just academic (or would that be, fundamental(s)?).
@11:05 PM - 1 hour
- Started back on Dots and Boxes.
- Just looked around and noticed I hadn't put my mouse back...
Guess (direct) coding doesn't take a lot of mouse work.
Direct* = Not having to look stuff up and test stuff out. Just coding.
@11:45 PM
- Ready to begin again...
@12:35 AM - 0.75 hour
- Finished transpiling my Open Office Calc data into JavaScript variables:
@2:30 AM - 2 hours
- Making good (notable) progress. :)
- Got it mostly working.
Moving up the tested line numbers; up to 3 and it doesn't look right.
boxRightNum should be 1, not 3 (when line picked is 3 on a 2x2 box grid).
@5:55 AM - 3.50 hours
- Think I've got it. "Think", being the operative word here.
- Adjusted each cardinal; had to split 'bottom' check/calc.
- Next issue: No errors thrown on my using `array[colName]` instead of `array['colName']`.
- Found because it was breaking the loop when multiple boxes were found.
@7:40 AM - 1.75 hours
- Next issue -- Gotta score and adjust move.
Then end/start game.
Then include names.
Issue is scoring happens each turn; not just on box pin.
@8:55 AM - 1.25 hours
- Scoring stuff done.
- Tested 2 and 3 players with 2 filled boxes.
= 13.25 hours
=== ~20-24 hours
[2018-01-17] - Let's make it playable!
@7:20 PM
- Failing silently (or Codepen showing error at end of file):
// Last time was my not quoting object keys. This time was because
// I didn't close the multiline comment at the end of the file ( `* /` )
[2018-01-18] - Let's create the board!
- Finished with all 'preChecks'. We have the data to initialize a board.
- Can now actually create a game... Now we just need a board to play on.
Game.prototype.createBoard = function() {
// We have our game instance and all its info regarding board size and players.
// How to draw the board...?
}
[2018-01-19] - Board is coming along nicely!
- Created SVG's for dot connectors (hor and ver hotspots).
- Got all nodes laid out.
- Got SVGs lined up nicely.
- Formatted player names
- Got setup for player turns with 'active' class.
- Added scores and prepped for dynamic updates.
- Got Hor and Ver event handlers working.
- To pass the index, I added a CSS class name to each node: node-n
- To execute the listener I call:
addEventListener('click', this.play.bind(this), false)
- @2:00 AM - Fixed: Issue with Hor and Ver hotspot overlaps.
Had to refigure the HTML / CSS (aligned the dot squares over them.)
- Updated 'open' SVGs to have gaps. Added yellow highlight on hover.
[2018-01-20] - Game play is completed. Ready for play.
- Integrated plays with board.
- Open vs Clicked lines.
- Player scores.
- Player numbers in pinned boxes.
- Change 'active' class when player turn changes.
- Allow player to change game board background color.
- Technically completion was [2018-01-21] @ ~2:30 AM PST
[2018-01-21]
- Forked Pen and started on 'React with TypeScript' version.
https://codepen.io/KeithDC/pen/OzdoLv/
ETA: [2018-01-26]
@TODO:
-
-->
body {
position: relative;
}
h1,
h2 {
text-align: center;
}
h1 {
margin: 1em auto 0.25em;
}
h2 {
color: darkgray;
font-size: 14px;
font-weight: normal;
margin: 0 auto 1.5em;
}
h3 {
margin: 0.5em auto;
}
p.description {
display: block;
font-size: 12px;
line-height: 1.4;
margin: 0 auto 1rem;
max-width: 434px;
outline: 0px solid green;
padding: 0;
position: relative;
}
.row {
position: relative;
}
aside {
font-size: 12px;
line-height: 1.4;
margin: 0 auto;
outline: 1px solid lightblue;
padding: 1em;
max-width: 400px;
}
aside li {
border-bottom: 1px dotted lightgray;
margin: 5px 0;
width: 95%;
}
form[name="icdots"] {
border: 3px solid lightblue;
border-radius: 2px;
box-shadow: 3px 3px green;
margin: 0 auto 15px;
padding: 15px;
min-width: 250px;
max-width: 400px;
width: 80%;
}
label {
display: inline-block;
font-size: 14px;
position: relative;
}
label .label-text {
display: inline-block;
width: 145px;
}
label .input-size {
padding-left: 5px;
width: 35px;
}
.start-it-container {
margin: 0.75em auto 0;
text-align: center;
}
.start-it-button {
background-color: #ddeeff;
cursor: pointer;
}
.buttons-up-down {
display: inline-block;
height: 30px;
outline: 0px solid red;
padding: 10px 0 0;
}
.players-count {
background-color: #ddeeff;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
outline: 0px solid red;
padding: 0;
width: 20px;
}
.tooltip-container {
display: inline-block;
outline: 0px solid blue;
position: relative;
}
tooltip {
display: inline-block;
margin: 0 0 0 2px;
position: absolute;
left: 0px;
top: -30px;
z-index: 0;
}
.tooltip-small {
border: 1px solid rgba(200,75,25,0.75);
border-radius: 5px;
color: rgba(200,75,25,0.75);
cursor: pointer;
display: inline-block;
font-size: 14px;
height: 15px;
line-height: 1.1;
opacity: 0.8;
padding: 1px;
text-align: center;
transition: all 0.1s ease-in;
width: 15px;
}
.tooltip-big {
background-color: #ffffff;
border: 2px dashed rgba(150,0,0,0.5);
border-radius: 7px;
font-size: 14px;
opacity: 1;
padding: 3px;
top: -50px;
transition: opacity 0.1s, border 2s ease-in;
}
.tooltip-small .tooltip-open,
.tooltip-big .tooltip-close {
background-color: #ffffff;
color: rgba(200,75,25,0.75);
cursor: pointer;
font-size: 14px;
height: 15px;
text-align: center;
text-transform: lowercase;
width: 15px;
}
.tooltip-small .tooltip-open {
border: 0px solid rgba(200,75,25,0.75);
border-radius: 5px;
display: inline-block;
line-height: 1.1;
padding: 0;
}
.tooltip-big .tooltip-close {
border-left: 1px solid maroon;
border-bottom: 1px solid maroon;
border-radius: 2px;
font-family: monospace;
line-height: 1.0;
position: absolute;
top: 1px;
right: 1px;
z-index: 3;
}
.tooltip-small .tooltip-close {
display: none;
}
.tooltip-big .tooltip-open {
display: none;
}
.tooltip-small .tooltip-content {
opacity: 0;
}
.tooltip-big .tooltip-content {
border: 2px dashed rgba(150,0,0,0);
opacity: 1;
transition: opacity 1s, opacity 0.25s ease-in;
}
.player-names-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.player-names {
display: inline-block;
margin: 5px auto 0;
position: relative;
z-index: 1;
width: 100px;
}
/* Utility Classes */
.hide,
.hidden {
display: none;
}
code,
pre,
main,
textarea {
border: 1px solid green;
display: inline-block;
font-size: 12px;
line-height: 1.8;
min-width: 400px;
width: 95%;
}
.bold {
font-weight: bold;
}
/* Responsive */
@media (max-width: 495px) {
.tooltip-big {
width: 155px;
}
}
@media (max-width: 470px) {
.tooltip-big {
width: 130px;
}
}
@media (max-width: 435px) {
.tooltip-big {
width: 105px;
}
}
@media (max-width: 400px) {
.tooltip-big {
width: 80px;
}
}
@media (min-width: 496px) {
.tooltip-big {
width: 180px;
}
}
.game-board-mask {
background-color: rgba(235, 245, 235, 0.9);
/* background-color: rgba(0, 50, 50, 0.1); */
display: none;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 99;
width: 100%;
}
.game-board {
background-color: #ffffff;
border: 2px solid rgba(0,150,50,1);
border-radius: 4px;
display: none;
min-height: 300px;
min-width: 300px;
padding: 1em 2em;
position: fixed;
top: 50%;
left: 50%;
z-index: 100;
text-align: center;
transform: translate(-50%, -50%);
}
.welcome {
color: darkgreen;
font-size: 16px;
font-weight: bold;
margin: 1em 0.5em;
}
.main-board {
background-color: rgba(50,205,205,.5);
/* background-color: rgba(200, 225, 255, 0.75); */
border: 3px solid lightgray;
border-radius: 10px;
display: flex;
flex-wrap: wrap;
font-family: monospace;
margin: 1em auto;
min-height: 200px;
min-width: 200px;
position: relative;
width: 225px; /*2=225;5=495;7=675px;*/
}
.mb-node {
color: green;
display: inline-block;
font-size: 2em;
height: 40px;
line-height: 1.5;
vertical-align: middle;
width: 45px;
}
.mb-lh {
position: relative;
outline: 0px dotted rgba(55,55,225,.5);
}
.mb-lv {
/* content: '\2506'; */ /*2506 2195 2502*/
outline: 0px dashed rgba(255,55,25,.5);
position: relative;
}
.mb-lh.open:hover,
.mb-lv.open:hover {
cursor: pointer;
outline: 1px solid yellow;
}
.div-svgH {
background-repeat: no-repeat;
height: 40px;
/* pointer-events: none; */
position: absolute;
top: 1px;
left: -27px;
outline: 0px dotted rgba(55,55,225,.5);
width: 97px;
}
.div-svgV {
background-repeat: no-repeat;
height: 97px;
/* pointer-events: none; */
position: absolute;
top: -23px;
left: 2px;
outline: 0px dotted rgba(55,55,225,.5);
width: 40px;
}
.mb-dot {
cursor: default;
outline: 0px solid green;
position: relative;
z-index: 110;
}
.mb-dot::after {
content: '\2022'; /*25c9*/
opacity: 0.1;
}
.mb-space {
/* content: ' '; */
}
.mb-play {
/* content: '1'; */
}
.main-board-stats {
font-size: 12px;
}
.main-board-stats div {
border: 2px solid rgba(0,150,50,1);
border-radius: 10px;
border-top: 0;
margin: 0.5em auto;
padding: 0.25em 0.75em;
}
.main-board-stats span {
border: 1px solid lightblue;
display: inline-block;
margin: 0.5em;
padding: 0.5em 0.5em 0.5em 1.0em;
position: relative;
white-space: nowrap;
}
.main-board-stats span playernum {
color: darkgray;
font-family: monospace;
font-size: 12px;
/* outline: 1px solid red; */
position: absolute;
left: 0;
top: 0;
}
.main-board-stats span.active {
border: 3px solid lightgreen;
}
.main-board-stats span.active playernum {
color: red;
font-weight: bold;
}
.main-board-close {
/*background-color: rgba(200, 225, 255, 0.75); */
background-color: #ddeeff;
cursor: pointer;
}
.main-board-footer {
margin: 0;
}
.main-board-footer .footer-left,
.main-board-footer .footer-right {
display: inline-block;
outline: 0px solid blue;
padding: 0.5em 0;
vertical-align: bottom;
width: 47%;
}
.main-board-footer .footer-left select {
margin: 10px 0 0;
padding: 3px;
}
;console.clear();
/* My interactive version of a game inspired by a Codewars kata.
// Wikipedia: https://en.wikipedia.org/wiki/Dots_and_Boxes
The Game:
1. Board is between 2 and 7 (represented as 1 row of boxes).
2. Number of players is between 2 and [Board] size.
3. Players take turns.
4. If a player fills in a box, they go again.
*/
// @TODO: Need better State Mgmt than global vars
// Perhaps localStorage. May do in React version.
let letsPlay1 = {},
SVGLib = {};
// SVG injection thanks to: https://stackoverflow.com/a/21626701/638153
const mySVGHClicked = "<svg xmlns='http://www.w3.org/2000/svg' width='98' height='40'><circle cx='4' cy='20' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><circle cx='94' cy='20' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><line x1='8' y1='20' x2='90' y2='20' style='stroke:rgb(255,0,0); stroke-width:1;'></line></svg>";
SVGLib['mySVGHClicked64'] = window.btoa(mySVGHClicked);
const mySVGVClicked = "<svg xmlns='http://www.w3.org/2000/svg' width='40' height='90'><circle cx='20' cy='4' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><circle cx='20' cy='94' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><line x1='20' y1='8' x2='20' y2='80' style='stroke:rgb(0,0,255); stroke-width:1;'></line></svg>";
SVGLib['mySVGVClicked64'] = window.btoa(mySVGVClicked);
const mySVGHOpen = "<svg xmlns='http://www.w3.org/2000/svg' width='98' height='40'><circle cx='4' cy='20' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><circle cx='94' cy='20' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><line x1='8' y1='20' x2='28' y2='20' style='stroke:rgb(255,100,100); stroke-width:1;' stroke-dasharray='3,3'></line><line x1='70' y1='20' x2='90' y2='20' style='stroke:rgb(255,100,100); stroke-width:1;' stroke-dasharray='3,3'></line></svg>";
SVGLib['mySVGHOpen64'] = window.btoa(mySVGHOpen);
const mySVGVOpen = "<svg xmlns='http://www.w3.org/2000/svg' width='40' height='90'><circle cx='20' cy='4' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><circle cx='20' cy='94' r='4' stroke='green' stroke-width='0' fill='yellow'></circle><line x1='20' y1='8' x2='20' y2='28' style='stroke:rgb(100,100,255); stroke-width:1;' stroke-dasharray='3,3'></line><line x1='20' y1='60' x2='20' y2='80' style='stroke:rgb(100,100,255); stroke-width:1;' stroke-dasharray='3,3'></line></svg>";
SVGLib['mySVGVOpen64'] = window.btoa(mySVGVOpen);
function Game(board, playerNames) {
this.boardSize = this.isGoodType(board, Number) ? board : 2; // 2 - 7
this.playerNames = this.isGoodType(playerNames, Array, String) ? playerNames : [];
this.numOfPlayers = this.playerNames.length; // 2 - [board]
this.numOfBoxes = 1 << this.boardSize; // A.k.a., Math.pow(2, this.boardSize);
this.linesPlayed = [];
this.boxesPinned = [];
this.numOfLines = (4 + ((this.boardSize-1)*3)) + ((this.boardSize-1) * (3 + ((this.boardSize-1)*2)));
this.allBoxNodes = ((this.boardSize * 2) + 1) * ((this.boardSize * 2) + 1);
this.players = Array.from(Array(this.numOfPlayers)).map( e => 0);
this.curPlayer = 1;
this.curLine = 0;
this.curLineDir = '';
// this.playListener = ()=>{};
this.play = this.play.bind(this);
}
Game.prototype.createBoard = function() {
const ourBoard = document.querySelector('.game-board'),
mainBoard = ourBoard.querySelector('.main-board');
// PUT PLAYER NAMES ON BOARD
let displayPlayerNames = '';
this.playerNames.forEach( (n,i) => {
if (i === 0) {
displayPlayerNames += '<span class="pname-' + i + ' active">' +
'<playernum>' + (i + 1) + '</playernum><b>' + n + '</b> [ <score>0</score> ]</span> ';
} else {
displayPlayerNames += '<span class="pname-' + i + '">' +
'<playernum>' + (i + 1) + '</playernum><b>' + n + '</b> [ <score>0</score> ]</span> ';
}
});
ourBoard.querySelector('.main-board-stats').innerHTML = "<div>" + displayPlayerNames + "</div>";
// console.log('mainBoard 1: ', mainBoard, mainBoard.children[0]);
// console.log('mainBoard 2: ', mainBoard.children.length, mainBoard.children);
// CLEAR ALL BOARD ELEMENTS IF SET PREVIOUSLY
while (mainBoard.children[0]) {
mainBoard.removeChild(mainBoard.children[0]);
}
mainBoard.style.width = (45 * ((this.boardSize * 2) + 1)).toString() + 'px';
ourBoard.previousElementSibling.style.display = 'block';
ourBoard.style.display = 'block';
// We have our game instance and all its info regarding board size and players.
// How to draw the board...?
// Decided to go with a simple custom modal - 2-DIV approach; 1 mask.
// Types of lines:
// Light dashed - Clickable
// Solid - Clicked
var createDiv = (idx, cssClass, clickLine) => {
// This function is called from (right below) within a loop of "allBoxNodes".
// `clickLine` is only counted and passed with horizontal and vertical clickable lines.
let newElementOut = document.createElement('div'),
newElementIn = document.createElement('div');
// var tmpPlay = this.play.bind(this);
// this.playListener = tmpPlay;
if (cssClass.indexOf('lh') > 0) {
newElementIn.setAttribute('class', 'div-svgH node-' + clickLine);
newElementIn.style.backgroundImage = "url('data:image/svg+xml;base64," + SVGLib['mySVGHOpen64'] + "')";
// newElementIn.addEventListener('click', e => {
// console.log('clicked: ', clickLine); this.play(clickLine); });
newElementOut.addEventListener('click', this.play); // console.log('clicked: ', e); // playListener
}
if (cssClass.indexOf('lv') > 0) {
newElementIn.setAttribute('class', 'div-svgV node-' + clickLine);
newElementIn.style.backgroundImage = "url('data:image/svg+xml;base64," + SVGLib['mySVGVOpen64'] + "')";
// newElementIn.addEventListener('click', e => {
// console.log('clicked: ', clickLine); this.play(clickLine); });
newElementOut.addEventListener('click', this.play); // console.log('clicked: ', e); // playListener
}
newElementOut.setAttribute('class', cssClass);
newElementOut.appendChild(newElementIn);
return newElementOut;
}
let gridRow = 0,
gridCol = 0,
curClickNode = 1;
for (let i = 0; i < this.allBoxNodes; i++) {
if (gridRow % 2 === 0 && gridCol % 2 === 0) {
mainBoard.appendChild(createDiv(i, 'mb-node mb-dot'));
} else if (gridRow % 2 === 0 && gridCol % 2 !== 0) {
mainBoard.appendChild(createDiv(i, 'mb-node mb-lh open', curClickNode)); // .play() is 1-based
curClickNode++;
} else if (gridRow % 2 !== 0 && gridCol % 2 === 0) {
mainBoard.appendChild(createDiv(i, 'mb-node mb-lv open', curClickNode));
curClickNode++;
} else if (gridRow % 2 !== 0 && gridCol % 2 !== 0) {
mainBoard.appendChild(createDiv(i, 'mb-node mb-space'));
} else if (false) {
mainBoard.appendChild(createDiv(i, 'mb-node mb-play'));
}
gridCol = ((i > 0) && ((i+1) % ((this.boardSize * 2) + 1) === 0)) ? 0 : gridCol + 1;
gridRow = (gridCol === 0) ? gridRow + 1 : gridRow;
}
}
Game.prototype.removeBoard = function() {
const ourBoard = document.querySelector('.game-board');
ourBoard.querySelector('.main-board')
ourBoard.querySelector('.main-board-stats').innerHTML = "";
ourBoard.previousElementSibling.style.display = 'none';
ourBoard.style.display = 'none';
}
Game.prototype.isGoodType = function(param, type, subtype) {
if (param.constructor === type && type === Array) {
for (let i = 0; i < param.length; i++) {
// Check elements inside Array for proper subtypes.
if (param[i].constructor !== subtype) {
throw new Error('Array subtype failed: [' + i + '] ' +
param + ' -- ' +
param[i].constructor + ' !== ' + subtype);
return false;
}
}
}
// Ensure the element's type is as expected.
if (param.constructor === type) {
return true;
} else {
throw new Error('Input param type failed: ' + param.constructor + ' !== ' + type);
return false;
}
}
Game.prototype.play = function(e) {
// See if play is good or not
// If good, run check for sides === 4 (successful pins)
// All 4-sided boxes that do not exist in [boxesPinned]
// If found, allow to play again.
// Did my first 'purposeful' recursive function
// const linePlay = e; // MouseEvent(.target)
// const linePlay = parseInt(Array.from(e.target.classList).filter(e=>e.indexOf('node-')===0)[0].split('-')[1], 10);
const classListArr = Array.from(e.target.classList),
classListNode = classListArr.filter(e=>e.indexOf('node-')===0),
lineNum = classListNode[0].split('-')[1],
linePlay = parseInt(lineNum, 10),
classListDir = classListArr.filter(e=>e.indexOf('div-svg')===0),
nodeDir = classListDir[0].split('svg')[1];
// classListArr // ["div-svgH", "node-9"]
// classListNode // ["node-9"]
// lineNum typeof // "9" "string"
// linePlay typeof // 9 "number"
// classListDir // ["div-svgH"]
// nodeDir // "H"
// this.boardSize // 3
// this.curPlayer // 1
// linePlay // 9
this.gotABoxArr = []; // If non-empty, player pinned a box; Let player go again.
// Check the play
if (!this.isGoodType(linePlay, Number)) {
// Cannot 'assume' value provided was indeed a number.
throw new Error("Not a number? Not a Number!!");
} else if (linePlay >= 1 && linePlay <= this.numOfLines && this.linesPlayed.indexOf(linePlay) === -1) {
// GOOD PLAY: Good check directly after Number check and before others; Good checks come before bad.
this.setLinePlayed(linePlay); // linesPlayed[] used in: checkIfBoxed(), and here in play()
this.setCurLine(linePlay); // Line in Play: Let's set the play line so other methods can access it.
this.setCurLineDir(nodeDir); // Line in Play's pointing direction: "H" | "V"
this.tryPlay();
} else if (this.linesPlayed.indexOf(linePlay) >= 0) {
// already played (a.k.a., "How'd you do that?")
throw new Error("How'd you do that? (...The UI should've disabled that option.)");
} else if (linePlay < 0 || linePlay > this.numOfLines) {
// play out of bounds (again with the, "How'd you do that?")
throw new Error("Intruder alert! (...and, How'd you accomplish an out-of-bounds?)");
} else {
// No play (a.k.a., What else is there?)
throw new Error("Wait... what happened? What'd you do?");
}
if (this.linesPlayed.length === this.numOfLines) {
alert('Good game!');
// Disable board // Enable 'Play Again' button
} else if (this.gotABoxArr.length === 0) { // If no boxes were filled; move to next player.
this.changePlayer();
}
// console.log('Player\'s Turn: ', this.curPlayer);
// console.log('Current Score: ', this.players);
// console.log('boxesPinned: ', this.boxesPinned);
// Scoreboard should automagically already be auto-updated with new stats
// Wait, that's a SPA. This is JavaScript. Can JavaScript do declarative?
}
Game.prototype.setLinePlayed = function(newLinePlayed) {
const expected = Number;
if (newLinePlayed.constructor === expected) {
this.linesPlayed.push(newLinePlayed);
// Does .push() indicate a mutation of the var? Should I make a copy, push, and assign?
// Although this method is supposed to act as a 'setter', so not sure how that works.
} else {
console.log('Error: Expected: ' + expected + ', Got: ', newLinePlayed.constructor)
}
}
Game.prototype.setCurLine = function(linePlay) {
this.curLine = linePlay; // The 'linePlay' param has already been vetted.
}
Game.prototype.setCurLineDir = function(linePlayDir) {
this.curLineDir = linePlayDir;
}
Game.prototype.tryPlay = function() {
const borderBoxes = this.getAdjacentBoxes(); // Array of boxes adjacent to selected line.
borderBoxes.forEach( boxNum => {
if (this.checkIfBoxed(boxNum)) { // Run 'boxed in' (pinned) check.
// If you're in here, you scored! Good job!!
this.gotABoxArr.push(boxNum); // Will capture if 1 or 2 boxes were pinned.
this.players[this.curPlayer-1]++; // Scores (boxes pinned per player)
this.boxesPinned.push({'box': boxNum, 'p': this.curPlayer });
// ^ Array of objects: [{'box': 1, 'p': 2 }]
// this.curPlayer // 1
// this.gotABoxArr // [1]
// this.boxesPinned // [{box: 1, p: 1}]
// this.players // [1, 0]
}
});
// SET DASHED LINE TO SOLID
this.updateCurLine();
// PUT PLAYER NUM IN PINNED SQUARE
this.updateBoxes();
// UPDATE PLAYER SCORES
this.updateScores();
}
Game.prototype.changePlayer = function() {
const playerNodes = document.querySelectorAll('.main-board-stats div span');
// Change CSS class name for `active` - (old) Current User
playerNodes[this.curPlayer - 1].classList.remove('active');
this.curPlayer = (this.curPlayer === this.numOfPlayers) ? 1 : this.curPlayer + 1;
// Change CSS class name for `active` - (new) Current User
playerNodes[this.curPlayer - 1].classList.add('active');
}
Game.prototype.updateScores = function() {
// Update all scores
//
let playerNodes = document.querySelectorAll('.main-board-stats div span'),
playerNodeNum = '0';
playerNodes.forEach( e => {
// Not trusting the `i` to be consistently in order,
// I'm instead grabbing each player's number from the node's class name.
playerNodeNum = e.className.match(/pname-(\d+)/)[1];
// playerNodeNum = e.classList.filter(e=>e.indexOf('pname-')===0)[0].split('-')[1],
// I couldn't see filtering each classList on each playerNode. Hopefully `.match()` is quicker.
e.getElementsByTagName('score')[0].innerHTML = this.players[playerNodeNum];
});
}
Game.prototype.updateBoxes = function() {
// Update boxes with player numbers
//
let boxesNode = document.querySelectorAll('.mb-space');
this.boxesPinned.forEach( (e, i) => {
boxesNode[e.box - 1].innerHTML = e.p;
});
}
Game.prototype.updateCurLine = function() {
/** SET DASHED LINE TO SOLID
* (and remove listener)
* (and remove cursor/outline styling (.open))
*/
let getSVG = document.querySelector('.node-' + this.curLine);
// console.log('playing: ', this.curLine, this.curLineDir, getSVG);
if (this.curLineDir.toUpperCase() === 'H') {
getSVG.style.backgroundImage = "url('data:image/svg+xml;base64," + SVGLib['mySVGHClicked64'] + "')";
getSVG.parentNode.classList.remove('open');
getSVG.parentNode.removeEventListener('click', this.play); // playListener
// console.log('getSVG.parentNode: ', getSVG.parentNode);
} else if (this.curLineDir.toUpperCase() === 'V') {
getSVG.style.backgroundImage = "url('data:image/svg+xml;base64," + SVGLib['mySVGVClicked64'] + "')";
getSVG.parentNode.classList.remove('open');
getSVG.parentNode.removeEventListener('click', this.play); // playListener
// console.log('getSVG.parentNode: ', getSVG.parentNode);
}
}
/* // 1st line
=(5 + (5 * 2) + 1) - (( (11 * 1) + 5) - D3)
=IF( (V4 >= 1) AND (V4 <= 5); (5 * R5) + V4; "False")
// 6th line (left of 1st box)
=(5 + (5 * 2) + 1) - (( (11 * 1) + 5) - C4)
=(T4 >= 5 + 1) AND (T4 <= 5 + 5)
=IF( (boxBaseLineNum >= 1) AND (boxBaseLineNum <= 5); (5 * R5) + boxBaseLineNum; "False")
*/
Game.prototype.getAdjacentBoxes = function() {
/**
* Get the box number to each side of the selected line.
*
* In the case of the first and last rows, there is always only 1 box below or above it.
*/
let boxedArray = [],
lastRowFirstLine = this.numOfLines - this.boardSize + 1,
lastRowFirstBox = (this.curLine >= lastRowFirstLine) ? (
(this.boardSize * this.boardSize) - (this.numOfLines - this.curLine)
) : 0;
if (this.curLine <= this.boardSize) { // First Row
boxedArray.push(this.curLine);
return boxedArray;
}
if (lastRowFirstBox > 0) { // Last Row
boxedArray.push(lastRowFirstBox);
return boxedArray;
}
const boxRowBase = (this.boardSize * 2) + this.boardSize + 1, // =(5+(5*2)+1) // Base box row size; 5-board = [16]
boxRowSize = (this.boardSize * 2) + 1, // =(5*2)+1 // Line count for each row; 5-board = [11]
boxRowNum = (this.curLIne <= boxRowBase) ? 0 : Math.ceil((this.curLine - this.boardSize) / boxRowSize) - 1,
// CEILING(C4/((5*2)+1);1) // 0, 1, ..., boardSize (each row)
boxBaseLineNum = boxRowBase - (( (boxRowSize * (boxRowNum + 1)) + this.boardSize) - this.curLine),
// Individual number 1-(16) [16]
// =( 5 + (5 * 2) + 1) - (( (11 * 1) + 5) - C4)
// =(5+(5*2)+1)-(((11*1)+5)-C4) // Each row, when being calc'd as a base, has 1-(16)
boxNumBase = (this.boardSize * boxRowNum) + boxBaseLineNum,
// =IF ( ( T4 >= 5 + 1 ) AND ( T4 <= 5 + 5 ); ( 5 * R5 ) + T4 ; "False")
// boxBelowTrue = (boxBaseLineNum >= 1) && (boxBaseLineNum <= this.boardSize),
// V4 = boxBaseLineNum // =(V4 >= 1) AND (V4 <= 5) // Nice stat, but not currently used.
boxLeftNum = (boxBaseLineNum >= (this.boardSize + 2) &&
boxBaseLineNum <= (this.boardSize + this.boardSize + 1)) ?
(this.boardSize * boxRowNum) + (boxBaseLineNum - this.boardSize - 1) : 0,
// =IF ( V4 >= 5 + 2 ) AND ( V4 <= 5 + 5 + 2 )
// ( 5 * R11 ) + ( T10 - 5 )
boxRightNum = (boxBaseLineNum >= (this.boardSize + 1) &&
boxBaseLineNum <= (this.boardSize + this.boardSize)) ?
(this.boardSize * boxRowNum) + (boxBaseLineNum - this.boardSize) : 0,
// =IF ( V4 >= 5 + 1 ) AND ( V4 <= 5 + 5 )
boxAboveNum = (boxBaseLineNum >= ((this.boardSize * 2) + 2) &&
boxBaseLineNum <= ((this.boardSize * 3) + 1)) ?
(this.boardSize * boxRowNum) + (boxBaseLineNum - boxRowSize) : 0;
// =IF ( V4 >= ((5 * 2) + 1) + 1) AND ( V4 <= ((5 * 3) + 1))
// End of constant assignments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .^
let boxBelowNum = 0;
// Had restriction on bottm boxes due to initially developing for the top row.
// In that top row, the bottom tier (6 and 7 in a 2x2) also have a bottom box,
// but that bottom box tolerance had yet to be developed; calculation adopted.
if (boxBaseLineNum >= 1 && boxBaseLineNum <= this.boardSize) {
boxBelowNum = (this.boardSize * boxRowNum) + boxBaseLineNum;
} else if (boxBaseLineNum > boxRowSize && (boxRowNum + 1) < this.boardSize) {
boxBelowNum = (this.boardSize * boxRowNum) + (boxBaseLineNum - boxRowSize + this.boardSize);
} // =IF ( (V4 >= 1) AND (V4 <= 5); V4 * R5; "False") // Box number or 0 (for none)
// console.log('above: ', boxBaseLineNum, boxRowNum, boxRowSize);
if (boxBelowNum > 0) boxedArray.push(boxBelowNum);
if (boxLeftNum > 0) boxedArray.push(boxLeftNum);
if (boxRightNum > 0) boxedArray.push(boxRightNum);
if (boxAboveNum > 0) boxedArray.push(boxAboveNum);
// console.log('boxRowBase', boxRowBase); // 16
// console.log('boxRowSize', boxRowSize); // 11
// console.log('boxRowNum', boxRowNum); // 3
// console.log('boxBaseLineNum', boxBaseLineNum); // 16
// console.log('boxNumBase', boxNumBase); // 31
// console.log('boxAboveNum', boxAboveNum); // 20
// console.log('boxBelowNum', boxBelowNum); // 25
// console.log('boxLeftNum', boxLeftNum); // 0
// console.log('boxRightNum', boxRightNum); // 0
// console.log('boxedArray', boxedArray); // [25, 20]
return boxedArray;
// return {
// 'boxBelowNum': boxBelowNum,
// 'boxLeftNum': boxLeftNum,
// 'boxRightNum': boxRightNum,
// 'boxAboveNum': boxAboveNum
// };
}
Game.prototype.checkIfBoxed = function(boxNum) {
let thisBox = [],
notFound = 0,
borderLines = this.get4BoxLines(boxNum);
// TOP
if (this.linesPlayed.indexOf(borderLines['top']) >= 0) {
thisBox.push(borderLines['top']);
} else { notFound = 'top'; }
// LEFT
if (this.linesPlayed.indexOf(borderLines['left']) >= 0) {
thisBox.push(borderLines['left']);
} else { notFound = 'left'; }
// RIGHT
if (this.linesPlayed.indexOf(borderLines['right']) >= 0) {
thisBox.push(borderLines['right']);
} else { notFound = 'right'; }
// BOTTOM BOX
if (this.linesPlayed.indexOf(borderLines['bottom']) >= 0) {
thisBox.push(borderLines['bottom']);
} else { notFound = 'bottom'; }
// console.log('checkIfBoxed', boxNum, '|', thisBox, notFound, '|', borderLines['top'], this.linesPlayed);
return (thisBox.length === 4) ? true : false;
}
Game.prototype.get4BoxLines = function(boxNum) {
let boardR = Math.ceil(boxNum / this.boardSize) - 1,
boardC = boxNum % this.boardSize === 0 ? this.boardSize : boxNum % this.boardSize,
base = (((this.boardSize * 3) + 1 - this.boardSize) * boardR),
top = base + boardC, // =(((box5*3)+1-box5)*box5r2)+box5c1
left = base + this.boardSize + boardC, // =(((box5*3)+1-box5)*box5r2)+box5+box5c1
right = base + this.boardSize + boardC + 1, // =(((box5*3)+1-box5)*box5r2)+box5+box5c1+1
bottom = base + ((this.boardSize * 2) + 1) + boardC; // =(((box5*3)+1-box5)*box5r2)+((box5*2)+1)+box5c1
// console.log('get4BoxLines', boxNum, boardR, boardC, '|', top, left, right, bottom);
return {
'top': top,
'left': left,
'right': right,
'bottom': bottom
};
}
// const original = [1, 3, 4],
// gameResults = [1, 3, 4, 6],
// game = new Game(2);
// const original = [ 20, 21, 24, 26, 33, 34, 35, 38, 39 ],
// gameResults = [20, 21, 24, 25, 26, 29, 30, 33, 34, 35, 38, 39],
// game = new Game(4);
// console.log(game.play(original), "Should return '[" + gameResults + "]'");
// const letsPlay1 = new Game(5, 3, ['Keith', 'Seliena', 'Tiffany']);
// letsPlay1.play(49); // ^ board, players, playerNames
// letsPlay1.play(48); // Player 2
// letsPlay1.play(55); // Player 3
// letsPlay1.play(54); // Player 1
// letsPlay1.play(60); // Player 2
// letsPlay1.play(59); // Player 2
// letsPlay1.play(53); // Player 3
// ng1.play(2);
function adjustPlayerNames(dir, bsize) {
// <Number>bsize is only used to accommodate board size decrements
let playerNamesArr = document.getElementsByClassName('player-names');
// Reset 'number of players' tooltip hint (activated when attempting to go too high/low).
// clearHint();
if (dir === 'up') {
if (parseInt(document.getElementsByClassName('board-size')[0].value, 10) > playerNamesArr.length) {
clearHint();
// add one
let tmpInput = document.createElement('input');
tmpInput.type = "text";
tmpInput.className = "player-names";
playerNamesArr[0].parentNode.appendChild(tmpInput);
} else {
if (document.getElementsByTagName('tooltip')[0].classList.contains('tooltip-small')) {
highlightHint(); // Maxed out. Highlight Tooltip for hint.
}
}
} else if (dir === 'down') {
// remove one
if (playerNamesArr.length > 2) {
clearHint();
playerNamesArr[0].parentNode.removeChild(playerNamesArr[playerNamesArr.length-1]);
} else {
if (document.getElementsByTagName('tooltip')[0].classList.contains('tooltip-small')) {
highlightHint(); // Min'd out. Highlight Tooltip for hint.
}
}
} else if (dir === 'max') {
clearHint();
while (playerNamesArr[0] && playerNamesArr.length > bsize) {
playerNamesArr[0].remove();
}
}
}
function highlightHint() {
// document.getElementsByTagName('tooltip')[0].style.fontWeight = 'bold';
document.querySelector('.tooltip-open').classList.add('bold');
document.querySelector('.tooltip-open').style.color = 'red';
document.querySelector('.tooltip-open').style.boxShadow = '1px 1px 2px 2px yellowgreen';
}
function dehighlightHint() {
// document.getElementsByTagName('tooltip')[0].style.fontWeight = 'bold';
document.querySelector('.tooltip-open').classList.remove('bold');
document.querySelector('.tooltip-open').style.color = 'rgba(200,75,25,0.75)';
document.querySelector('.tooltip-open').style.boxShadow = 'none';
}
function showHint() {
// document.querySelector('.tooltip-content').style.display = 'inline-block';
// console.log('cl: ', document.getElementsByTagName('tooltip')[0]);
if (document.getElementsByTagName('tooltip')[0].classList.contains('tooltip-small')) {
document.querySelector('.tooltip').style.zIndex = 2;
document.getElementsByTagName('tooltip')[0].classList.remove('tooltip-small');
document.getElementsByTagName('tooltip')[0].classList.add('tooltip-big');
}
}
function clearHint() {
dehighlightHint();
let thisTip = document.getElementsByTagName('tooltip');
// document.querySelector('.tooltip-content').style.display = 'none';
document.querySelector('.tooltip').style.zIndex = 0;
thisTip[0].classList.remove('tooltip-big');
thisTip[0].classList.add('tooltip-small');
// document.getElementsByClassName('tooltip-close')[0].innerHTML = '?';
// console.log('click ttc cH', thisTip[0]);
}
function getName(curNames) {
const rNames = ['Paper', 'Steckle', 'Lunarcey', 'Hydraple', 'D-31', 'Superr Duperr',
'Fortwone', 'Atlassed', 'Everwood', 'Oak', 'Abovert', 'Lethion',
'Elmentts', 'Qemple', 'Curry Ahn', 'Parry Over', 'Peezee',
'Central', 'Sentral', 'Cyglue', 'Get-a-Grip', 'Down Up',
'Xissors', 'Yoo Doo', 'Marsent', 'Tuthedoreon Ish'],
getPick = () => Math.floor(Math.random() * rNames.length);
let tmpRun = 0;
let keepCnt = 0;
let tmpPick = '';
const getNameTry = () => {
if (keepCnt > 7)
return;
else
keepCnt++;
tmpPick = rNames[getPick()];
if (curNames.includes(tmpPick)) {
// console.log('getNameTry again: ', keepCnt, tmpPick, curNames);
getNameTry();
}
}
getNameTry();
// console.log('GOT Pick: ', tmpPick);
return tmpPick;
}
function preChecks() {
let iBoardSize = document.querySelector('.board-size').value,
iNumPlayers = document.getElementsByClassName('player-names');
// @TODO: Need error-handling
// (can pass back to the game board for feedback)
if (isNaN(parseInt(iBoardSize, 10))) {
throw new Error('Board size can only be a number (between 2 and 7, inclusive).');
} else {
// So far, so good.
let iBoard = parseInt(iBoardSize, 10),
iPlayers = iNumPlayers.length;
if (iBoard < 2 || iBoard > 7) {
throw new Error('Board size can only be between 2 and 7, inclusive.');
} else {
// So far, so good, good. (We can trust iBoard to be a number between 2 and 7.)
if (iNumPlayers.length < 2 || iNumPlayers.length > iBoard) {
throw new Error('Number of players can only be between 2 and the size of the board, inclusive.');
} else {
// So far, so good, good... good. (Number of names [iNumPlayers] is a good count.)
let playerNamesArr = Array.from(iNumPlayers),
curNames = [],
regCheck = /^[a-z0-9\s_\-']+$/i,
nameErrors = [];
// Names can only contain A-Z 0-9 \W _ - '
for (let ii = 0; ii < playerNamesArr.length; ii++) {
if (playerNamesArr[ii].value.length !== 0) {
// A name was entered for this field #
if (regCheck.test(playerNamesArr[ii].value)) {
// Passed our RegExp check
curNames[ii] = playerNamesArr[ii].value;
} else {
// An invalid character? Log it, and assign something else.
curNames[ii] = getName(curNames);
nameErrors.push({
playerNum: ii,
nameOld: playerNamesArr[ii].value,
nameNew: curNames[ii]
});
}
} else {
curNames[ii] = getName(curNames);
}
// /[a-z0-9\w]/gi.test(t1) // My pitiful first-thought attempt
// /^[a-z0-9\W]+$/i.test(t1) // https://stackoverflow.com/a/389022/638153
}
// console.log('preChecks Complete: ', curNames, nameErrors);
letsPlay(iBoard, curNames, nameErrors);
// iNumPlayers
// And now we're really good! Silver-like!
/*
this.boardSize = this.isGoodType(board, Number) ? board : 2; // 2 - 7
this.numOfPlayers = this.isGoodType(players, Number) ? players : 2; // 2 - [board]
this.playerNames = this.isGoodType(playerNames, Array, String) ? playerNames : [];
this.numOfBoxes = 1 << this.boardSize; // A.k.a., Math.pow(2, this.boardSize);
this.linesPlayed = [];
this.boxesPinned = [];
this.numOfLines = (4 + ((this.boardSize-1)*3)) + ((this.boardSize-1) * (3 + ((this.boardSize-1)*2)));
this.players = Array.from(Array(this.numOfPlayers)).map( e => 0);
this.curPlayer = 1;
this.curLine = 0;
*/
}
}
}
}
function letsPlay(lpBoardSize, lpCurNames, lpNameErrors) {
// @TODO: Create a queue so multiple games can be played.
letsPlay1 = new Game(lpBoardSize, lpCurNames);
letsPlay1.createBoard();
// This will get called when a <div> on the board is clicked.
// letsPlay1.play(49); // ^ board, playerNames
}
function clearGame() {
letsPlay1.removeBoard();
letsPlay1 = {};
}
window.onload = function() {
document.querySelector('.start-it-button').addEventListener('click', () => {
clearHint();
preChecks();
// Check all inputs
// Assign inputs to variables
// Add functional buttons as needed.
});
document.querySelector('.main-board-close').addEventListener('click', () => {
clearGame(letsPlay1);
});
document.getElementsByClassName('board-size')[0].addEventListener('change', (e) => {
adjustPlayerNames('max', parseInt(e.target.value, 10));
});
document.getElementsByClassName('players-count-up')[0].addEventListener('click', () => {
adjustPlayerNames('up');
});
document.getElementsByClassName('players-count-down')[0].addEventListener('click', () => {
adjustPlayerNames('down');
});
document.querySelector('.tooltip .tooltip-open').addEventListener('click', () => {
showHint();
});
document.querySelector('.tooltip .tooltip-close').addEventListener('click', () => {
clearHint();
});
document.getElementsByClassName('player-names')[0].addEventListener('change', (e) => {
clearHint();
});
document.getElementsByClassName('player-names')[1].addEventListener('change', (e) => {
clearHint();
});
document.querySelector('.main-board-background').addEventListener('change', (e) => {
document.querySelector('.main-board').style.backgroundColor = e.target.value;
})
document.addEventListener('keydown', function(e) {
// [event.keyCode] is deprecated.
// [event.key] is its replacement.
// [event.code] is not supported well (2017-11-07).
if (e.key.toLowerCase() === 'escape') { // ESCAPE
if (Object.keys(letsPlay1).length !== 0 || letsPlay1.constructor !== Object) {
if (window.confirm("Do you really want to quit?")) {
clearGame(letsPlay1);
}
}
}
});
}
Also see: Tab Triggers