Skip to content

Instantly share code, notes, and snippets.

@ThePotatoChronicler
Last active September 24, 2021 02:19
Show Gist options
  • Select an option

  • Save ThePotatoChronicler/bc2b6f694c6e518ce434feefad5e4cef to your computer and use it in GitHub Desktop.

Select an option

Save ThePotatoChronicler/bc2b6f694c6e518ce434feefad5e4cef to your computer and use it in GitHub Desktop.
A TicTacToe program I've made for a school assignment
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TicTacToe</title>
<style>
html, body {
height: 100%;
margin: 0;
}
body {
background-color: #333333;
transition: background 1s ease-in-out
}
body.circle-turn {
background: rgb(0,0,127)
}
body.cross-turn {
background: rgb(127,0,0);
}
#selection-holder {
z-index: 1;
position: absolute;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#end-holder {
z-index: 1;
position: absolute;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
animation: end-anim 2s ease-out;
}
#end-text {
font-size: 2rem;
}
#end-circle {
height: 50%;
width: 50%;
filter: drop-shadow(0 0 30vmax black);
}
#end-circle > circle.circle {
animation-duration: 4s;
}
#end-cross {
height: 50%;
width: 50%;
filter: drop-shadow(0 0 30vmax black);
}
#end-cross > line.crossline1 {
animation-duration: 2s;
}
#end-cross > line.crossline2 {
animation-delay: 2s;
animation-duration: 2s;
}
@keyframes end-anim {
from {
bottom: 100%;
}
to {
bottom: 0%;
}
}
#author {
position: absolute;
bottom: 0;
}
#board-selection {
display: flex;
flex-direction: column;
align-items: center;
}
#board-holder {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
transition: filter 4s;
}
#board-holder.blur-out {
filter: blur(4px);
}
#board {
background-color: #888888;
border-color: #444444;
border-left-width: 10px;
border-right-width: 10px;
border-left-style: solid;
border-right-style: solid;
height: 100%;
width: 100vh;
display: grid;
grid-gap: 3px;
}
#board > div {
background-color: #666666;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
#board > div > svg {
margin: 5%;
width: 100%;
height: 100%;
}
circle.circle {
stroke-dasharray: 236;
stroke-dashoffset: 236;
animation: dashtozero 1s ease-in-out;
animation-fill-mode: forwards;
}
line.crossline1 {
stroke-dasharray: 100;
stroke-dashoffset: 100;
animation: dashtozero 0.5s ease-in-out 0.5s;
animation-fill-mode: forwards;
}
line.crossline2 {
stroke-dasharray: 100;
stroke-dashoffset: 100;
animation: dashtozero 0.5s ease-in-out;
animation-fill-mode: forwards;
}
@keyframes dashtozero {
to {
stroke-dashoffset: 0;
}
}
</style>
</head>
<body>
<svg width="100" height="100" viewbox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"
id="circle-orig" style="visibility: hidden; display: none;">
<circle class="circle" cx="50" cy="50" r="37.5" stroke="#0000FF" stroke-width="25"/>
</svg>
<svg width="86" height="86" viewbox="0 0 86 86" fill="none" xmlns="http://www.w3.org/2000/svg"
id="cross-orig" style="visibility: hidden; display: none;">
<line class="crossline1" x1="78.2843" y1="7.57359" x2="7.57359" y2="78.2843" stroke="#FF0000" stroke-width="20"/>
<line class="crossline2" x1="7.07107" y1="7.57359" x2="77.7817" y2="78.2843" stroke="#FF0000" stroke-width="20"/>
</svg>
<div id="selection-holder">
<div id="board-selection">
<p class="selection-text" style="margin: 0;">Select board size</p>
<div style="display: flex; justify-content: center; align-items: center;">
<input type="range" id="board-range" value="3" min="3" max="10" style="margin-right: 10px;"/>
<output>3</output>
</div>
<p class="selection-text" style="margin-bottom: 0;">Select win length</p>
<div style="display: flex; justify-content: center; align-items: center;">
<input type="range" id="win-range" value="3" min="3" max="10" style="margin-right: 10px;"/>
<output>3</output>
</div>
<input type="button" id="start-button" value="Start" />
</div>
<div id="author">
<p>Author: Potato Chronicler</p>
</div>
</div>
<div id="end-holder" style="display: none; visibility: hidden;">
<svg id="end-circle" width="100" height="100" viewbox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle class="circle" cx="50" cy="50" r="37.5" stroke="#0000FF" stroke-width="25"/>
</svg>
<svg id="end-cross" width="86" height="86" viewbox="0 0 86 86" fill="none" xmlns="http://www.w3.org/2000/svg">
<line class="crossline1" x1="78.2843" y1="7.57359" x2="7.57359" y2="78.2843" stroke="#FF0000" stroke-width="20"/>
<line class="crossline2" x1="7.07107" y1="7.57359" x2="77.7817" y2="78.2843" stroke="#FF0000" stroke-width="20"/>
</svg>
<p id="end-text"></p>
<input type="button" value="Restart" style="font-size: 1.5rem; padding: 0.5rem;" onclick="location.reload()" />
</div>
<div id="board-holder" class="blur-out">
<div id="board">
</div>
</div>
</body>
<script>
let turn = false
const board = document.getElementById("board")
const board_holder = document.getElementById("board-holder")
const selection_holder = document.getElementById("selection-holder")
const end_holder = document.getElementById("end-holder")
const board_range = document.getElementById("board-range")
const win_range = document.getElementById("win-range")
const start_button = document.getElementById("start-button")
const circle_template = (function () {
const orig = document.getElementById("circle-orig")
const e = orig.cloneNode(true)
orig.remove()
e.setAttribute("id", "")
e.dataset.symbol = "o"
e.style.visibility = ""
e.style.display = ""
return e
})()
const cross_template = (function () {
const orig = document.getElementById("cross-orig")
const e = orig.cloneNode(true)
orig.remove()
e.setAttribute("id", "")
e.dataset.symbol = "x"
e.style.visibility = ""
e.style.display = ""
return e
})()
board_range.addEventListener("input", function (e) {
win_range.setAttribute("max", board_range.valueAsNumber)
board_range.nextElementSibling.value = board_range.value
win_range.nextElementSibling.value = win_range.value
while (board.children[0]) board.removeChild(board.children[0])
const bsize = board_range.valueAsNumber
for (let i = 0; i < (bsize * bsize); i++) {
const e = document.createElement("div")
e.addEventListener("click", function (e) {
if (e.target.children.length > 0) {
return
}
const bsize = board_range.valueAsNumber
const wlen = win_range.valueAsNumber
e.target.appendChild(turn ? circle_template.cloneNode(true) : cross_template.cloneNode(true))
let winner = false
win_found: {
// Creates a helpful board to quickly
// check which tiles are the player's that just played
const bvarr = (() => {
const arr = []
for (let row = 0; row < bsize; row++) {
const inarr = []
for (let col = 0; col < bsize; col++) {
const c = board.children[row + bsize * col]
if (c.firstChild == null) {
inarr.push(false)
continue
}
inarr.push(turn ?
c.firstChild.dataset.symbol == "o" :
c.firstChild.dataset.symbol == "x")
}
arr.push(inarr)
}
return arr
})()
const barr = (() => {
const arr = []
for (let col = 0; col < bsize; col++) {
const inarr = []
for (let row = 0; row < bsize; row++) {
const c = board.children[row + bsize * col]
if (c.firstChild == null) {
inarr.push(false)
continue
}
inarr.push(turn ?
c.firstChild.dataset.symbol == "o" :
c.firstChild.dataset.symbol == "x")
}
arr.push(inarr)
}
return arr
})()
// Checks if wlen true values are consecutively in array a
function winc(a) {
let i = 0
let c = 0
for (const v of a) {
if (v) {
c++;
} else {
c = 0
}
if (c == wlen) return true
}
return false
}
// Test vertical
for (let col = 0; col < bsize; col++) {
for (let row = 0; row <= ((bsize - wlen)); row++) {
if(winc(bvarr[col].slice(row, row + wlen))) {
winner = true
break win_found
}
}
}
// Test horizontal
for (let row = 0; row < bsize; row++) {
for (let col = 0; col <= (bsize - wlen); col++) {
if(winc(barr[row].slice(col, col + wlen))) {
winner = true
break win_found
}
}
}
// Test diagonal (left bottom to top right)
{
let row = wlen - 1
let over = 1
for (;row >= (wlen - 1); row += over) {
if (over == 1) {
const arr = []
for (let col = row; col >= 0; col--) {
arr.push(bvarr[col][row-col])
}
if (winc(arr)) {
winner = true
break win_found
}
} else {
const arr = []
for (let col = row; col >= 0; col--) {
arr.push(bvarr[(bsize-1)-col][(bsize-1)-(row-col)])
}
if (winc(arr)) {
winner = true
break win_found
}
}
if ((row + 2) > bsize) over = -over
}
}
// Test diagonal (left bottom to top right)
{
let row = wlen - 1
let over = 1
for (;row >= (wlen - 1); row += over) {
if (over == 1) {
const arr = []
for (let col = row; col >= 0; col--) {
arr.push(barr[(bsize-1)-(row-col)][col])
}
if (winc(arr)) {
winner = true
break win_found
}
} else {
const arr = []
for (let col = row; col >= 0; col--) {
arr.push(bvarr[(bsize-1)-(row-col)][col])
}
if (winc(arr)) {
winner = true
break win_found
}
}
if ((row + 2) > bsize) over = -over
}
}
}
let tie
{
let i = 0
for (;i < board.children.length; i++) {
if (board.children[i].children.length == 0) break;
}
tie = (i == board.children.length)
}
if (tie || winner) {
board_holder.classList.add("blur-out")
end_holder.style.visibility = ""
end_holder.style.display = ""
}
if (winner) {
document.getElementById("end-text").textContent = (turn ? "Circle" : "Cross") + " is the winner!"
if (turn) {
document.getElementById("end-cross").remove()
} else {
document.getElementById("end-circle").remove()
}
} else if (tie) {
const text = document.getElementById("end-text")
text.textContent = "A tie!"
text.style.marginTop = "0"
document.getElementById("end-cross").remove()
document.getElementById("end-circle").remove()
}
if (tie || winner) return
if (tie) {
document.body.classList.remove("circle-turn")
document.body.classList.remove("cross-turn")
} else {
if (turn) {
document.body.classList.remove("circle-turn")
document.body.classList.add("cross-turn")
} else {
document.body.classList.remove("cross-turn")
document.body.classList.add("circle-turn")
}
}
turn = !turn
})
board.appendChild(e)
board.style.gridTemplateColumns = `repeat(${bsize}, 1fr)`
board.style.gridTemplateRows = `repeat(${bsize}, 1fr)`
}
})
win_range.addEventListener("input", function (e) {
win_range.nextElementSibling.value = win_range.value
})
board_range.dispatchEvent(new Event("input"))
start_button.addEventListener("click", function (e) {
turn = false
selection_holder.style.visibility = "hidden"
board_holder.classList.remove("blur-out")
document.body.classList.add("cross-turn")
})
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment