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();}