Skip to content

Instantly share code, notes, and snippets.

@Khoding
Last active July 26, 2022 11:54
Show Gist options
  • Save Khoding/801cbb7f045fcbfa4d5292344e5ef084 to your computer and use it in GitHub Desktop.
Save Khoding/801cbb7f045fcbfa4d5292344e5ef084 to your computer and use it in GitHub Desktop.
//bookmarklet_title: Ultimate TicTacToe
//bookmarklet_about: TicTacToeCeption
/* jshint esversion: 6 */
// javascript: (function() {
// var js = document.body.appendChild(document.createElement('script'));
// js.onerror = function() {
// alert('Sorry, the script could not be loaded.');
// };
// js.src = 'https://cdn.jsdelivr.net/gh/Krazete/bookmarklets@master/tic.js';
// })();
// Randomize the first marker.
let turn = Math.random() < 1 / 2 ? 0 : 1;
// Set unit size.
const unit = 100 / 11;
// Create menu.
const menu = document.createElement('div');
menu.style.position = 'fixed';
menu.style.top = '50%';
menu.style.left = '50%';
menu.style.height = 9 * unit + 'vmin';
menu.style.width = 9 * unit + 'vmin';
menu.style.transform = 'translate(-50%,-50%)';
menu.style.zIndex = 9999;
menu.innerHTML = "<a style='background:white;position:absolute;left:100%;cursor:pointer' onClick='menu.remove()'>exit</a>";
document.body.appendChild(menu);
// Create board.
const board = document.createElement('div');
board.style.background = 'rgba(255,255,255,0.9)';
board.style.position = 'absolute';
board.style.top = 0;
board.style.left = 0;
board.style.height = 9 * unit + 'vmin';
board.style.width = 9 * unit + 'vmin';
board.mark = null;
menu.appendChild(board);
// Create macro and micro maps.
BOXES = [];
boxes = [[], [], [], [], [], [], [], [], []];
// Fill the board.
for (var i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
const BOX = document.createElement('div');
BOX.style.position = 'absolute';
BOX.style.top = (i * 100) / 3 + '%';
BOX.style.left = (j * 100) / 3 + '%';
BOX.style.height = 100 / 3 + '%';
BOX.style.width = 100 / 3 + '%';
BOX.mark = null;
for (let k = 0; k < 3; k++) {
for (let l = 0; l < 3; l++) {
const box = document.createElement('div');
box.setAttribute('onMouseOver', "this.style.background='rgba(0,0,0,0.25)'");
box.setAttribute('onMouseOut', "this.style.background='transparent'");
box.setAttribute('onClick', 'pick(this)');
box.style.position = 'absolute';
box.style.top = (k * 100) / 3 + '%';
box.style.left = (l * 100) / 3 + '%';
box.style.height = 100 / 3 + '%';
box.style.width = 100 / 3 + '%';
box.macro = 3 * i + j;
box.micro = 3 * k + l;
box.mark = null;
boxes[3 * i + j].push(box);
if (box.macro % 2 == 0) {
box.style.boxShadow = '0 0 1vmin black inset';
} else {
box.style.boxShadow = '0 0 1vmin gray inset';
}
BOX.appendChild(box);
}
}
BOXES.push(BOX);
board.appendChild(BOX);
}
}
/*
+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
| 3 | 4 | 5 |
+---+---+---+
| 6 | 7 | 8 |
+---+---+---+
*/
// Check for victories.
function check(m) {
let checkmark = 0;
if (m[0].mark != null) {
if ((m[0].mark == m[1].mark && m[1].mark == m[2].mark) || (m[0].mark == m[3].mark && m[3].mark == m[6].mark)) {
checkmark = 1;
}
}
if (m[4].mark != null) {
if (
(m[3].mark == m[4].mark && m[4].mark == m[5].mark) ||
(m[1].mark == m[4].mark && m[4].mark == m[7].mark) ||
(m[0].mark == m[4].mark && m[4].mark == m[8].mark) ||
(m[2].mark == m[4].mark && m[4].mark == m[6].mark)
) {
checkmark = 1;
}
}
if (m[8].mark != null) {
if ((m[6].mark == m[7].mark && m[7].mark == m[8].mark) || (m[2].mark == m[5].mark && m[5].mark == m[8].mark)) {
checkmark = 1;
}
}
if (checkmark) {
marker(m[0].parentNode);
} else {
if (
m[0].mark != null &&
m[1].mark != null &&
m[2].mark != null &&
m[3].mark != null &&
m[4].mark != null &&
m[5].mark != null &&
m[6].mark != null &&
m[7].mark != null &&
m[8].mark != null
) {
draw(m[0].parentNode);
}
}
}
// Place a tied marker.
function draw(obj) {
// Disable the tied box.
obj.mark = 2;
disable(obj);
obj.style.pointerEvents = 'none';
}
// Place a marker.
function marker(obj) {
// Place an O.
if (turn == 0) {
const o = document.createElement('div');
o.style.boxSizing = 'border-box';
o.style.position = 'absolute';
o.style.top = '10%';
o.style.left = '10%';
o.style.height = '80%';
o.style.width = '80%';
// Set O border size based on nested box level, because for some reason percentage isn't supported.
let inBOXES = 0;
for (let i = 0; i < 9; i++) {
if (obj == BOXES[i]) {
inBOXES = 1;
break;
}
}
o.style.border = (unit * (obj == board ? 9 : inBOXES ? 3 : 1)) / 5 + 'vmin solid black';
o.style.borderRadius = '50%';
obj.appendChild(o);
}
// Place an X.
else {
const x1 = document.createElement('div');
x1.style.boxSizing = 'border-box';
x1.style.background = 'black';
x1.style.position = 'absolute';
x1.style.top = '10%';
x1.style.left = '40%';
x1.style.height = '80%';
x1.style.width = '20%';
x1.style.transform = 'rotate(45deg)';
obj.appendChild(x1);
const x2 = x1.cloneNode();
x2.style.transform = 'rotate(-45deg)';
obj.appendChild(x2);
}
// Mark the map and disable the marked box.
obj.mark = turn;
disable(obj);
obj.style.pointerEvents = 'none';
}
// Do stuff to a box upon click.
function pick(b) {
// Mark box.
marker(b);
// Check victories.
check(boxes[b.macro]);
check(BOXES);
// Pass turn to other player.
turn > 0 ? (turn = 0) : (turn = 1);
// Enable next macro box if it isn't won.
if (BOXES[b.micro].mark == null) {
// Disable all boxes.
for (i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
disable(boxes[i][j]);
}
}
// Enable next macro box.
for (i = 0; i < 9; i++) {
// Forces won macro boxes to stay disabled.
if (board.mark == null) {
enable(boxes[b.micro][i]);
}
}
}
// Enable all macro boxes if it's won.
else {
for (i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
// Forces won macro boxes to stay disabled.
if (board.mark == null) {
enable(boxes[i][j]);
}
}
}
}
// Forces won micro boxes to stay disabled.
disable(b);
}
// Disables a box.
function disable(b) {
b.style.background = 'rgba(0,0,0,0.25)';
b.setAttribute('onMouseOver', '');
b.setAttribute('onMouseOut', '');
b.setAttribute('onClick', '');
}
// Enables a box.
function enable(b) {
// Forces won boxes to stay disabled.
if (b.mark == null) {
b.style.background = 'transparent';
}
b.setAttribute('onMouseOver', "this.style.background='rgba(0,0,0,0.25)'");
b.setAttribute('onMouseOut', "this.style.background='transparent'");
b.setAttribute('onClick', 'pick(this)');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment