BITNODE: IPvGO territory control strategy game (#934)

This commit is contained in:
Michael Ficocelli
2023-12-26 11:45:27 -05:00
committed by GitHub
parent c6141f2adf
commit 7ef12a0323
68 changed files with 7833 additions and 17 deletions
+242
View File
@@ -0,0 +1,242 @@
// The character 'wei' is part of the original Chinese name of the game Go, meaning to surround or enclose
export const weiArt = `
... .:lc;,'',:c:. ..
.:;,'.,dOOxddONWNNNNNWNOdxxl,,.
.''.... .:dxdolc::kWWNXK0KWMMMMMMMMMMMMMMMMMNK0kl:'..
...... 'colcc:;,'.,xNXXK0OkxxkXMMMWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKkxo:;'..
'c:::;,,'.....;xK00Okxxdold0WMWWWNNNXXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNX0xxo;.
.'......'. .'''...... .dkkxddollc:::dKWWWNNNXXKK000XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMW0xooc.
:OKOOO0KOl::;. .. ....... .loolllc::;;,,'.'dNNNXKK00OOkxxONMMMMMMWWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWXko;.
.kMMMMMMNXNXkdxd:.';;,,''....... ;0KK00OOkkxxddoollkWMMWWWWWWNNNNNXXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNNNNWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMW0d:,.
:KMMMMMMMMMMMMMN0KXNNNNXXXKKKK000XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKkkOOOO00000KKKKXXXXXNNNNNWWWWWx,',,,;;;:::cccccllllcccc:;cOXXMMMMMMMMMMMMMMMMMMMMMMMMMMWKxd;
.cx0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKKKXXNNNWWMKocclloodxxkkO0Oo' .............''''',,,;;;:;. 'cdKWMMMMMMMMMMMMMMMMMMMMMMMMMMWKc
.:ONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNNNWWWWWWMMXkodxxkO0KKX0o'..',,;:cl; ... 'cONMMMMMMMMMMMMMMMMMMMMMMMMWOl'
;lxNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0kO00KXXNWXd;,;::cloddkx;...';:'.... .dNMMMMMMMMMMMMMMMMMMMMMMMNOo'
'kWMMMMMMMMMMMMMMMNXXXXKKKKKXXNNWWWOoclodxkO0x' ....',,;, .xK00000d;;:,.. 'odKMMMMMMMMMMMMMMMMMMMMMMXl,.
.ckNMMMMMMMMMMMMMWx'.........',;:cc:. . .dXWMMMWNXNKxoc''. .,0MMMMMMMMMMMMMMMMMMMMMXOd'
.cXMMMMMMMMMMMMMWx. .:l0MMMMMMMMMWK0koc:' cKMMMMMMMMMMMMMMMMMMMMMXo'
'kWMMMMMMMMMMMMMWx. .:KMMMMMMMMMMMMWWXkl;.. .dNMMMMMMMMMMMMMMMMMMMMKl,
,xkXMMMMMMMMMMMMWk. ,o0WMMMMMMMMMMMMMWXkd; .kWMMMMMMMMMMMMMMMMMMMMK:
.;0MMMMMMMMMMMMWk' .dWMMMMMMMMMMMMMMWKOc ... .... .lOXMMMMMMMMMMMMMMMMMMK0o.
:KMMMMMMMMMMMMWO' 'kWMMMMMMMMMMMMMNxc:. ..;c:o00kxxkOOd::c,.. .kMMMMMMMMMMMMMMMMMWo'.
.oXMMMMMMMMMMMMWO, 'kWMMMMMMMMMMMMXxl' .,;oxOXNWMMMMMMMWNXWKkxo;;' 'OMMMMMMMMMMMMMMMMMWx.
.xNMMMMMMMMMMMMW0, .kWMMMMMMMMMMMMKl' ''.. .:lllOKKWMMMMMMMMMMMMMMMMMWXK0kl'.. ;0MMMMMMMMMMMMMMMMMWk'
.OWMMMMMMMMMMMMW0; .dWMMMMMMMMMMMKxxocc::dKKOkxONWWMMMMMMMMMMMMMMMMMMMMMMMMMMN0ko' .cXMMMMMMMMMMMMMMMMMW0;
'ONWMMMMMMMMMMMMK: ',dWMMMMMMMMMMMKxxkKWWNNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXd:. .dNMMMMMMMMMMMMMMMMMMXc
.;OMMMMMMMMMMMMK: .''..... .xKXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKO: .ckKMMMMMMMMMMMMMMMWK0o
.xMMMMMMMMMMMMXc .. .:oc;,',xNNXK0OOkxkXMMMMMMMMMMMMMMMMMMMMMMWXXXXNWXdcoxkOkxdOWMMMMMMMMMMMMMMMMMMWXOx: .oWMMMMMMMMMMMMMMNc'.
.kMMMMMMMMMMMMNl ;xxdxXMWNNXXWMMMMMMMMMMMMMMMMMMMMMMMWWWMNkdkOKO;.',;c;. ;OWMMMMMMMMMMMMMMMMWXOdc' .xWMMMMMMMMMMMMMMNo.
.OMMMMMMMMMMMMNo 'coONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWOolol. ... ;KWMMMMMMMMMMMMMMWXOxc' 'kWMMMMMMMMMMMMMMNx.
'OMMMMMMMMMMMMWd. .';oKWNXXXXXXXXXNNW0dxd0MMMMMMMMMXxo; 'kWMMMMMMMMMMMMMWOlc' ,OWMMMMMMMMMMMMMMWO'
,0MMMMMMMMMMWOo, .;:,''......',;c',xx0MMMMMMMMMXl. .oNMMMMMMMMMMMMN0x; ,0WMMMMMMMMMMMMMMW0;
;0MMMMMMMMMMX: .kWMMMMMMMMMMWO' :oxNMMMMMMMMMMMNOc'. ;0WMMMMMMMMMMMMMMMK:
;0MMMMMMMMMMNc .lXMMMMMMMMMXd;. :0WMMMMMMMMMMM0o:. ;0MMMMMMMMMMMMMMMMXc
:KMMMMMMMMMMNl :KMMMMMMMMMXl. ,:xWMMMMMMMMMW0o:. :KMMMMMMMMMMMMMMMMNc
:KMMMMMMMMMMNo. ..,OMMMMMMMMNkc. .o0XMMMMMMMMMMWk;. .'.... .... :KMMMMMMMMMMMMMMMMWl
cKMMMMMMMMMMNo. ,kOXMMMMMMMM0; .;OMMMMMMMMMMWOc' .lolc::;',kNXK0OOkkO00x:;:,.. cXMMMMMMMMMMMMMMWKo.
cKMMMMMMMMMMNd. 'OWMMMMMMMMWXl. .;lddKMMMMMMMMMMW0xxxdoolkWMWWWNNNNWMMMMMMMMMMMNXNKkdo;. cXMMMMMMMMMMMMMMK;
cKMMMMMMMMMMNd. .dNMMMMMMMWO:...,'.... .cxkxddollc:lONNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXKOl:' lNMMMMMMMMMMMMMMK,
cKMMMMMMMMMMWx. ....lXMMMMMMMWO:',l0XXK0OkkxONMMMMMMWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNkoc. lNMMMMMMMMMMMMMMK,
:KMMMMMMMMMMWk' .::;,'...'xK0kkNMMMMMMMMWNXXXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKl. oWMMMMMMMMMMMMMMK,
:KMMMMMMMMMMWk' .,,,,''''.... .:dxdolc::kWWNNXXK0KNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0kkOOO0000000KKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXNXl. .dWMMMMMMMMMMMMMMK,
:KMMMMMMMMMMWO, .:;,c0NWNNNNXXK0OkkxkNMMMWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNKKXXXNNNWWWMMXdcclloddxkkOOOl. ...........................'''''''''''''''''. .:xNMMMMMMMMMMMMK,
;0MMMMMMMMMMWO, .xKXXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWWWWKxdxkO00KXXo...'',;;:ccllc, ... :XMMMMMMMMMMMMK,
;0MMMMMMMMMMW0; .oxONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXO0KXNk;;:lol, ....... cNMMMMMMMMMMMMK,
,0MMMMMMMMMMWK; .,cxKXXWMMMMMMMMMMMMMMMNKXNN0olodxOl...',. lNMMMMMMMMMMMMK,
,OMMMMMMMMMMWK: .,::lkXWWMMMMMMWWXkxkkc,;::. .lNMMMMMMMMMMMMK,
'OMMMMMMMMMMMX: ..:x0KOkkOK0xl;.... .oNMMMMMMMMMMMMK,
'OMMMMMMMMMMMXc .,..;:..,' .. 'clc;,,,;:l:... .dNMMMMMMMMMMMMK,
.kMMMMMMMMMMMNc .. .;;:,'.,dOkdxKWWNNNNNWNOxo;''. .xNMMMMMMMMMMMMK,
.kMMMMMMMMMN0x, .'...',. ','....;dkxdlc:ldONNXK0KWMMMMMMMMMMMMMMMX00xoc,. .xNMMMMMMMMMMMMK,
.xMMMMMMMMMK, .,d0KKXOc;:c. .. ... .clc;,;dKXK0OkkXMMMMWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMW0occ;. .xWMMMMMMMMMMMMK,
.xMMMMMMMMMK; .,lXMMMNXNWNOxd:'...:O0OkdoxNWWNNXNWMMMMMMWNWWWWWWMMMMMMMMMMWWWMMMMMMMMMMMMMMMMMNNXk;. .xWMMMMMMMMMMMMK,
.d0NMMMMMMMMMK: .cdONMMMMMMMMN0KKK0XWMMMMMMMMMMMMMWKO0XNNOc;::clooodxxxxxxdloxKWMMMMMMMMMMMMMMMMMMNxc. .xNMMMMMMMMMMMMK,
'0MMMMMMMMMMMXc .':OWMMMMMMMMMMMMMMWKKKXXNWNxloxOk;...,;;. .'lNMMMMMMMMMMMMMMMMMMWKo. .xNMMMMMMMMMMMMK,
'OMMMMMMMMMMMXl. .cd0WMMMMMMMMWOOX0o'..';::. ',lNMMMMMMMMMMMMMMMWWXkl;. .dNMMMMMMMMMMMMK,
.kWMMMMMMMMMMXo. .;OWMMMMMMMMNxc:'. l0XWMMMMMMMMMMMMMWKxd:.. .dWMMMMMMMMMMMMK,
.kWMMMMMMMMMMNd. .cOWMMMMMMMMWNo ,OWMMMMMMMMMMMMWKxl:. .dNMMMMMMMMMMMMK,
.xNMMMMMMMMMMNx. 'kNMMMMMMMMMXc ';xWMMMMMMMMMMMKo:,. .dNMMMMMMMMMMMMK,
.dNMMMMMMMMMMWk. 'oXMMMMMMMMK: .l0XMMMMMMMMMMMXkc. .dNMMMMMMMMMMMMK,
.oXMMMMMMMMMMWO' .oXMMMMMMMWO, .c0MMMMMMMMMMWOol, .dNMMMMMMMMMMMMK,
.lXMMMMMMMMMMW0, 'ONWMMMMMMWk. ';',l0WMMMMMMMMMNOx: .dNMMMMMMMMMMMMK,
.cKMMMMMMMMMMMK, 'coKMMMMMMWd. .'... ;xxdccOWXKKNMMMMMMMMMNOo;. .dNMMMMMMMMMMMMK,
:KMMMMMMMMMMMX; .cKMMMMMMWx'. ... .clc;,:kKKOkKWWMWWMMMMMMMMMMMMMMMXko:' .dNMMMMMMMMMMMMK,
;0MMMMMMMMMXxc. .xXWMMMMMMNKo. .::;'..,d0OxddONWNNXNMMMMMMMMMMMMMMMMMMMMMMMMMMWXkl:' .dNMMMMMMMMMMMMK,
'0MMMMMMMMMO. .clOMMMMMMMXxc:o0WNXXK0KWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXk: .dNMMMMMMMMMMMMK,
.OMMMMMMMMMO. ;0MMMMMMMMWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMNKKXXNNWWWWMMKocclloodxko. .oNMMMMMMMMMMMMK,
.:dKMMMMMMMMMO' .oXWMMMMMMMMMMMMMMMMMMWNWWMMMMMMMMKxkO0KKOc'.',;::ccll; .oNMMMMMMMMMMMMK,
;KMMMMMMMMMMM0, .:oOWMMMMMMMMWKOO0KXXNk;:KMMMMMMMM0oddl,... .oNMMMMMMMMMMMMK,
,0WMMMMMMMMMM0, ;kXWMMMMXkOk, ....',. ;0MMMMMMMMMMN0dc:,. .oNMMMMMMMMMMMMK,
'kWMMMMMMMMMM0; ,cxNMMWKd,. .lXMMMMMMMMMMMWNNNO, .oNMMMMMMMMMMMMK,
.xNMMMMMMMMMMK; 'oOKX0o, .xNMMMMMMMMMMMMMMMO' .oNMMMMMMMMMMMM0,
.dNMMMMMMMMMMK: .,,,,. 'OWMMMMMMMMMMMMMMMK: .oNMMMMMMMMMMMMK,
.lXMMMMMMMMMMK: '0MMMMMMMMMMMMMW0dkl. .oNMMMMMMMMMMMMNd.
:XMMMMMMMMMMKc. '0MMMMMMMMMMMMMWd''. .''.'',. .oNMMMMMMMMMMMMMW0;
;KMMMMMMMMMMXc. .,l0MMMMMMMMMMMM0x; .. .cc,'dXXXXXOl:c:. .oNMMMMMMMMMMMMMMNl
.cxNMMMMMMMMMMXl. .dMMMMMMMMMMWOlc'.,:;'.;k0xdkNWNNWMMMMMWNNN0kl,'. .lNMMMMMMMMMMMMMMNc
cNMMMMMMMMMMMMXo. .''....xMMMMMMMMMMNxclccxXNXKXWMMMMMMMMMMMMMMMMMMMMN0xc. .lNMMMMMMMMMMMMMMXc
;KWMMMMMMMMMMMXo. ......... .:oolcc:;,'.;ONXXKOkKMMMMMMMMMMMWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMM0c:. .lNMMMMMMMMMMMMMMX:
,OWMMMMMMMMMMMNd. ..:OXXXXK0OOkxxdooldXMMWWWWNNNXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNWWWWWMKxddkOKx;;. lNMMMMMMMMMMMMMWK;
'kWMMMMMMMMMMMNd. ;ONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXOkO0KXNWXo,;:clodo, ... lNMMMMMMMMMMMMMW0;
.dNMMMMMMMMMMMWx. .lx0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKKNMMMMMMMMMMWOoloodxkOx;. ...';,. lNMMMMMMMMMMMMMW0,
.lNMMMMMMMMMMMWk. .;dOO0WMMMMMMMMMWWWMMW0dddxkkOO0KKXNk,'kMMMMMMMMMMW0; . lNMMMMMMMMMMMMMWO'
cNMMMMMMMMMMMMO' ..;kWMMMMMMMMWkcodxd, ........ .kMMMMMMMMMMMNc cNMMMMMMMMMMMMMWk'
,d0WMMMMMMMMMMNXO' 'kWMMMMMMMMWd. .kMMMMMMMMMXd;. :XMMMMMMMMMMMMMWx.
lNMMMMMMMMMMMMk;;. .oNMMMMMMMMNO; .xMMMMMMMMMK, 'ckNMMMMMMMMMMMMMNd.
:KMMMMMMMMMMMMx. ;lxNMMMMMMMNxl, .xMMMMMMMMMK: dWMMMMMMMMMMMMMMMNo.
;0WMMMMMMMMMMMk. ;0WMMMMMMMWXd' .xMMMMMMMMMXc oNMMMMMMMMMMMMMMMNl.
'kWMMMMMMMMMMMk. ,oKWMMMMMMWXd' .xMMMMMMMMMXo. lNMMMMMMMMMMMMMMMNc
.dWMMMMMMMMMMMO' 'lKMMMMMMMMKdc. .xMMMMMMMMMNd. .'.... ..'. cXMMMMMMMMMMMMMMMXc
.'xWMMMMMMMMMMM0, ,okXMMMMMMMKoc. .xMMMMMMMMMNk,...... ;olc:;,'.:0NXK0OOkkkO00xc;... cXMMMMMMMMMMMMMMMNl..
.oKWMMMMMMMMMMMM0; ,dkXMMMMMMMW0:. .xMMMMMMMMMWXKK000Okkxddlo0MWWWNNNXNWMMMMMMMMMMMWNXOdoc;;' :KMMMMMMMMMMMMMMMMNXl
.oNMMMMMMMMMMMMMK: 'lkNMMMMMMMWO;. .''... 'dxdolc:ckNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNKK0dc. :KMMMMMMMMMMMMMMMMMWo
cKMMMMMMMMMMMMMXc. ..lKWMMMMMMMMWk:;:loc:;,'cOXK0Okx0WMMMMWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc. ;0MMMMMMMMMMMMMMMMMNl
,0MMMMMMMMMMMMMXl. .;o0WMMMMMMMMMMW0xkXMWWNNXXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0dl. ;0MMMMMMMMMMMMMMMMMXl
.kMMMMMMMMMMMMMNo. .o0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOOO000KKXXNNNNNNNXXXXXKKKK000000000000000000000ko:. ,OWMMMMMMMMMMMMMMMMXc
.cxXMMMMMMMMMMMMMWd. .oxKMMMMMMMMMMMMMMMMMMMMMMMMMMWKKXXNNWWWOlooo0MMMMMMMMO' .......'',,,,'''''............................. ,OWMMMMMMMMMMMMMMMMK:
.kWMMMMMMMMMMMMMMWx. .cx0NMMMMMMMMMMMMWWWWWMXkxk0K0l'.',;:cl' .xMMMMMMMM0, 'OWMMMMMMMMMMMMMMMMK:
.lXMMMMMMMMMMMMWKkc. .,coONMMMMMN0OKN0c;codc. .... .xMMMMMMMMK: 'kWMMMMMMMMMMMMMMMM0;
;0MMMMMMMMMMMMWo .':xXWW0doc'.,. .xMMMMMMMMXl. 'kWMMMMMMMMMMMMMMMW0;
.,;OMMMMMMMMMMMMWo. .;::c, .xMMMMMMMMNd. .kWMMMMMMMMMMMMMMMWO,
,OKNMMMMMMMMMMMMWd. .kMMMMMMMMWk. .xWMMMMMMMMMMMMMMMWO,
.xNMMMMMMMMMMMMMWx. .kMMMMMMM0do. .xWMMMMMMMMMMMMMMMWO'
.cXMMMMMMMMMMMMMWk' 'OMMMMMMMk. .dWMMMMMMMMMMMMMMMWk'
,odXMMMMMMMMMMMMMWO, ,0MMMMMMM0; .oWMMMMMMMMMMMMMMMWk'
;0WMMMMMMMMMMMMMMM0; :KMMMMMMNKo. ..oWMMMMMMMMMMMMMMMWk.
.xNMMMMMMMMMMMMMMMXc .lXMMMMMWkc;. .oxOWMMMMMMMMMMMMMMMWx.
..lNMMMMMMMMMMMMMMMNl .d0XMMMWKk: .xWMMMMMMMMMMMMMMMMMWx.
ck0WMMMMMMMMMMMMMNkdc ';xWMWKd:. .oNMMMMMMMMMMMMMMMMMWd.
,OWMMMMMMMMMMMMMMNc. ;dOXKo:. .:c:;,,'''''',;;;. :KMMMMMMMMMMMMMMMMMWd.
.'dWMMMMMMMMMMMMMMNd. ..;dxl. ','''........ .dkxxdolc:;dNWWNNNXXXXXXNNNOlcol..'. ,OMMMMMMMMMMMMMMMMMWd.
.dOKMMMMMMMMMMMMMMMWk' ....... .:;. .cdoolllcc::;;,,''.,dKNNXXXKK00OOOkxxONMMMMMMMWWMMMMMMMMMMMMMMMMWWWMN0Okoc;. .;OMMMMMMMMMMMMMMMMMWo.
.lXMMMMMMMMMMMMMMMMW0; .;cc::;;,,''...... ,kKK00OOkkxxddoollxNMMMWWWWWWWNNNNNXXXXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNXkc,,..dKWMMMMMMMMMMMMMMMMMWo.
;0MMMMMMMMMMMMMMMMMX: .','''....... .cxkkxxddoollcc::;ckNMWWWNNNXXXKKK000KWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWK0OolxXMMMMMMMMMMMMMMMMMMWo.
.kMMMMMMMMMMMMMMMNOxo;,''..lXWNNXXKK000OOkkxxdONMMMMMMMMMMMWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWMMMMMMMMMMMMMMMMMMMWd.
.,,xMMMMMMMMMMMMMMMWNNNNNNNXXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNNNWWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWd.
'OXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMW0kOOO000KKXXXNNNNWWWWO;',,;;:::cclllloooollllllldKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWx.
.xKNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNKKKXXXXNNNNWWWWWMM0l:cclllooddxxkkkOO00x' .......''',,,;;;:. .cx0WMMMMMMMMMMMMMMMMMMMMMMMMMMMWk'
.,OMMMMMMMMMMMMMMMMWNWMMMMMMMMMMMMMMMMMMMMWWNNWWWWWWWWWMMMMMNkooddxxkkOO000KKXXNXo.....'',,;;;::ccll, ... .cx0WMMMMMMMMMMMMMMMMMMMMMMMMMMW0;
cKMMMMMMMMMMMMMMMNOx0NXK00OO0000KKXXXNNWWKl',;;::cclloooddxl. ............ .:d0WMMMMMMMMMMMMMMMMMMMMMMMMWKOl
'oKWMMMMMMMMMMMMKddl;,.............'',,;;,. .:d0WMMMMMMMMMMMMMMMMMMMMMMMNo,.
.l0WMMMMMMMMMMM0,. .:o0WMMMMMMMMMMMMMMMMMMMMMMNO,
.lKWMMMMMMMMMMXc. .:o0MMMMMMMMMMMMMMMMMMMMMXxo,
.lxXMMMMMMMMNKx. .:d0WMMMMMMMMMMMMMMMMMMKdc.
.,xXWMMMMMWkc;. .:oOWMMMMMMMMMMMMMMMMKd:.
;dkKWMMWkc, .,xNMMMMMMMMMMMMMMMXx:.
.:lxKkoo; .:kNMMMMMMMMMMMMMNx;'
.;;. 'okXMMMMMMMMMMN0k:
,oONMMMMMWK0klc,.
.,cd0XXNXkc,.
.,;,',;.
`;
export const bitverseArt = `
.*/////*
.((((((/
.((((((/
...... .((((((/ .......
*((((((* .((((((/ .(((((((.
*((((((*. .((((((/ .(((((((.
....... *((((((*. .((((((/ ,(((((((. ......
,(((((((* *((((((*. .((((((/ ,(((((((. ,(((((((/.
,(((((((/ *((((((*. .((((((/ ,(((((((. ,(((((((/.
,(((((((/ *((((((*. .((((((/ ,(((((((. ,(((((((/.
*((((((*. ,(((((((/ *((((((*. .((((((/ ,(((((((. ,(((((((/. ,(((((((.
*((((((*. ,(((((((* *((((((*. .((((((/ ,(((((((. ,(((((((/. ,(((((((.
*((((((*. ,(((((((/ *((((((*. .((((((/ ,(((((((. ,(((((((/. ,(((((((.
/((((((, *((((((*. ,(((((((/ *((((((*. .((((((/ ,(((((((. ,(((((((/. ,(((((((. .*((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. .((((((/ ,(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. ,((((((/. ,(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. .*((((((((/, ,(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. .*((((((((((/, ,(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. ,/(((((((((((((*. ,(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. ,((((((((((((((((*. ,(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((/. ,(((((((/ *((((((*. .*(((((((((((((((((((, .(((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((*. ./(((((((((((((((((((((, ,/((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *(((((((/*.*((((((((/(((((((((((((((/,,/((((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ *((((((((((((((((((*,((((((/,/((((((((((((((((((. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ .*((((((((((((((/,..((((((/..*((((((((((((((/, ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ .*(((((((((((/. .((((((/. *(((((((((((/,. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ ./((((((((*. .((((((/ ./((((((((,. ,(((((((/. ,(((((((. ./((((((,
.(((((((, *((((((*. ,(((((((/ .*((((((((,. .((((((/ ./(((((((/, ,(((((((/. ,(((((((. ./((((((,
.(((((((, .*(((((((/. ,(((((((/ .((((((((/. .((((((/ ,(((((##(/. ,(((((((/. ,((((((((,. ./((((((,
.(((((((, ./(((((((((*. ,(((((((/. ,((((((((*. .((((((/ *((((((((*. ,(((((((/. ,(((((((((/,. ./((((((,
.(((((((, ,/(#(((((((/. ,((((((((/(((((((((((((((((((((, .((((((/ ./(((((((((((((((((((((/(((((((/. .,((((((((((*. ./((((((,
.(((((((, ,/(((((((((/, ,((((((((((((((((((#((((((((((, .((((((/ .*((((((((((((((((((((((((((((/. .*((((((((((*. ./((((((,
.(((((((,..*((((((((((*. .,(((((((((((((((((((((((((((/. .((((((/ .,(((((((((((((((((((((((((((/. ,/(((((((((/.../((((((,
.(((((((**((((((((((/. ,(((((((((((((((((((((((((((*. .((((((/ ,(((((((((((((((((((((((((((/. ,((((((((((/*/((((((,
.(((((((((((((((#/,. ,/(((((((((/,. ./((((((*. .((((((/ ,(((((((,. .*((((((((((*. .*((((((((((((((((,
.(((((((((((((((*. .*((((((((#(*. /((((((*. .((((((/ ,(((((((. ,/(((((((((/,. ,/((((((((((((((,
.((((((((((((/. ,/(((((((((/. /((((((*. .((((((/ ,(((((((. .*((((((((((*. .*((((((((((((,
.((((((((((/, ,/(((((((((/,. *((((((*. .((((((/ .(((((((. .*((((((((((*. .*((((((((((,
.((((((((*. .*((((((((((*. *((((((*. .((((((/ ,(((((((. ,/(((((((((/, ,/(((((((,
.(((((((,. .,/(((((((((/. *((((((*. .((((((/ ,(((((((. .,((((((((((*. ./((((((,
.(((((#(, ./((((((((((,. *((((((*. .((((((/ ,(((((((. ./((((((((((,. ./((((((,
.(((((((, .*((((((((#(*. *((((((*. .((((((/ ,(((((((. ./(((((((((/, .*((((((,
.(((((((, ,/(((((((((/. *((((((*. .((((((/ ,(((((((. .*((((((((((*. ./((((((,
.(((((((, ./(((((((((/, *((((((*. .((((((/ ,(((((((. .*((((((((((* ./((((((,
.(((((((, *((((((((*. ./(((((((*. .(((#((/ ,((((((((*. ,/((((((((. ./((((((,
.(((((((, *((((((/, ,/((((((((, .(((#((/ ./((((((((*. .*(((((((. ./((((((,
.(((((((, *((((((/. ./((((((((*. .((((((/. ,(((((((((,. ,(((((((. ./((((((,
.(((((((, *((((((*. ,/((((((((*. ,((((((((*. ,(((((((((*. ,((#((((. ./((((((,
.(((((((, *((((((/. .*((((((((/. .*(#(((((((#/, ,((((((((/, ,(((((((. ./((((((,
.(((((((, *((((((/. ,/((((((((*. ,/(((((((((((((*. ,/((((((((,. ,(((((((. ./((((((,
.(((((((, *((((((/. .*((((((((/. *((((((((((((((((/. *((((((((/,......................*(((((((. ./((((((,
.(((((((, *((((((*. ./((((((((*. .*(((((((/,,/((((((((, ,(((((((((((((((((((((((((((((((((((((((. ./((((((,
.(((((((,. *((((((((((((((((((((((((((((((((((((/. .*((((((((/. .,(((((((#/, .*(((((((((((((((((((((((((((((((((((((. ./((((((,
.(((((((/,. *(((((((((((((((((((((((((((((((((((/. .*((((((#(*. ,(((((((((, .*((((((((((/(((((((((((((((((((((((((. .*(((((((,
.((((((((#(,. *((((((((((((((((((((((((((((((((((,. ,((((((((/, .*#(((((((*. ./((((((/. .,(((((((. .*(#(((((((,
.*((((((((((, *((((((/*,,,,,,,,,,,,,,,*,,*((((((/. .*((((((((*. ,((((((((/, .*((((((/ ,(((((((. ./(((((((((/,.
,/(((((((((/. *((((((*. .((((((/. ./(((((((/, .*((((((((, *((((((/ ,(((((((. .*((((((((((*.
.,((((((((((/. *((((((*. .((((((/. .(((((((/. .,(((((((, *((((((/ ,(((((((. .*((((((((((/.
,/((((((((#/,*((((((*. .((((((/. /((((((, ./((((((, .*((((((/ ,(((((((,*((((((((((*.
,/((((((((((((((((*. .((((((/. /((((((, ./((((((, .*((((((/ ,(((((((((((((((((*.
./#(((((((((((((*. .((((((/. /((((((, ./((((((, .*((((((/ ,(((((((((((((((,.
.*((((((((((((*. .((((((/. /((((((, ./((((((, .*((((((/ ,((((((((((((/,
.*((((((((((*. .((((((/. /((((((, ./((((((, .*((((((/ ,((((((((((/,.
.,/(((((((*. .((((((/. ./((((((, ./((((((, .*((((((/ ,((((((((*,
.*((((((*. .((((((/. ./((((((*. ./((((((, .*((((((/ ,(((((((.
*((((((*. .((((((/. ./((((((*. .,/((((((, .*((((((/ ,(((((((.
*((((((*. .((((((/. ./(((((((/. .*((((((((, .*((((((/ ,(((((((.
*((((((/. .((((((/. ./(((((((((/,. .*((((((((((. *((((((/ ,(((((((.
*((((((*. .((((((/. ,/((((((((#/. .*((((((((((*. *((((((/ .,(((((((.
*(((((((/, .((((((/. .*((((((((((*..,/(((((((((/, .*((((((/ .*((((((((.
,((((((((((*. .((((((/. ,/(((((((((((((((((((*. .*((((((/ ./(((((((((/.
.*((((((((#(,. .((((((/. ,/(((((((((((((#(*. .*((((((/ ./(((((((((/,
.*((((((((((*. .((((((/. ,/(((((((((((*. .*((((((/ ./(((((((((/,
.,((((((((((*. .((((((/. .*(((((((,. .*((((((/ .,/(((((((((/.
.*((((((((((*. .((((((/. .((((((/ .*((((((/ ,/(((((((((/,
,/(((((((*. .((((((/. .((((((/ *((((((/ ./(((((((*.
.,/(((*. ..... .... .... ,/(((*.
.. ..
`;
+344
View File
@@ -0,0 +1,344 @@
import {
bitverseBoardShape,
Board,
BoardState,
Move,
Neighbor,
opponents,
PlayerColor,
playerColors,
PointState,
validityReason,
} from "./goConstants";
import { getExpansionMoveArray } from "../boardAnalysis/goAI";
import {
evaluateIfMoveIsValid,
findAllCapturedChains,
findLibertiesForChain,
getAllChains,
getBoardFromSimplifiedBoardState,
} from "../boardAnalysis/boardAnalysis";
import { endGoGame } from "../boardAnalysis/scoring";
import { cloneDeep } from "lodash";
import { addObstacles, resetCoordinates, rotate90Degrees } from "./offlineNodes";
/**
* Generates a new BoardState object with the given opponent and size
*/
export function getNewBoardState(
boardSize: number,
ai = opponents.Netburners,
applyObstacles = false,
boardToCopy?: Board,
): BoardState {
if (ai === opponents.w0r1d_d43m0n) {
boardToCopy = resetCoordinates(rotate90Degrees(getBoardFromSimplifiedBoardState(bitverseBoardShape).board));
}
const newBoardState = {
history: [],
previousPlayer: playerColors.white,
ai: ai,
passCount: 0,
cheatCount: 0,
board: Array.from({ length: boardSize }, (_, x) =>
Array.from({ length: boardSize }, (_, y) =>
!boardToCopy || boardToCopy?.[x]?.[y]
? {
player: boardToCopy?.[x]?.[y]?.player ?? playerColors.empty,
chain: "",
liberties: null,
x,
y,
}
: null,
),
),
};
if (applyObstacles) {
addObstacles(newBoardState);
}
const handicap = getHandicap(newBoardState.board[0].length, ai);
if (handicap) {
applyHandicap(newBoardState, handicap);
}
return newBoardState;
}
/**
* Determines how many starting pieces the opponent has on the board
*/
export function getHandicap(boardSize: number, opponent: opponents) {
// Illuminati and WD get a few starting routers
if (opponent === opponents.Illuminati || opponent === opponents.w0r1d_d43m0n) {
return ceil(boardSize * 0.35);
}
return 0;
}
/**
* Make a new move on the given board, and update the board state accordingly
*/
export function makeMove(boardState: BoardState, x: number, y: number, player: PlayerColor) {
// Do not update on invalid moves
const validity = evaluateIfMoveIsValid(boardState, x, y, player, false);
if (validity !== validityReason.valid || !boardState.board[x][y]?.player) {
console.debug(`Invalid move attempted! ${x} ${y} ${player} : ${validity}`);
return false;
}
boardState.history.push(getBoardCopy(boardState).board);
boardState.history = boardState.history.slice(-4);
const point = boardState.board[x][y];
if (!point) {
return false;
}
point.player = player;
boardState.previousPlayer = player;
boardState.passCount = 0;
return updateCaptures(boardState, player);
}
/**
* Pass the current player's turn without making a move.
* Ends the game if this is the second pass in a row.
*/
export function passTurn(boardState: BoardState, player: playerColors, allowEndGame = true) {
if (boardState.previousPlayer === null || boardState.previousPlayer === player) {
return;
}
boardState.previousPlayer =
boardState.previousPlayer === playerColors.black ? playerColors.white : playerColors.black;
boardState.passCount++;
if (boardState.passCount >= 2 && allowEndGame) {
endGoGame(boardState);
}
}
/**
* Makes a number of random moves on the board before the game starts, to give one player an edge.
*/
export function applyHandicap(boardState: BoardState, handicap: number) {
const availableMoves = getEmptySpaces(boardState);
const handicapMoveOptions = getExpansionMoveArray(boardState, playerColors.black, availableMoves);
const handicapMoves: Move[] = [];
// select random distinct moves from the move options list up to the specified handicap amount
for (let i = 0; i < handicap && i < handicapMoveOptions.length; i++) {
const index = floor(Math.random() * handicapMoveOptions.length);
handicapMoves.push(handicapMoveOptions[index]);
handicapMoveOptions.splice(index, 1);
}
handicapMoves.forEach((move: Move) => {
const point = boardState.board[move.point.x][move.point.y];
return move.point && point && (point.player = playerColors.white);
});
return updateChains(boardState);
}
/**
* Finds all groups of connected stones on the board, and updates the points in them with their
* chain information and liberties.
*/
export function updateChains(boardState: BoardState, resetChains = true) {
resetChains && clearChains(boardState);
for (let x = 0; x < boardState.board.length; x++) {
for (let y = 0; y < boardState.board[x].length; y++) {
const point = boardState.board[x][y];
// If the current point is already analyzed, skip it
if (!point || point.chain !== "") {
continue;
}
const chainMembers = findAdjacentPointsInChain(boardState, x, y);
const libertiesForChain = findLibertiesForChain(boardState, chainMembers);
const id = `${point.x},${point.y}`;
chainMembers.forEach((member) => {
member.chain = id;
member.liberties = libertiesForChain;
});
}
}
return boardState;
}
/**
* Assign each point on the board a chain ID, and link its list of 'liberties' (which are empty spaces
* adjacent to some point on the chain including the current point).
*
* Then, remove any chains with no liberties.
*/
export function updateCaptures(initialState: BoardState, playerWhoMoved: PlayerColor, resetChains = true): BoardState {
const boardState = updateChains(initialState, resetChains);
const chains = getAllChains(boardState);
const chainsToCapture = findAllCapturedChains(chains, playerWhoMoved);
if (!chainsToCapture?.length) {
return boardState;
}
chainsToCapture?.forEach((chain) => captureChain(chain));
return updateChains(boardState);
}
/**
* Removes a chain from the board, after being captured
*/
function captureChain(chain: PointState[]) {
chain.forEach((point) => {
point.player = playerColors.empty;
point.chain = "";
point.liberties = [];
});
}
/**
* Removes the chain data from given points, in preparation for being recalculated later
*/
function clearChains(boardState: BoardState): BoardState {
for (const x in boardState.board) {
for (const y in boardState.board[x]) {
const point = boardState.board[x][y];
if (point && point.chain && point.liberties) {
point.chain = "";
point.liberties = null;
}
}
}
return boardState;
}
/**
* Finds all the pieces in the current continuous group, or 'chain'
*
* Iteratively traverse the adjacent pieces of the same color to find all the pieces in the same chain,
* which are the pieces connected directly via a path consisting only of only up/down/left/right
*/
export function findAdjacentPointsInChain(boardState: BoardState, x: number, y: number) {
const point = boardState.board[x][y];
if (!point) {
return [];
}
const checkedPoints: PointState[] = [];
const adjacentPoints: PointState[] = [point];
const pointsToCheckNeighbors: PointState[] = [point];
while (pointsToCheckNeighbors.length) {
const currentPoint = pointsToCheckNeighbors.pop();
if (!currentPoint) {
break;
}
checkedPoints.push(currentPoint);
const neighbors = findNeighbors(boardState, currentPoint.x, currentPoint.y);
[neighbors.north, neighbors.east, neighbors.south, neighbors.west]
.filter(isNotNull)
.filter(isDefined)
.forEach((neighbor) => {
if (neighbor && neighbor.player === currentPoint.player && !contains(checkedPoints, neighbor)) {
adjacentPoints.push(neighbor);
pointsToCheckNeighbors.push(neighbor);
}
checkedPoints.push(neighbor);
});
}
return adjacentPoints;
}
/**
* Finds all empty spaces on the board.
*/
export function getEmptySpaces(boardState: BoardState): PointState[] {
const emptySpaces: PointState[] = [];
boardState.board.forEach((column) => {
column.forEach((point) => {
if (point && point.player === playerColors.empty) {
emptySpaces.push(point);
}
});
});
return emptySpaces;
}
/**
* Makes a deep copy of the given board state
*/
export function getStateCopy(initialState: BoardState) {
const boardState = cloneDeep(initialState);
boardState.history = [...initialState.history];
boardState.previousPlayer = initialState.previousPlayer;
boardState.ai = initialState.ai;
boardState.passCount = initialState.passCount;
return boardState;
}
/**
* Makes a deep copy of the given BoardState's board
*/
export function getBoardCopy(boardState: BoardState) {
const boardCopy = getNewBoardState(boardState.board[0].length);
const board = boardState.board;
for (let x = 0; x < board.length; x++) {
for (let y = 0; y < board[x].length; y++) {
const pointToEdit = boardCopy.board[x][y];
const point = board[x][y];
if (!point || !pointToEdit) {
boardCopy.board[x][y] = null;
} else {
pointToEdit.player = point.player;
}
}
}
return boardCopy;
}
export function contains(arr: PointState[], point: PointState) {
return !!arr.find((p) => p && p.x === point.x && p.y === point.y);
}
export function findNeighbors(boardState: BoardState, x: number, y: number): Neighbor {
const board = boardState.board;
return {
north: board[x]?.[y + 1],
east: board[x + 1]?.[y],
south: board[x]?.[y - 1],
west: board[x - 1]?.[y],
};
}
export function getArrayFromNeighbor(neighborObject: Neighbor): PointState[] {
return [neighborObject.north, neighborObject.east, neighborObject.south, neighborObject.west]
.filter(isNotNull)
.filter(isDefined);
}
export function isNotNull<T>(argument: T | null): argument is T {
return argument !== null;
}
export function isDefined<T>(argument: T | undefined): argument is T {
return argument !== undefined;
}
export function floor(n: number) {
return ~~n;
}
export function ceil(n: number) {
const floored = floor(n);
return floored === n ? n : floored + 1;
}
+311
View File
@@ -0,0 +1,311 @@
import { getNewBoardState } from "./boardState";
import { FactionName } from "@enums";
export enum playerColors {
white = "White",
black = "Black",
empty = "Empty",
}
export enum validityReason {
pointBroken = "That node is offline; a piece cannot be placed there",
pointNotEmpty = "That node is already occupied by a piece",
boardRepeated = "It is illegal to repeat prior board states",
noSuicide = "It is illegal to cause your own pieces to be captured",
notYourTurn = "It is not your turn to play",
gameOver = "The game is over",
invalid = "Invalid move",
valid = "Valid move",
}
export enum opponents {
none = "No AI",
Netburners = FactionName.Netburners,
SlumSnakes = FactionName.SlumSnakes,
TheBlackHand = FactionName.TheBlackHand,
Tetrads = FactionName.Tetrads,
Daedalus = FactionName.Daedalus,
Illuminati = FactionName.Illuminati,
w0r1d_d43m0n = "????????????",
}
export const opponentList = [
opponents.Netburners,
opponents.SlumSnakes,
opponents.TheBlackHand,
opponents.Tetrads,
opponents.Daedalus,
opponents.Illuminati,
];
export const opponentDetails = {
[opponents.none]: {
komi: 5.5,
description: "Practice Board",
flavorText: "Practice on a subnet where you place both colors of routers.",
bonusDescription: "",
bonusPower: 0,
},
[opponents.Netburners]: {
komi: 1.5,
description: "Easy AI",
flavorText:
"The Netburners faction are a mysterious group with only the most tenuous control over their subnets. Concentrating mainly on their hacknet server business, IPvGO is not their main strength.",
bonusDescription: "increased hacknet production",
bonusPower: 1.3,
},
[opponents.SlumSnakes]: {
komi: 3.5,
description: "Spread AI",
flavorText:
"The Slum Snakes faction are a small-time street gang who turned to organized crime using their subnets. They are known to use long router chains snaking across the subnet to encircle territory.",
bonusDescription: "crime success rate",
bonusPower: 1.2,
},
[opponents.TheBlackHand]: {
komi: 3.5,
description: "Aggro AI",
flavorText:
"The Black Hand faction is a black-hat hacking group who uses their subnets to launch targeted DDOS attacks. They are famous for their unrelenting aggression, surrounding and strangling any foothold their opponents try to establish.",
bonusDescription: "hacking money",
bonusPower: 0.9,
},
[opponents.Tetrads]: {
komi: 5.5,
description: "Martial AI",
flavorText:
"The faction known as Tetrads prefers to get up close and personal. Their combat style excels at circling around and cutting through their opponents, both on and off of the subnets.",
bonusDescription: "strength, dex, and agility levels",
bonusPower: 0.7,
},
[opponents.Daedalus]: {
komi: 5.5,
description: "Mid AI",
flavorText:
"Not much is known about this shadowy faction. They do not easily let go of subnets that they control, and are known to lease IPvGO cycles in exchange for reputation among other factions.",
bonusDescription: "reputation gain",
bonusPower: 1.1,
},
[opponents.Illuminati]: {
komi: 7.5,
description: "Hard AI",
flavorText:
"The Illuminati are thought to only exist in myth. Said to always have prepared defenses in their IPvGO subnets. Provoke them at your own risk.",
bonusDescription: "faster hack(), grow(), and weaken()",
bonusPower: 0.7,
},
[opponents.w0r1d_d43m0n]: {
komi: 9.5,
description: "???",
flavorText: "What you have seen is only the shadow of the truth. It's time to leave the cave.",
bonusDescription: "hacking level",
bonusPower: 2,
},
};
export const boardSizes = [5, 7, 9, 13];
export type PlayerColor = playerColors.white | playerColors.black | playerColors.empty;
export type Board = (PointState | null)[][];
export type MoveOptions = {
capture: Move | null;
defendCapture: Move | null;
eyeMove: EyeMove | null;
eyeBlock: EyeMove | null;
pattern: PointState | null;
growth: Move | null;
expansion: Move | null;
jump: Move | null;
defend: Move | null;
surround: Move | null;
corner: PointState | null;
random: PointState | null;
};
export type Move = {
point: PointState;
oldLibertyCount: number | null;
newLibertyCount: number | null;
};
export type EyeMove = {
point: PointState;
createsLife: boolean;
};
export type BoardState = {
board: Board;
previousPlayer: PlayerColor | null;
history: Board[];
ai: opponents;
passCount: number;
cheatCount: number;
};
export type PointState = {
player: PlayerColor;
chain: string;
liberties: (PointState | null)[] | null;
x: number;
y: number;
};
/**
* "invalid" or "move" or "pass" or "gameOver"
*/
export enum playTypes {
invalid = "invalid",
move = "move",
pass = "pass",
gameOver = "gameOver",
}
export type Play = {
success: boolean;
type: playTypes;
x: number;
y: number;
};
export type Neighbor = {
north: PointState | null;
east: PointState | null;
south: PointState | null;
west: PointState | null;
};
export type goScore = {
White: { pieces: number; territory: number; komi: number; sum: number };
Black: { pieces: number; territory: number; komi: number; sum: number };
};
export const columnIndexes = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
type opponentHistory = {
wins: number;
losses: number;
nodes: number;
nodePower: number;
winStreak: number;
oldWinStreak: number;
highestWinStreak: number;
favor: number;
};
export function getGoPlayerStartingState(): {
previousGameFinalBoardState: BoardState | null;
boardState: BoardState;
status: { [o in opponents]: opponentHistory };
} {
const previousGame: BoardState | null = null;
return {
boardState: getNewBoardState(7),
status: {
[opponents.none]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.Netburners]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.SlumSnakes]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.TheBlackHand]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.Tetrads]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.Daedalus]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.Illuminati]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
[opponents.w0r1d_d43m0n]: {
wins: 0,
losses: 0,
nodes: 0,
nodePower: 0,
winStreak: 0,
oldWinStreak: 0,
highestWinStreak: 0,
favor: 0,
},
},
previousGameFinalBoardState: previousGame,
};
}
export const bitverseBoardShape = [
"########...########",
"######.#...#.######",
"###.#..#...#..#.###",
".#..#..#...#..#..#.",
".#.....#...#.....#.",
"...................",
"...................",
"...................",
"...................",
".....##.....##.....",
"....###.....###....",
"....##.......##....",
"....#.........#....",
".........#.........",
"#........#........#",
"##.......#.......##",
"##.......#.......##",
"###.............###",
"####...........####",
];
+605
View File
@@ -0,0 +1,605 @@
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
export const pointStyle = makeStyles((theme: Theme) =>
createStyles({
hover: {},
valid: {},
priorPoint: {},
point: {
position: "relative",
height: "100%",
width: "100%",
"&$hover$valid:hover $innerPoint": {
outlineColor: theme.colors.white,
},
"&$hover$priorPoint $innerPoint": {
outlineColor: theme.colors.white,
},
"&$hover$priorPoint $priorStoneTrad$blackPoint": {
outlineColor: theme.colors.white,
display: "block",
},
"&$hover$priorPoint $priorStoneTrad$whitePoint": {
outlineColor: theme.colors.black,
display: "block",
},
"&$hover:hover $coordinates": {
display: "block",
},
"&:hover $broken": {
opacity: "0.4",
},
},
broken: {
backgroundImage: "repeating-radial-gradient(circle at 17% 32%, white, black 0.00085px)",
backgroundPosition: "center",
animation: `$static 5s linear infinite`,
opacity: "0",
margin: "8px",
borderRadius: "4px",
width: "83%",
height: "83%",
transition: "all 0.3s",
"& $coordinates": {
fontSize: "10px",
},
},
"@keyframes static": {
from: {
backgroundSize: "100% 100%",
},
to: {
backgroundSize: "200% 200%",
},
},
hideOverflow: {
overflow: "hidden",
},
traditional: {
"& $innerPoint": {
display: "none",
},
"& $broken": {
backgroundImage: "none",
backgroundColor: "black",
},
"& $tradStone": {
display: "block",
},
"& $liberty": {
backgroundColor: "black",
transition: "none",
"&:not($northLiberty):not($southLiberty):not($eastLiberty):not($westLiberty)": {
width: 0,
height: 0,
},
},
"& $northLiberty, & $southLiberty": {
width: "0.9px",
},
"& $eastLiberty, & $westLiberty": {
height: "0.9px",
},
"&$nineteenByNineteen": {
"& $blackPoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(30px, 5vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
},
"& $whitePoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(30px, 5vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
"& $coordinates": {
fontSize: "0.9vw",
},
},
"&$thirteenByThirteen": {
"& $blackPoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(40px, 6vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
},
"& $whitePoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(40px, 6vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
"& $coordinates": {
fontSize: "0.9vw",
},
},
"&$nineByNine": {
"& $blackPoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(60px, 7vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
},
"& $whitePoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(60px, 7vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
},
"&$sevenBySeven": {
"& $blackPoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(80px, 8vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
},
"& $whitePoint": {
"&:before": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(80px, 8vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
},
"& $coordinates": {
color: "black",
left: "15%",
},
"& $blackPoint ~ $coordinates": {
color: "white",
},
},
fiveByFive: {
"& $blackPoint": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(35px, 4vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
"& $whitePoint": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(35px, 4vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
sevenBySeven: {
"& $blackPoint": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(23px, 3vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
"& $whitePoint": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(25px, 3vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
nineByNine: {
"& $filledPoint": {
boxShadow: "0px 0px 30px hsla(0, 100%, 100%, 0.48)",
},
"& $blackPoint": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(15px, 2vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
"& $whitePoint": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(15px, 2vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
thirteenByThirteen: {
"& $filledPoint": {
boxShadow: "0px 0px 18px hsla(0, 100%, 100%, 0.48)",
},
"& $blackPoint": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(10px, 1.5vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
"& $whitePoint": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(10px, 1.5vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
nineteenByNineteen: {
"& $filledPoint": {
boxShadow: "0px 0px 18px hsla(0, 100%, 100%, 0.48)",
},
"& $blackPoint": {
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(10px, 1.5vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
"& $whitePoint": {
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(10px, 1.5vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
"& $innerPoint": {
width: "70%",
height: "70%",
margin: "15%",
},
},
tradStone: {
display: "none",
borderRadius: "50%",
margin: 0,
"&:before": {
zIndex: 2,
borderRadius: "50%",
bottom: 0,
content: '" "',
display: "block",
left: 0,
position: "absolute",
right: 0,
top: 0,
},
"&:after": {
boxShadow: "2.5px 4px 0.5em hsla(0, 0%, 0%, 0.5)",
zIndex: 1,
borderRadius: "50%",
bottom: 0,
content: '" "',
display: "block",
left: 0,
position: "absolute",
right: 0,
top: 0,
},
"&$blackPoint": {
position: "static",
outlineWidth: 0,
width: 0,
height: 0,
margin: 0,
"&:before": {
backgroundColor: "black",
backgroundImage:
"linear-gradient(145deg, transparent, black 65%), radial-gradient(calc(min(150px, 11vw)) at 42% 38%, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.25) 35%, transparent 36%)",
},
},
"&$whitePoint": {
backgroundColor: "transparent",
width: 0,
height: 0,
margin: 0,
"&:before": {
backgroundColor: "hsla(0, 0%, 90%, 1)",
backgroundImage:
"linear-gradient(145deg, transparent, white 65%), radial-gradient(calc(min(150px, 11vw)) at 42% 38%, white 0%, white 35%, transparent 36%)",
},
},
"&$emptyPoint": {
width: 0,
height: 0,
margin: 0,
backgroundColor: "transparent",
"&:before": {
display: "none",
},
"&:after": {
display: "none",
},
},
},
innerPoint: {
outlineStyle: "solid",
outlineWidth: "1px",
outlineColor: "transparent",
borderRadius: "50%",
width: "50%",
height: "50%",
margin: "25%",
position: "absolute",
},
emptyPoint: {
width: "10%",
height: "10%",
margin: "45%",
backgroundColor: "white",
position: "relative",
},
filledPoint: {
outlineStyle: "solid",
outlineWidth: "1px",
borderRadius: "50%",
position: "relative",
boxShadow: "0px 0px 40px hsla(0, 100%, 100%, 0.48)",
},
whitePoint: {
width: "70%",
height: "70%",
margin: "15%",
backgroundColor: "hsla(0, 0%, 85%, 1)",
outlineStyle: "none",
},
blackPoint: {
width: "70%",
height: "70%",
margin: "15%",
backgroundColor: "black",
outlineColor: "white",
},
fadeLoopAnimation: {
animation: `$fadeLoop 800ms ${theme.transitions.easing.easeInOut} infinite alternate`,
},
"@keyframes fadeLoop": {
"0%": {
opacity: 0.4,
},
"100%": {
opacity: 1,
},
},
liberty: {
position: "absolute",
transition: "all 0.5s ease-out",
backgroundColor: "transparent",
width: "2%",
height: "2%",
top: "50%",
left: "50%",
},
libertyWhite: {
backgroundColor: theme.colors.cha,
},
libertyBlack: {
backgroundColor: theme.colors.success,
},
northLiberty: {
width: "2%",
height: "54%",
top: "-3%",
left: "50%",
},
southLiberty: {
width: "2%",
height: "50%",
top: "50%",
left: "50%",
},
eastLiberty: {
width: "50%",
height: "2%",
top: "50%",
left: "50%",
},
westLiberty: {
width: "50%",
height: "2%",
top: "50%",
left: "0",
},
coordinates: {
color: "white",
fontFamily: `"Lucida Console", "Lucida Sans Unicode", "Fira Mono", Consolas, "Courier New", Courier, monospace, "Times New Roman"`,
fontSize: "calc(min(1.3vw, 12px))",
display: "none",
position: "relative",
top: "15%",
left: "8%",
zIndex: "10",
userSelect: "none",
},
priorStoneTrad: {
display: "none",
outlineStyle: "solid",
outlineWidth: "4px",
outlineColor: "transparent",
borderRadius: "50%",
width: "50%",
height: "50%",
margin: "25%",
background: "none",
position: "absolute",
zIndex: "10",
},
}),
);
export const boardStyles = makeStyles((theme: Theme) =>
createStyles({
tab: {
paddingTop: 0,
paddingBottom: 0,
whiteSpace: "pre",
height: "50px",
minHeight: "unset",
width: "210px",
},
gameboardWrapper: {
position: "relative",
width: "calc(min(100vw - 250px, 90vh - 150px, 800px))",
height: "calc(min(100vw - 250px, 90vh - 150px, 800px))",
padding: "5px",
},
boardFrame: {
position: "relative",
width: "752px",
},
statusPageGameboard: {
position: "relative",
width: "calc(min(400px, max(60vw - 250px, 300px)))",
height: "calc(min(400px, max(60vw - 250px, 300px)))",
},
historyPageGameboard: {
position: "relative",
width: "calc(min(300px, max(60vw - 250px, 250px)))",
height: "calc(min(300px, max(60vw - 250px, 250px)))",
},
statusPageScore: {
width: "400px",
paddingLeft: "20px",
},
factionStatus: {
padding: "10px",
margin: "10px",
borderWidth: "1px",
borderStyle: "solid",
borderColor: theme.colors.success,
width: "320px",
},
board: {
width: "100%",
height: "100%",
position: "relative",
},
traditional: {
backgroundColor: "#ca973e",
},
opponentName: {
paddingTop: "3px",
paddingBottom: "5px",
paddingRight: "10px",
},
opponentLabel: {
padding: "3px 10px 5px 10px",
},
opponentTitle: {
padding: "10px 0 0 0",
},
flavorText: {
minHeight: "120px",
padding: "0px 12px",
},
link: {
textDecoration: "underline",
opacity: 0.7,
cursor: "pointer",
"&:hover": {
opacity: 1,
},
},
inlineFlexBox: {
display: "inline-flex",
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
},
resetBoard: {
width: "200px",
},
buttonHighlight: {
borderStyle: "solid",
borderWidth: "8px",
borderColor: theme.colors.success,
padding: "0 12px",
width: "200px",
animation: `$fadeLoop 600ms ${theme.transitions.easing.easeInOut} infinite alternate`,
},
"@keyframes fadeLoop": {
"0%": {
opacity: 0.6,
},
"100%": {
opacity: 1,
},
},
scoreBox: {
display: "inline-flex",
flexDirection: "row",
whiteSpace: "pre",
padding: "10px 30px",
},
searchBox: {
maxWidth: "550px",
minHeight: "460px",
},
fiveByFive: {
height: "20%",
"& $fiveByFive": {
width: "20%",
height: "100%",
},
},
sevenBySeven: {
height: "14%",
"& $sevenBySeven": {
width: "14%",
height: "100%",
},
},
nineByNine: {
height: "11%",
"& $nineByNine": {
width: "11%",
height: "100%",
},
},
thirteenByThirteen: {
height: "7.5%",
"& $thirteenByThirteen": {
width: "7.5%",
height: "100%",
},
},
nineteenByNineteen: {
height: "5.2%",
"& $nineteenByNineteen": {
width: "5.2%",
height: "100%",
},
},
background: {
position: "absolute",
opacity: 0.09,
color: theme.colors.white,
fontFamily: "monospace",
fontSize: "calc(min(.65vh - 2px, 0.65vw - 2px))",
whiteSpace: "pre",
pointerEvents: "none",
paddingTop: "15px",
},
bitverseBackground: {
"&$background": {
fontSize: "calc(min(.83vh - 1px, 0.72vw, 7.856px))",
opacity: 0.11,
},
},
instructionScroller: {
height: "calc(100vh - 80px)",
overflowY: "scroll",
marginTop: "10px",
},
instructionBoard: {
width: "350px",
height: "350px",
},
instructionBoardWrapper: {
maxWidth: "400px",
minHeight: "485px",
marginRight: "20px",
},
instructionsBlurb: {
width: "60%",
minWidth: "500px",
marginRight: "20px",
},
translucent: {
opacity: 0.6,
},
cellNone: {
borderBottom: "none",
padding: 0,
margin: 0,
color: theme.colors.success,
},
cellBottomPadding: {
paddingBottom: "20px",
},
keyText: {
paddingTop: "10px",
color: theme.colors.int,
},
scoreModal: {
width: "400px",
},
centeredText: {
textAlign: "center",
},
}),
);
+127
View File
@@ -0,0 +1,127 @@
import { Board, boardSizes, BoardState, PointState } from "./goConstants";
import { WHRNG } from "../../Casino/RNG";
import { Player } from "@player";
import { floor } from "./boardState";
type rand = (n1: number, n2: number) => number;
export function addObstacles(boardState: BoardState) {
const rng = new WHRNG(Player.totalPlaytime);
const random = (n1: number, n2: number) => n1 + floor((n2 - n1 + 1) * rng.random());
const shouldRemoveCorner = !random(0, 4);
const shouldRemoveRows = !shouldRemoveCorner && !random(0, 4);
const shouldAddCenterBreak = !shouldRemoveCorner && !shouldRemoveRows && random(0, 3);
const obstacleTypeCount = +shouldRemoveCorner + +shouldRemoveRows + +shouldAddCenterBreak;
const edgeDeadCount = random(0, (getScale(boardState.board) + 2 - obstacleTypeCount) * 1.5);
if (shouldRemoveCorner) {
boardState.board = addDeadCorners(boardState.board, random);
}
if (shouldAddCenterBreak) {
boardState.board = addCenterBreak(boardState.board, random);
}
boardState.board = randomizeRotation(boardState.board, random);
if (shouldRemoveRows) {
boardState.board = removeRows(boardState.board, random);
}
boardState.board = addDeadNodesToEdge(boardState.board, random, edgeDeadCount);
boardState.board = resetCoordinates(boardState.board);
}
export function resetCoordinates(board: Board) {
const size = board[0].length;
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
const point = board[x]?.[y];
if (point) {
point.x = x;
point.y = y;
}
}
}
return board;
}
function getScale(board: Board) {
return boardSizes.indexOf(board[0].length);
}
function removeRows(board: Board, random: rand) {
const rowsToRemove = Math.max(random(-2, getScale(board)), 1);
for (let i = 0; i < rowsToRemove; i++) {
board[i] = board[i].map(() => null);
}
board = rotateNTimes(board, 3);
return board;
}
function addDeadNodesToEdge(board: Board, random: rand, maxPerEdge: number) {
const size = board[0].length;
for (let i = 0; i < 4; i++) {
const count = random(0, maxPerEdge);
for (let j = 0; j < count; j++) {
const yIndex = Math.max(random(-2, size - 1), 0);
board[0][yIndex] = null;
}
board = rotate90Degrees(board);
}
return board;
}
function addDeadCorners(board: Board, random: rand) {
const scale = getScale(board) + 1;
addDeadCorner(board, random, scale);
if (!random(0, 3)) {
board = rotate90Degrees(board);
board = rotate90Degrees(board);
addDeadCorner(board, random, scale - 2);
}
return randomizeRotation(board, random);
}
function addDeadCorner(board: Board, random: rand, size: number) {
let currentSize = size;
for (let i = 0; i < size && i < currentSize; i++) {
random(0, 1) && currentSize--;
board[i].forEach((point, index) => index < currentSize && point && (board[point.x][point.y] = null));
}
return board;
}
function addCenterBreak(board: Board, random: rand) {
const size = board[0].length;
const maxOffset = getScale(board);
const xIndex = random(0, maxOffset * 2) - maxOffset + floor(size / 2);
const length = random(1, floor(size / 2 - 1));
board[xIndex] = board[xIndex].map((point, index) => (index < length ? null : point));
return randomizeRotation(board, random);
}
function randomizeRotation(board: Board, random: rand) {
return rotateNTimes(board, random(0, 3));
}
function rotateNTimes(board: Board, rotations: number) {
for (let i = 0; i < rotations; i++) {
board = rotate90Degrees(board);
}
return board;
}
export function rotate90Degrees(board: Board) {
return board[0].map((_, index: number) => board.map((row: (PointState | null)[]) => row[index]).reverse());
}