Why don't you play a game?
Log in to post a comment.
const startPlayer = 0; //min=0 max=1 step=1 (Computer, Human)
const move1 = -1;// min=-1 max=8 step=1 (Pick a move, Top-Left, Top-Middle, Top-Right, Center-Left, Center-Middle, Center-Right, Bottom-Left, Bottom-Middle, Bottom-Right)
const move2 = -1;// min=-1 max=8 step=1 (Pick a move, Top-Left, Top-Middle, Top-Right, Center-Left, Center-Middle, Center-Right, Bottom-Left, Bottom-Middle, Bottom-Right)
const move3 = -1;// min=-1 max=8 step=1 (Pick a move, Top-Left, Top-Middle, Top-Right, Center-Left, Center-Middle, Center-Right, Bottom-Left, Bottom-Middle, Bottom-Right)
const move4 = -1;// min=-1 max=8 step=1 (Pick a move, Top-Left, Top-Middle, Top-Right, Center-Left, Center-Middle, Center-Right, Bottom-Left, Bottom-Middle, Bottom-Right)
const move5 = -1;// min=-1 max=8 step=1 (Pick a move, Top-Left, Top-Middle, Top-Right, Center-Left, Center-Middle, Center-Right, Bottom-Left, Bottom-Middle, Bottom-Right)
const iAmJurgen = 0; // min=0 max=1 step=1 (No, Yes)
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(1);
// Global code will be evaluated once.
const turtle = new Turtle();
const text = new Text();
const ttt = new TicTacToe(startPlayer);
const msgr = new Messenger();
let hasOutput = false;
[move1, move2, move3, move4, move5].reduce((p,c) => p[0] || c == -1? [true, p[1]]: [false, p[1].concat(c)], [false, []]).pop().forEach(m => -1 < m? ttt.humanTurn(m):0);
turtle.jump(-54, -82);
text.print(turtle, 'Tic Tac Toe', .6);
ttt.drawState(turtle);
if(ttt.moves < 2) {
msgr.startplay();
} else {
msgr.yourTurn();
}
function TicTacToe (startPlayer = 0) {
class TicTacToe {
constructor(sp) {
this.start = sp;
this.turn = sp;
this.moves = 0;
this.computer = 0;
this.human = 1;
this.empty = 2;
this.wins = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]];
this.state = Array.from({length: 9}).map(i => this.empty);
if(this.start == this.computer) {
this.myTurn();
}
}
humanTurn(m) {
this.move(m);
if(this.checkWin(this.human)) return msgr.win();
if(this.checkDraw()) return msgr.draw();
this.myTurn();
if(this.checkWin(this.computer)) return msgr.lose();
if(this.checkDraw()) return msgr.draw();
}
checkWin(player) {
return this.wins.map(w => [
w,
[w.map(c => this.state[c])] //map every win possibility to the values on the field
.map(pos => [0, 0, 0].map((v, i) => //for [computer, human, empty]
pos.filter(v => v == i).length //find the count of it in that row/column/diagonal
)).pop()
])
.filter(c => c[1][player] == 3).length > 0;
}
checkDraw() {
return this.state.filter(s => s === this.empty).length == 0;
}
myTurn() {
if(iAmJurgen) {
return [1, 3, 5, 7, 0, 2, 6, 8, 4].reduce((p, c) => p?p:(this.state[c] == this.empty? this.move(c): false), false);
}
if((this.moves == 0 && this.start == this.computer) || (this.moves == 1 && this.state[4] == this.empty)) {
return this.move(4);
}
if(this.moves == 1 && this.start == this.human) {
return this.move(0);
}
if((this.start == this.computer && this.moves == 2)) {;// || this.moves == 3) {
return this.move([0, 2, 6, 8].filter(c => this.state[c] == this.empty).shift());
}
//check if computer can get a win
const win = this.canWin(this.computer, this.state);
if(win !== false) {
return this.move(win);
}
//check if computer needs to defend
const defend = this.canWin(this.human, this.state);
if(defend !== false) {
return this.move(defend);
}
//pick first open spot
this.move(this.state.map((v, i) => [i, v]).filter(c => c[1] === this.empty).shift()[0]);
}
canWin(player, state) {
const canWin = this.wins.map(w => [
w,
[w.map(c => state[c])] //map every win possibility to the values on the field
.map(pos => [0, 0, 0].map((v, i) => //for [computer, human, empty]
pos.filter(v => v == i).length //find the count of it in that row/column/diagonal
)).pop()
])
.filter(c => c[1][player] == 2 && c[1][this.empty] == 1) //find the combo that has 2 player and 1 empty
.map(c => c[0])
.pop();
if(canWin == undefined) return false;
return canWin.map(c => [c, state[c]]).filter(v => v[1] == this.empty).pop()[0];
}
move(m) {
if(this.state[m] !== this.empty) {
return msgr.cheat();
}
this.state[m] = this.turn;
this.turn = (this.turn + 1) % 2;
this.moves++;
return true;
}
drawState(turtle, location = [0,0], size=130) {
const cellCenters = [[-1,-1], [0, -1], [1, -1],[-1, 0], [0, 0], [1, 0],[-1, 1], [0, 1], [1, 1]];
const thickness = size/17;
const cellWidth = thickness * 5;
const boxSize = thickness * 4;
const cellCenterInterval = cellWidth+thickness;
const add2 = (a,b)=>[a[0]+b[0],a[1]+b[1]];
const scale2 = (a,s)=>[a[0]*s,a[1]*s];
const drawPath = (v, i, a) => { turtle.jump(v); turtle.goto(a[(i+1)%a.length]) }
const getCrossPath = () => [(thickness**2/2)**.5].map(dd => [0, 1, 2, 3].map(i => rot2(i*-Math.PI/2)).flatMap(rot => [[-dd, 0], [-boxSize/2, dd-boxSize/2], [dd-boxSize/2,-boxSize/2]].map(pt => trans2(rot, pt))));
const getCirclePath = () => [boxSize/2, boxSize/4].map(r => Array.from({length: (size * Math.PI + 1) | 0}).map((v, i, a) => [Math.sin(i * 2 * Math.PI/a.length), Math.cos(i * 2 * Math.PI/a.length)].map(o => o * r)));
const getFieldPath = () => [[0, 1, 2, 3].map(i => rot2(i*-Math.PI/2)).flatMap(rot => [true, false].flatMap(mirror => [[[cellWidth/2, -cellWidth/2 - thickness],[cellWidth/2, -3*cellWidth/2 - thickness],[cellWidth/2 + thickness, -3*cellWidth/2 - thickness],[cellWidth/2 + thickness, -cellWidth/2 - thickness]]].map(a => mirror? a.reverse().map(pt => [-pt[0], pt[1]]): a).pop()).map(pt => trans2(rot, pt))), [[-.5,-.5],[.5,-.5],[.5,.5],[-.5,.5]].map(pt => [pt[0]*cellWidth,pt[1]*cellWidth])];
const chars = [getCrossPath, getCirclePath, () => []];
getFieldPath().forEach(p => p.forEach(drawPath));
this.state.forEach((v, ix) => chars[v]()
.map(p => p.map(pt => add2(pt, scale2(cellCenters[ix], cellCenterInterval))))
.forEach(p => p.forEach(drawPath))
);
}
}
return new TicTacToe(startPlayer);
}
function Messenger() {
class Messenger {
constructor() {
this.hasOutput = false;
}
cheat() {
this.hasOutput = true;
turtle.jump(-90, 80);
if(iAmJurgen == 0)
return text.print(turtle, 'Well, well, well... Typically human to cheat. You lose!', .2);
text.print(turtle, 'Since you\'re so cool and all, I\'ll let it slide.', .2);
}
draw() {
this.hasOutput = true;
turtle.jump(-90, 90);
text.print(turtle, 'It\'s a draw. Click \'Reset values\' to play again.', .2);
}
win() {
this.hasOutput = true;
turtle.jump(-90, 90);
text.print(turtle, 'You win! You\'re so cool!', .2);
}
lose() {
this.hasOutput = true;
console.log('a', this.hasOutput);
turtle.jump(-90, 80);
text.print(turtle, 'Probably not the first time. And since you\'re\nonly human, certainly not the last: you lost.', .25);
}
yourTurn() {
console.log('b', this.hasOutput);
if(this.hasOutput) return;
this.hasOutput = true;
console.log('c', this.hasOutput);
turtle.jump(-90, 90);
if(iAmJurgen) return text.print(turtle, 'Feel free to make a move sweetheart...', .2);
const insults = [
'Don\'t get your hopes up. Resistance is futile.',
'So far you did well, for a human.',
'Hope is human error, like confidence is.',
]
text.print(turtle, 'Your turn. ' + insults[(Math.random() * insults.length) | 0], .2);
}
startplay() {
this.hasOutput = true;
turtle.jump(-90, 90);
if(iAmJurgen) return text.print(turtle, 'Feel free to make a move sweetheart...', .2);
text.print(turtle, 'Your turn. Try your best you obsolete human.', .2);
}
}
return new Messenger();
}
function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; }
function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; } //Matrix(2x1) x Matrix(2x2)
////////////////////////////////////////////////////////////////
// Text utility code. Created by Reinder Nijhoff 2019
// https://turtletoy.net/turtle/1713ddbe99
// Jurgen 2021: Fixed Text.print() to restore turtle._fullCircle
//. if was in e.g. degrees mode (or any other)
////////////////////////////////////////////////////////////////
function Text() {class Text {print (t, str, scale = 1, italic = 0, kerning = 1) {let fc = t._fullCircle;t.radians();let pos = [t.x(), t.y()], h = t.h(), o = pos;str.split('').map(c => {const i = c.charCodeAt(0) - 32;if (i < 0 ) {pos = o = this.rotAdd([0, 48*scale], o, h);} else if (i > 96 ) {pos = this.rotAdd([16*scale, 0], o, h);} else {const d = dat[i], lt = d[0]*scale, rt = d[1]*scale, paths = d[2];paths.map( p => {t.up();p.map( s=> {t.goto(this.rotAdd([(s[0]-s[1]*italic)*scale - lt, s[1]*scale], pos, h));t.down();});});pos = this.rotAdd([(rt - lt)*kerning, 0], pos, h);}});t._fullCircle = fc;}rotAdd (a, b, h) {return [Math.cos(h)*a[0] - Math.sin(h)*a[1] + b[0], Math.cos(h)*a[1] + Math.sin(h)*a[0] + b[1]];}}const dat = ('br>eoj^jl<jqirjskrjq>brf^fe<n^ne>`ukZdz<qZjz<dgrg<cmqm>`thZhw<lZlw<qao_l^h^e_caccdeefggmiojpkqmqporlshsercp>^vs^as<f^h`hbgdeeceacaab_d^f^h_k`n`q_s^<olmmlolqnspsrrspsnqlol>]wtgtfsereqfphnmlpjrhsdsbraq`o`makbjifjekckaj_h^f_eaecffhimporqssstrtq>eoj`i_j^k_kajcid>cqnZl\\j_hcghglhqjulxnz>cqfZh\\j_lcmhmllqjuhxfz>brjdjp<egom<ogem>]wjajs<ajsj>fnkojpiojnkokqis>]wajsj>fnjniojpkojn>_usZaz>`ti^f_dbcgcjdofrisksnrpoqjqgpbn_k^i^>`tfbhak^ks>`tdcdbe`f_h^l^n_o`pbpdofmicsqs>`te^p^jfmfogphqkqmppnrkshserdqco>`tm^clrl<m^ms>`to^e^dgefhekenfphqkqmppnrkshserdqco>`tpao_l^j^g_ebdgdlepgrjsksnrppqmqlpingkfjfggeidl>`tq^gs<c^q^>`th^e_dadceegfkgnhpjqlqopqorlshserdqcocldjfhigmfoepcpao_l^h^>`tpeohmjjkikfjdhcecddaf_i^j^m_oapepjoomrjshserdp>fnjgihjikhjg<jniojpkojn>fnjgihjikhjg<kojpiojnkokqis>^vrabjrs>]wagsg<amsm>^vbarjbs>asdcdbe`f_h^l^n_o`pbpdofngjijl<jqirjskrjq>]xofndlcicgdfeehekfmhnknmmnk<icgefhfkgmhn<ocnknmpnrntluiugtdsbq`o_l^i^f_d`bbad`g`jambodqfrislsorqqrp<pcokompn>asj^bs<j^rs<elol>_tc^cs<c^l^o_p`qbqdpfoglh<chlhoipjqlqopqorlscs>`urcqao_m^i^g_eadccfckdnepgrismsorqprn>_tc^cs<c^j^m_oapcqfqkpnopmrjscs>`sd^ds<d^q^<dhlh<dsqs>`rd^ds<d^q^<dhlh>`urcqao_m^i^g_eadccfckdnepgrismsorqprnrk<mkrk>_uc^cs<q^qs<chqh>fnj^js>brn^nnmqlrjshsfreqdndl>_tc^cs<q^cl<hgqs>`qd^ds<dsps>^vb^bs<b^js<r^js<r^rs>_uc^cs<c^qs<q^qs>_uh^f_daccbfbkcndpfrhslsnrppqnrkrfqcpan_l^h^>_tc^cs<c^l^o_p`qbqepgohlici>_uh^f_daccbfbkcndpfrhslsnrppqnrkrfqcpan_l^h^<koqu>_tc^cs<c^l^o_p`qbqdpfoglhch<jhqs>`tqao_l^h^e_caccdeefggmiojpkqmqporlshsercp>brj^js<c^q^>_uc^cmdpfrisksnrppqmq^>asb^js<r^js>^v`^es<j^es<j^os<t^os>`tc^qs<q^cs>asb^jhjs<r^jh>`tq^cs<c^q^<csqs>cqgZgz<hZhz<gZnZ<gznz>cqc^qv>cqlZlz<mZmz<fZmZ<fzmz>brj\\bj<j\\rj>asazsz>fnkcieigjhkgjfig>atpeps<phnfleiegfehdkdmepgrislsnrpp>`sd^ds<dhffhekemfohpkpmopmrkshsfrdp>asphnfleiegfehdkdmepgrislsnrpp>atp^ps<phnfleiegfehdkdmepgrislsnrpp>asdkpkpiognfleiegfehdkdmepgrislsnrpp>eqo^m^k_jbjs<gene>atpepuoxnylzizgy<phnfleiegfehdkdmepgrislsnrpp>ate^es<eihfjemeofpips>fni^j_k^j]i^<jejs>eoj^k_l^k]j^<kekvjyhzfz>are^es<oeeo<ikps>fnj^js>[y_e_s<_ibfdegeifjijs<jimfoeretfuius>ateees<eihfjemeofpips>atiegfehdkdmepgrislsnrppqmqkphnfleie>`sdedz<dhffhekemfohpkpmopmrkshsfrdp>atpepz<phnfleiegfehdkdmepgrislsnrpp>cpgegs<gkhhjfleoe>bsphofleieffehfjhkmlompopporlsisfrep>eqj^jokrmsos<gene>ateeeofrhsksmrpo<peps>brdejs<pejs>_ubefs<jefs<jens<rens>bseeps<pees>brdejs<pejshwfydzcz>bspees<eepe<esps>cqlZj[i\\h^h`ibjckekgii<j[i]i_jakbldlfkhgjkllnlpkrjsiuiwjy<ikkmkojqirhthvixjylz>fnjZjz>cqhZj[k\\l^l`kbjcieigki<j[k]k_jaibhdhfihmjilhnhpirjskukwjy<kkimiojqkrltlvkxjyhz>^vamakbhdgfghhlknlplrksi<akbidhfhhillnmpmrlsisg>brb^bscsc^d^dsese^f^fsgsg^h^hsisi^j^jsksk^l^lsmsm^n^nsoso^p^psqsq^r^rs').split('>').map(r=> { return [r.charCodeAt(0)-106,r.charCodeAt(1)-106, r.substr(2).split('<').map(a => {const ret = []; for (let i=0; i<a.length; i+=2) {ret.push(a.substr(i, 2).split('').map(b => b.charCodeAt(0)-106));} return ret; })]; });return new Text();}