Skip to content

Instantly share code, notes, and snippets.

@mousetail
Created May 31, 2024 10:50
Show Gist options
  • Save mousetail/487cb775e4f4a499da6a6553f8b46ae2 to your computer and use it in GitHub Desktop.
Save mousetail/487cb775e4f4a499da6a6553f8b46ae2 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<style>
td {
color: grey;
border: 1px solid white;
padding: 0.25rem;
}
td.valid {
color: black;
border: 1px solid black;
}
td.valid-streak {
background-color: green;
color: white;
}
</style>
</head>
<body>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<div>
<h2>It's <span id="turn"></span>'s Turn</h2>
<table id="table" style="font-family: monospace">
</table>
<button id="undo-btn">
Undo
</button>
</div>
<div>
<h2>Streaks</h2>
<table id="streaks-table">
</table>
<button id='export'>Copy</button>
<textarea id='js-copytextarea' style='opacity: 0'>
</textarea>
</div>
</div>
<script>
const MAX_STREAK = 8;
const nececairy_directions = [
[1, 0,], // Can be flipped to create [0, 1], [-1, 0], [0, -1];
[1, 1,], // Can be flipped to create [-1, -1],
[-1, 1,], // Can be flipped to create [1, -1],
];
const get_nececairy_direction_index = (x,y) => (
nececairy_directions.findIndex(([dx, dy]) => (
(x == dx && y == dy) ||
(x == -dx && y == -dy) ||
(x == dy && y == dx) ||
(x == -dy && y == -dx)
))
);
const updateStreaksTable = () => {
const streaksTable = document.getElementById('streaks-table');
for (let i = 0; i<MAX_STREAK; i++) {
let row = document.createElement('tr');
let th = document.createElement('th');
th.textContent = i;
row.appendChild(th);
for (let j=0; j<nececairy_directions.length; j++) {
let cell = document.createElement('td');
cell.textContent = nececairy_directions[j].join(', ');
row.appendChild(cell);
}
streaksTable.appendChild(row);
}
}
(() => {
const table = document.getElementById('table');
const streaksTable = document.getElementById('streaks-table');
let turn = 'X';
let undo = [];
let streaks = [
[null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null],
];
const g = (a,b) => table.rows.item(a).childNodes[b].textContent;
const s = (a,b,v) => table.rows.item(a).childNodes[b].textContent = v;
const set_valid = (a,b,v) => table.rows.item(a).childNodes[b].classList.toggle('valid', v);
const set_streak_valid = (a,b,v) => streaksTable.rows.item(a).childNodes[b + 1].classList.toggle('valid-streak', v);
const set_tiles = (a,b,v) => table.rows.item(a).childNodes[b].dataset.tiles = JSON.stringify(v);
const get_other_team = (team) => team === 'O' ? 'X' : 'O';
const is_in_range = (t)=> t>=0 && t<8;
const setTurn = (t) => {
turn = t;
document.getElementById('turn').textContent = turn;
}
const updateTurnAndStreaks = () => {
const directions = [
[1, 1,],
[1, 0,],
[-1, 1],
[1, -1,],
[-1, -1],
[-1, 0],
[0, 1,],
[0, -1,],
];
for (let x = 0; x<8; x++){
for (let y=0; y<8; y++) {
let flippableTiles = [];
let activeStreaks = [];
if (g(x,y) !== '.') {
set_valid(x, y, false);
continue;
}
for ([dx, dy] of directions) {
let next_x = x + dx;
let next_y = y + dy;
if (!is_in_range(next_x) || !is_in_range(next_y)) {
continue;
}
if (g(next_x, next_y) != get_other_team(turn)) {
continue;
}
let flippable_tiles_in_direction = [];
while (
is_in_range(next_x) && is_in_range(next_y) &&
g(next_x, next_y) == get_other_team(turn)
) {
flippable_tiles_in_direction.push([next_x, next_y]);
next_x += dx;
next_y += dy;
}
if (
is_in_range(next_x) && is_in_range(next_y) &&
g(next_x, next_y) == turn
) {
flippableTiles.push(...flippable_tiles_in_direction);
if (get_nececairy_direction_index(dx, dy) != -1) {
activeStreaks.push([[dx, dy], flippable_tiles_in_direction.length]);
}
}
}
set_valid(x, y, flippableTiles.length > 0);
set_tiles(x, y, flippableTiles);
if (activeStreaks.length === 1 && turn === 'O') {
let [[dx, dy], len] = activeStreaks[0];
let streak_index = get_nececairy_direction_index(dx, dy);
if (streaks[streak_index][len] === null) {
let o = [...table.rows].map((k)=>k.innerText.replace(/\t/g,'')).join('\n');
streaks[streak_index][len] = o;
set_streak_valid(len, streak_index, true);
}
}
}
}
}
for (let i = 0; i<8; i++) {
let row = document.createElement('tr');
for (let j=0; j<8; j++) {
let cell = document.createElement('td');
cell.textContent = '.';
row.appendChild(cell);
cell.addEventListener('click', ()=> {
if (cell.textContent === '.' && cell.classList.contains('valid')) {
cell.textContent = turn;
let tiles = JSON.parse(cell.dataset.tiles);
for ([x,y] of tiles) {
s(x,y,turn);
}
setTurn(get_other_team(turn));
undo.push([cell, tiles]);
updateTurnAndStreaks();
};
})
}
table.appendChild(row);
}
document.getElementById('undo-btn').addEventListener('click', ()=>{
let [cell, tiles] = undo.pop();
for ([x,y] of tiles) {
s(x,y,turn);
}
setTurn(get_other_team(turn));
cell.textContent = '.';
updateTurnAndStreaks();
});
document.getElementById('export').addEventListener('click', function(event) {
const copyTextarea = document.getElementById('js-copytextarea');
copyTextarea.value = streaks.flat().filter(i=>i!=null).join('\n\n');
copyTextarea.focus();
copyTextarea.select();
try {
const successful = document.execCommand('copy');
console.log('Copy succeeded' + successful);
} catch (err) {
console.log('Copy failed');
}
});
s(3,3,'O');
s(4,4,'O');
s(4,3,'X');
s(3,4,'X');
updateStreaksTable();
updateTurnAndStreaks();
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment