From: Benjamin Auder Date: Thu, 7 Jan 2021 23:10:07 +0000 (+0100) Subject: A few fixes. Draft Synchrone2 (not working at all right now) X-Git-Url: https://git.auder.net/variants/Chakart/bundles/framework/css/structure.css?a=commitdiff_plain;h=9d15c433c207a2c3bb548d095939c3e08b4038fd;p=vchess.git A few fixes. Draft Synchrone2 (not working at all right now) --- diff --git a/TODO b/TODO index 555f57dd..41530300 100644 --- a/TODO +++ b/TODO @@ -20,8 +20,6 @@ Squatter Chess: safe on last rank = win Companion Chess : pieces of same nature don't attack each others https://www.chessvariants.com/difftaking.dir/brotherhood.html Crossing Chess = win when the king cross half-board -Medusa Chess = Isardam ---> à chaque déplacement, check attaques directes + découvertes autour de case de départ dans 8 directions (pas caval) Kingmaker: pawns can promote also into enemy king --> no king tracking, getCheckSquares + underCheck test all kings Eightkings: 8 pawns + 8 kings (non-royal until the last remains?) diff --git a/client/public/images/pieces/Otage/ac.png b/client/public/images/pieces/Otage/ac.png new file mode 100644 index 00000000..021203ec --- /dev/null +++ b/client/public/images/pieces/Otage/ac.png @@ -0,0 +1 @@ +#$# git-fat 65e285390d1316a145caf59dba56082e84f89c97 1796 diff --git a/client/public/images/pieces/Otage/ad.png b/client/public/images/pieces/Otage/ad.png new file mode 100644 index 00000000..d97512c3 --- /dev/null +++ b/client/public/images/pieces/Otage/ad.png @@ -0,0 +1 @@ +#$# git-fat 58766b94570a490158e015089860d757dbe82709 2114 diff --git a/client/public/images/pieces/Otage/ae.png b/client/public/images/pieces/Otage/ae.png new file mode 100644 index 00000000..e78a71b4 --- /dev/null +++ b/client/public/images/pieces/Otage/ae.png @@ -0,0 +1 @@ +#$# git-fat 193a5ddafe4ef4d2eded12e288f69acda4774b78 2136 diff --git a/client/public/images/pieces/Otage/af.png b/client/public/images/pieces/Otage/af.png new file mode 100644 index 00000000..03696291 --- /dev/null +++ b/client/public/images/pieces/Otage/af.png @@ -0,0 +1 @@ +#$# git-fat 69b49bbc5cef4a38c06ba83d0b768109c9c3ec35 2703 diff --git a/client/public/images/pieces/Otage/ag.png b/client/public/images/pieces/Otage/ag.png new file mode 100644 index 00000000..af8a3feb --- /dev/null +++ b/client/public/images/pieces/Otage/ag.png @@ -0,0 +1 @@ +#$# git-fat 88e66486bbd6a0cd30323c5001065757d19f69bb 2410 diff --git a/client/public/images/pieces/Otage/ai.png b/client/public/images/pieces/Otage/ai.png new file mode 100644 index 00000000..2dffc6f2 --- /dev/null +++ b/client/public/images/pieces/Otage/ai.png @@ -0,0 +1 @@ +#$# git-fat 774cb529106591f7fe02aea5b7296d18b5fc59da 2052 diff --git a/client/public/images/pieces/Otage/aj.png b/client/public/images/pieces/Otage/aj.png new file mode 100644 index 00000000..4da175fd --- /dev/null +++ b/client/public/images/pieces/Otage/aj.png @@ -0,0 +1 @@ +#$# git-fat 339260da4f0b7f479ea19a91e8b3dcefa9b08751 2088 diff --git a/client/public/images/pieces/Otage/al.png b/client/public/images/pieces/Otage/al.png new file mode 100644 index 00000000..b56798d4 --- /dev/null +++ b/client/public/images/pieces/Otage/al.png @@ -0,0 +1 @@ +#$# git-fat 95edb0a871d5a69dc8793fd82107ddfd917d2036 2652 diff --git a/client/public/images/pieces/Otage/am.png b/client/public/images/pieces/Otage/am.png new file mode 100644 index 00000000..95026cdd --- /dev/null +++ b/client/public/images/pieces/Otage/am.png @@ -0,0 +1 @@ +#$# git-fat 8086b2787f75ce1df7742800097bca4877b53e1c 2336 diff --git a/client/public/images/pieces/Otage/as.png b/client/public/images/pieces/Otage/as.png new file mode 100644 index 00000000..f9246f5c --- /dev/null +++ b/client/public/images/pieces/Otage/as.png @@ -0,0 +1 @@ +#$# git-fat c5e1df6ab7eaebc7e821f91627eb3b36d080e912 2641 diff --git a/client/public/images/pieces/Otage/at.png b/client/public/images/pieces/Otage/at.png new file mode 100644 index 00000000..0a9811bc --- /dev/null +++ b/client/public/images/pieces/Otage/at.png @@ -0,0 +1 @@ +#$# git-fat 13b756cdc30146754ed5d108ba2d1a8e04dd327a 3601 diff --git a/client/public/images/pieces/Otage/au.png b/client/public/images/pieces/Otage/au.png new file mode 100644 index 00000000..7eadfef8 --- /dev/null +++ b/client/public/images/pieces/Otage/au.png @@ -0,0 +1 @@ +#$# git-fat 0426e2dc88bd35b1ac744113caa08ff96f3f07ca 3396 diff --git a/client/public/images/pieces/Otage/aw.png b/client/public/images/pieces/Otage/aw.png new file mode 100644 index 00000000..6651a477 --- /dev/null +++ b/client/public/images/pieces/Otage/aw.png @@ -0,0 +1 @@ +#$# git-fat 0134aeb64a94399e30a77d26e140d2a749808d24 3158 diff --git a/client/public/images/pieces/Otage/ax.png b/client/public/images/pieces/Otage/ax.png new file mode 100644 index 00000000..fbc55051 --- /dev/null +++ b/client/public/images/pieces/Otage/ax.png @@ -0,0 +1 @@ +#$# git-fat ac11ef0357001f61184c15b9885f76f7d665b71f 3015 diff --git a/client/public/images/pieces/Otage/az.png b/client/public/images/pieces/Otage/az.png new file mode 100644 index 00000000..43da7c1f --- /dev/null +++ b/client/public/images/pieces/Otage/az.png @@ -0,0 +1 @@ +#$# git-fat cef32ab03de7d74ffaea24d74d71fe16fbfa18ba 4179 diff --git a/client/public/images/pieces/Otage/b_.png b/client/public/images/pieces/Otage/b_.png new file mode 100644 index 00000000..76cb7e82 --- /dev/null +++ b/client/public/images/pieces/Otage/b_.png @@ -0,0 +1 @@ +#$# git-fat d22c1958114554822bfd3bb1e45f4e67f43beee4 4356 diff --git a/client/public/images/pieces/Otage/ba.png b/client/public/images/pieces/Otage/ba.png new file mode 100644 index 00000000..91d0a1c0 --- /dev/null +++ b/client/public/images/pieces/Otage/ba.png @@ -0,0 +1 @@ +#$# git-fat d12f6fbdee0dfadf161e59769dc200c16cc79607 1791 diff --git a/client/public/images/pieces/Otage/bb.png b/client/public/images/pieces/Otage/bb.png new file mode 120000 index 00000000..6dfdf95a --- /dev/null +++ b/client/public/images/pieces/Otage/bb.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/bb.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/bc.png b/client/public/images/pieces/Otage/bc.png new file mode 100644 index 00000000..ec8575b2 --- /dev/null +++ b/client/public/images/pieces/Otage/bc.png @@ -0,0 +1 @@ +#$# git-fat e120c1ec8fc61bc965ff8270a6b5aa65c2aef3cc 1684 diff --git a/client/public/images/pieces/Otage/bd.png b/client/public/images/pieces/Otage/bd.png new file mode 100644 index 00000000..1cef7022 --- /dev/null +++ b/client/public/images/pieces/Otage/bd.png @@ -0,0 +1 @@ +#$# git-fat e02afb036bb850aeb8608e5a75da1b8beaa541ca 2763 diff --git a/client/public/images/pieces/Otage/be.png b/client/public/images/pieces/Otage/be.png new file mode 100644 index 00000000..7965808a --- /dev/null +++ b/client/public/images/pieces/Otage/be.png @@ -0,0 +1 @@ +#$# git-fat 9bce3cb99b6089f7cb131f44c4e64f9841ac7344 2399 diff --git a/client/public/images/pieces/Otage/bf.png b/client/public/images/pieces/Otage/bf.png new file mode 100644 index 00000000..cd8a0d8c --- /dev/null +++ b/client/public/images/pieces/Otage/bf.png @@ -0,0 +1 @@ +#$# git-fat cb6891f0ddb698cd67bdb4a5c2539ccc69de2c71 3666 diff --git a/client/public/images/pieces/Otage/bg.png b/client/public/images/pieces/Otage/bg.png new file mode 100644 index 00000000..2cfbf4e8 --- /dev/null +++ b/client/public/images/pieces/Otage/bg.png @@ -0,0 +1 @@ +#$# git-fat 6eb758f960e42c05bb45c7cabde713b08ec37d57 3658 diff --git a/client/public/images/pieces/Otage/bh.png b/client/public/images/pieces/Otage/bh.png new file mode 100644 index 00000000..68db8835 --- /dev/null +++ b/client/public/images/pieces/Otage/bh.png @@ -0,0 +1 @@ +#$# git-fat 0d28e9e17956e33cfbd29a0e09f8929f58843068 1666 diff --git a/client/public/images/pieces/Otage/bi.png b/client/public/images/pieces/Otage/bi.png new file mode 100644 index 00000000..7f1e839f --- /dev/null +++ b/client/public/images/pieces/Otage/bi.png @@ -0,0 +1 @@ +#$# git-fat 4d1f04788eaa8a200e1a7e1e34bf2fd693d1ecb3 2845 diff --git a/client/public/images/pieces/Otage/bj.png b/client/public/images/pieces/Otage/bj.png new file mode 100644 index 00000000..e629b10c --- /dev/null +++ b/client/public/images/pieces/Otage/bj.png @@ -0,0 +1 @@ +#$# git-fat d82164152aa37cdd53608e393ea335355793684c 2377 diff --git a/client/public/images/pieces/Otage/bk.png b/client/public/images/pieces/Otage/bk.png new file mode 120000 index 00000000..5779820a --- /dev/null +++ b/client/public/images/pieces/Otage/bk.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/bk.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/bl.png b/client/public/images/pieces/Otage/bl.png new file mode 100644 index 00000000..76a9d932 --- /dev/null +++ b/client/public/images/pieces/Otage/bl.png @@ -0,0 +1 @@ +#$# git-fat 2e89fd4980d48a02041db56fc40ebd325c1d8fd7 3554 diff --git a/client/public/images/pieces/Otage/bm.png b/client/public/images/pieces/Otage/bm.png new file mode 100644 index 00000000..78c377e0 --- /dev/null +++ b/client/public/images/pieces/Otage/bm.png @@ -0,0 +1 @@ +#$# git-fat 6c3e540858fcdcc65a2956444b373ce10af8a678 3582 diff --git a/client/public/images/pieces/Otage/bn.png b/client/public/images/pieces/Otage/bn.png new file mode 120000 index 00000000..45d65470 --- /dev/null +++ b/client/public/images/pieces/Otage/bn.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/bn.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/bo.png b/client/public/images/pieces/Otage/bo.png new file mode 100644 index 00000000..555b50a7 --- /dev/null +++ b/client/public/images/pieces/Otage/bo.png @@ -0,0 +1 @@ +#$# git-fat f8fa77908672c97ab6b5f9b38373d55ece85495b 3162 diff --git a/client/public/images/pieces/Otage/bp.png b/client/public/images/pieces/Otage/bp.png new file mode 120000 index 00000000..9fa31726 --- /dev/null +++ b/client/public/images/pieces/Otage/bp.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/bp.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/bq.png b/client/public/images/pieces/Otage/bq.png new file mode 120000 index 00000000..ca064fd4 --- /dev/null +++ b/client/public/images/pieces/Otage/bq.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/bq.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/br.png b/client/public/images/pieces/Otage/br.png new file mode 120000 index 00000000..cad7b691 --- /dev/null +++ b/client/public/images/pieces/Otage/br.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/br.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/bs.png b/client/public/images/pieces/Otage/bs.png new file mode 100644 index 00000000..773f0c0a --- /dev/null +++ b/client/public/images/pieces/Otage/bs.png @@ -0,0 +1 @@ +#$# git-fat 762cdf2994ae23d97926ba5165c6cfe8e6cc9ad0 3240 diff --git a/client/public/images/pieces/Otage/bt.png b/client/public/images/pieces/Otage/bt.png new file mode 100644 index 00000000..6ecf3718 --- /dev/null +++ b/client/public/images/pieces/Otage/bt.png @@ -0,0 +1 @@ +#$# git-fat 65661cb71ca2a39381645457a43bc3cd6eb6c004 3900 diff --git a/client/public/images/pieces/Otage/bu.png b/client/public/images/pieces/Otage/bu.png new file mode 100644 index 00000000..92983092 --- /dev/null +++ b/client/public/images/pieces/Otage/bu.png @@ -0,0 +1 @@ +#$# git-fat f82094d9a3c991f5e81457a7e90e5cf32feeb4c2 3874 diff --git a/client/public/images/pieces/Otage/bv.png b/client/public/images/pieces/Otage/bv.png new file mode 100644 index 00000000..7e45eeee --- /dev/null +++ b/client/public/images/pieces/Otage/bv.png @@ -0,0 +1 @@ +#$# git-fat a7d85e986bc2e0901bfcf6e84e40f27cef99ae07 2803 diff --git a/client/public/images/pieces/Otage/bw.png b/client/public/images/pieces/Otage/bw.png new file mode 100644 index 00000000..af76815c --- /dev/null +++ b/client/public/images/pieces/Otage/bw.png @@ -0,0 +1 @@ +#$# git-fat a5a2aecef097e46a1cb6c63bb3f4d02584624b66 3972 diff --git a/client/public/images/pieces/Otage/bx.png b/client/public/images/pieces/Otage/bx.png new file mode 100644 index 00000000..355825ed --- /dev/null +++ b/client/public/images/pieces/Otage/bx.png @@ -0,0 +1 @@ +#$# git-fat c020ed2347d1817d1046bc1c0eb632a1244a82d0 4152 diff --git a/client/public/images/pieces/Otage/by.png b/client/public/images/pieces/Otage/by.png new file mode 100644 index 00000000..aaa5e980 --- /dev/null +++ b/client/public/images/pieces/Otage/by.png @@ -0,0 +1 @@ +#$# git-fat 380e33b4d0ed9b7e12009c1d1fb8b60e4de9b808 4419 diff --git a/client/public/images/pieces/Otage/bz.png b/client/public/images/pieces/Otage/bz.png new file mode 100644 index 00000000..6f56f2ce --- /dev/null +++ b/client/public/images/pieces/Otage/bz.png @@ -0,0 +1 @@ +#$# git-fat 8ac9cf1d541acbc1fd06316f16637912449fb265 4612 diff --git a/client/public/images/pieces/Otage/script.sh b/client/public/images/pieces/Otage/script.sh new file mode 100644 index 00000000..776540d0 --- /dev/null +++ b/client/public/images/pieces/Otage/script.sh @@ -0,0 +1,11 @@ +taille=64 +for color in w b; do + [[ $color = 'w' ]] && oppCol='b' || oppCol='w' + for captured in p r n b q k; do + convert $color$captured.png -resize "$taille"x"$taille" $color"$captured"_small.png + for piece in p r n b q k; do + convert -composite -gravity center $oppCol$piece.png $color"$captured"_small.png $color$captured$piece.png + done + done +done +# Finally: manual renaming (TODO) diff --git a/client/public/images/pieces/Otage/vc.png b/client/public/images/pieces/Otage/vc.png new file mode 100644 index 00000000..0902e553 --- /dev/null +++ b/client/public/images/pieces/Otage/vc.png @@ -0,0 +1 @@ +#$# git-fat 236752cfe7d08810ef3ebcadadd5fef4598a055c 1802 diff --git a/client/public/images/pieces/Otage/vd.png b/client/public/images/pieces/Otage/vd.png new file mode 100644 index 00000000..40fc1ff9 --- /dev/null +++ b/client/public/images/pieces/Otage/vd.png @@ -0,0 +1 @@ +#$# git-fat 2ffcc839afd07a0ab24044ad069d28a04cbeed58 2983 diff --git a/client/public/images/pieces/Otage/ve.png b/client/public/images/pieces/Otage/ve.png new file mode 100644 index 00000000..eedda801 --- /dev/null +++ b/client/public/images/pieces/Otage/ve.png @@ -0,0 +1 @@ +#$# git-fat df9c4fae98b2081ef9bc8fd1464f9157415c0119 2961 diff --git a/client/public/images/pieces/Otage/vf.png b/client/public/images/pieces/Otage/vf.png new file mode 100644 index 00000000..212b330a --- /dev/null +++ b/client/public/images/pieces/Otage/vf.png @@ -0,0 +1 @@ +#$# git-fat e2f005c73214ee900ee14c5dfadcde52ada05cbe 4277 diff --git a/client/public/images/pieces/Otage/vg.png b/client/public/images/pieces/Otage/vg.png new file mode 100644 index 00000000..3e822fbd --- /dev/null +++ b/client/public/images/pieces/Otage/vg.png @@ -0,0 +1 @@ +#$# git-fat 67c43942a3a7e3cabc992b1dcbea0ed539aa0a65 3141 diff --git a/client/public/images/pieces/Otage/vi.png b/client/public/images/pieces/Otage/vi.png new file mode 100644 index 00000000..b5df5543 --- /dev/null +++ b/client/public/images/pieces/Otage/vi.png @@ -0,0 +1 @@ +#$# git-fat 81f9a3f4865dfbcc92ea4f198ca8ba4d9c284def 3125 diff --git a/client/public/images/pieces/Otage/vj.png b/client/public/images/pieces/Otage/vj.png new file mode 100644 index 00000000..4aec896c --- /dev/null +++ b/client/public/images/pieces/Otage/vj.png @@ -0,0 +1 @@ +#$# git-fat 41eb0d17e29224bdfade80d9c9d7ac9d83da441d 2990 diff --git a/client/public/images/pieces/Otage/vl.png b/client/public/images/pieces/Otage/vl.png new file mode 100644 index 00000000..6f88954c --- /dev/null +++ b/client/public/images/pieces/Otage/vl.png @@ -0,0 +1 @@ +#$# git-fat 446a4edf4046c1e82109531b9b8c5cff7f4880be 4284 diff --git a/client/public/images/pieces/Otage/vm.png b/client/public/images/pieces/Otage/vm.png new file mode 100644 index 00000000..5148cf8d --- /dev/null +++ b/client/public/images/pieces/Otage/vm.png @@ -0,0 +1 @@ +#$# git-fat 811e84107a9a1ee6acf80564a4b46e91dadc64fd 3090 diff --git a/client/public/images/pieces/Otage/vs.png b/client/public/images/pieces/Otage/vs.png new file mode 100644 index 00000000..b554885b --- /dev/null +++ b/client/public/images/pieces/Otage/vs.png @@ -0,0 +1 @@ +#$# git-fat 949a78814edadf22b0e664c5aff663d270bdccf5 3326 diff --git a/client/public/images/pieces/Otage/vt.png b/client/public/images/pieces/Otage/vt.png new file mode 100644 index 00000000..25dd7e82 --- /dev/null +++ b/client/public/images/pieces/Otage/vt.png @@ -0,0 +1 @@ +#$# git-fat 5c6311a1cdf14cb4e215a0bb8c351d9ce2924f59 4593 diff --git a/client/public/images/pieces/Otage/vu.png b/client/public/images/pieces/Otage/vu.png new file mode 100644 index 00000000..4b46bd7b --- /dev/null +++ b/client/public/images/pieces/Otage/vu.png @@ -0,0 +1 @@ +#$# git-fat f8c087c9652f542a954d156a458f78d7772dba8d 3629 diff --git a/client/public/images/pieces/Otage/vw.png b/client/public/images/pieces/Otage/vw.png new file mode 100644 index 00000000..7e8745b4 --- /dev/null +++ b/client/public/images/pieces/Otage/vw.png @@ -0,0 +1 @@ +#$# git-fat b5adbdcee6e9c589d105d62c97aecb86b65494ea 4508 diff --git a/client/public/images/pieces/Otage/vx.png b/client/public/images/pieces/Otage/vx.png new file mode 100644 index 00000000..fd04cf34 --- /dev/null +++ b/client/public/images/pieces/Otage/vx.png @@ -0,0 +1 @@ +#$# git-fat 4f03b86e99ccbeb0be463607a0099ac3ce0d8b3b 3506 diff --git a/client/public/images/pieces/Otage/vz.png b/client/public/images/pieces/Otage/vz.png new file mode 100644 index 00000000..c3d1f0d2 --- /dev/null +++ b/client/public/images/pieces/Otage/vz.png @@ -0,0 +1 @@ +#$# git-fat 5ea91792aa06c1d1b63b8b07d023d23cede90808 4056 diff --git a/client/public/images/pieces/Otage/w_.png b/client/public/images/pieces/Otage/w_.png new file mode 100644 index 00000000..f239feac --- /dev/null +++ b/client/public/images/pieces/Otage/w_.png @@ -0,0 +1 @@ +#$# git-fat 05ab786ebb4fc25440b15ee766315c787d5786fc 4396 diff --git a/client/public/images/pieces/Otage/wa.png b/client/public/images/pieces/Otage/wa.png new file mode 100644 index 00000000..63ad031a --- /dev/null +++ b/client/public/images/pieces/Otage/wa.png @@ -0,0 +1 @@ +#$# git-fat 026c80ccc3b3527c06d97be79954f90a785ea715 2229 diff --git a/client/public/images/pieces/Otage/wb.png b/client/public/images/pieces/Otage/wb.png new file mode 120000 index 00000000..36a1712c --- /dev/null +++ b/client/public/images/pieces/Otage/wb.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wb.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/wc.png b/client/public/images/pieces/Otage/wc.png new file mode 100644 index 00000000..086de728 --- /dev/null +++ b/client/public/images/pieces/Otage/wc.png @@ -0,0 +1 @@ +#$# git-fat df49dd56b0bdaf77efb955d674c7f0d596e831d7 2125 diff --git a/client/public/images/pieces/Otage/wd.png b/client/public/images/pieces/Otage/wd.png new file mode 100644 index 00000000..6035099a --- /dev/null +++ b/client/public/images/pieces/Otage/wd.png @@ -0,0 +1 @@ +#$# git-fat a671d25cab689601ebc29528b7c3398deea867b9 2457 diff --git a/client/public/images/pieces/Otage/we.png b/client/public/images/pieces/Otage/we.png new file mode 100644 index 00000000..6756b8e2 --- /dev/null +++ b/client/public/images/pieces/Otage/we.png @@ -0,0 +1 @@ +#$# git-fat 2662e08a587c44d74b999c97b4ca8274bd301d61 2634 diff --git a/client/public/images/pieces/Otage/wf.png b/client/public/images/pieces/Otage/wf.png new file mode 100644 index 00000000..eb0dd00c --- /dev/null +++ b/client/public/images/pieces/Otage/wf.png @@ -0,0 +1 @@ +#$# git-fat 24cc6c2b2e241a134d0414b105489a6b4f48cad2 3060 diff --git a/client/public/images/pieces/Otage/wg.png b/client/public/images/pieces/Otage/wg.png new file mode 100644 index 00000000..f80dd0c3 --- /dev/null +++ b/client/public/images/pieces/Otage/wg.png @@ -0,0 +1 @@ +#$# git-fat 0cf7be0d77ff50a60b1d042f40d28f952780936e 3269 diff --git a/client/public/images/pieces/Otage/wh.png b/client/public/images/pieces/Otage/wh.png new file mode 100644 index 00000000..7766f328 --- /dev/null +++ b/client/public/images/pieces/Otage/wh.png @@ -0,0 +1 @@ +#$# git-fat 066a4540c9232bec7fdd3635f2aca844814c1b4a 1868 diff --git a/client/public/images/pieces/Otage/wi.png b/client/public/images/pieces/Otage/wi.png new file mode 100644 index 00000000..beb83842 --- /dev/null +++ b/client/public/images/pieces/Otage/wi.png @@ -0,0 +1 @@ +#$# git-fat 129ab825232e9a19c10b375536c1c1fb99534153 2316 diff --git a/client/public/images/pieces/Otage/wj.png b/client/public/images/pieces/Otage/wj.png new file mode 100644 index 00000000..241c02b9 --- /dev/null +++ b/client/public/images/pieces/Otage/wj.png @@ -0,0 +1 @@ +#$# git-fat 814246e6d014e16b9fd9d66f94fb2457075ffc4a 2161 diff --git a/client/public/images/pieces/Otage/wk.png b/client/public/images/pieces/Otage/wk.png new file mode 120000 index 00000000..acf54001 --- /dev/null +++ b/client/public/images/pieces/Otage/wk.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wk.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/wl.png b/client/public/images/pieces/Otage/wl.png new file mode 100644 index 00000000..7733c533 --- /dev/null +++ b/client/public/images/pieces/Otage/wl.png @@ -0,0 +1 @@ +#$# git-fat 5cca31f9cf96f0bb0ca89b7a9e280a1b7c9b3445 2873 diff --git a/client/public/images/pieces/Otage/wm.png b/client/public/images/pieces/Otage/wm.png new file mode 100644 index 00000000..49dd0e50 --- /dev/null +++ b/client/public/images/pieces/Otage/wm.png @@ -0,0 +1 @@ +#$# git-fat 13fcad23431fd4e972675536f136b760ad644795 3040 diff --git a/client/public/images/pieces/Otage/wn.png b/client/public/images/pieces/Otage/wn.png new file mode 120000 index 00000000..4508036e --- /dev/null +++ b/client/public/images/pieces/Otage/wn.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wn.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/wo.png b/client/public/images/pieces/Otage/wo.png new file mode 100644 index 00000000..e0a00581 --- /dev/null +++ b/client/public/images/pieces/Otage/wo.png @@ -0,0 +1 @@ +#$# git-fat 150776276e067f50f8d8e9cdd10bd946582e9c12 3541 diff --git a/client/public/images/pieces/Otage/wp.png b/client/public/images/pieces/Otage/wp.png new file mode 120000 index 00000000..b1c56708 --- /dev/null +++ b/client/public/images/pieces/Otage/wp.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wp.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/wq.png b/client/public/images/pieces/Otage/wq.png new file mode 120000 index 00000000..536649a9 --- /dev/null +++ b/client/public/images/pieces/Otage/wq.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wq.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/wr.png b/client/public/images/pieces/Otage/wr.png new file mode 120000 index 00000000..fb36f29e --- /dev/null +++ b/client/public/images/pieces/Otage/wr.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wr.png \ No newline at end of file diff --git a/client/public/images/pieces/Otage/ws.png b/client/public/images/pieces/Otage/ws.png new file mode 100644 index 00000000..5274172c --- /dev/null +++ b/client/public/images/pieces/Otage/ws.png @@ -0,0 +1 @@ +#$# git-fat 12e4984af6227a93acca7d8ff1231e0ac558622c 3401 diff --git a/client/public/images/pieces/Otage/wt.png b/client/public/images/pieces/Otage/wt.png new file mode 100644 index 00000000..9982d945 --- /dev/null +++ b/client/public/images/pieces/Otage/wt.png @@ -0,0 +1 @@ +#$# git-fat 49aa672eb86924d39f722a98ade88244eca0d5cb 4161 diff --git a/client/public/images/pieces/Otage/wu.png b/client/public/images/pieces/Otage/wu.png new file mode 100644 index 00000000..473da49f --- /dev/null +++ b/client/public/images/pieces/Otage/wu.png @@ -0,0 +1 @@ +#$# git-fat 2244328474fd95e628f75821e5ef8dd505e59972 4232 diff --git a/client/public/images/pieces/Otage/wv.png b/client/public/images/pieces/Otage/wv.png new file mode 100644 index 00000000..f00d4800 --- /dev/null +++ b/client/public/images/pieces/Otage/wv.png @@ -0,0 +1 @@ +#$# git-fat d26f408f59430cdadf663616927871b6f038edae 3322 diff --git a/client/public/images/pieces/Otage/ww.png b/client/public/images/pieces/Otage/ww.png new file mode 100644 index 00000000..c655b277 --- /dev/null +++ b/client/public/images/pieces/Otage/ww.png @@ -0,0 +1 @@ +#$# git-fat 4b211057ba07ade56ea40dbe63b8cd2ab53facec 3883 diff --git a/client/public/images/pieces/Otage/wx.png b/client/public/images/pieces/Otage/wx.png new file mode 100644 index 00000000..25d83995 --- /dev/null +++ b/client/public/images/pieces/Otage/wx.png @@ -0,0 +1 @@ +#$# git-fat 76d08fca30cbfcd5be55129aab7a60503f860a92 4021 diff --git a/client/public/images/pieces/Otage/wy.png b/client/public/images/pieces/Otage/wy.png new file mode 100644 index 00000000..a9c12cd3 --- /dev/null +++ b/client/public/images/pieces/Otage/wy.png @@ -0,0 +1 @@ +#$# git-fat 412a2f4b1000bdbc9d307b7467c17f7b6ebfb234 4977 diff --git a/client/public/images/pieces/Otage/wz.png b/client/public/images/pieces/Otage/wz.png new file mode 100644 index 00000000..7f2e974b --- /dev/null +++ b/client/public/images/pieces/Otage/wz.png @@ -0,0 +1 @@ +#$# git-fat b47b3b98b69cc245600e2727f6dcddd650b03ab7 5293 diff --git a/client/src/base_rules.js b/client/src/base_rules.js index b8f05060..e9ea7bc1 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -528,6 +528,7 @@ export const ChessRules = class ChessRules { } // Scan board for kings positions + // TODO: should be done from board, no need for the complete FEN scanKings(fen) { // Squares of white and black king: this.kingPos = { w: [-1, -1], b: [-1, -1] }; @@ -1073,7 +1074,7 @@ export const ChessRules = class ChessRules { this.board[rx][ry] != V.EMPTY && this.getPiece(rx, ry) == piece && this.getColor(rx, ry) == color && - this.canTake([rx, ry], [x, y]) + this.canTake([rx, ry], [x, y]) //for Paco-Sako (TODO: necessary?) ) { return true; } diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 0aebead6..fc8bd3d5 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -179,6 +179,7 @@ export const translations = { "Both sides of the mirror": "Both sides of the mirror", "Burmese Chess": "Burmese Chess", "Capture all of a kind": "Capture all of a kind", + "Capture and release hostages": "Capture and release hostages", "Capture both colors": "Capture both colors", "Capture en passant": "Capture en passant", "Capture on the edge": "Capture on the edge", @@ -251,7 +252,8 @@ export const translations = { "Pawns capture backward": "Pawns capture backward", "Pawns move diagonally": "Pawns move diagonally", "Pieces upside down": "Pieces upside down", - "Play at the same time": "Play at the same time", + "Play at the same time (v1)": "Play at the same time (v1)", + "Play at the same time (v2)": "Play at the same time (v2)", "Play more and more moves (v1)": "Play more and more moves (v1)", "Play more and more moves (v2)": "Play more and more moves (v2)", "Play opponent's pieces": "Play opponent's pieces", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 2a9bb365..d0f87e04 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -179,6 +179,7 @@ export const translations = { "Both sides of the mirror": "Ambos lados del espejo", "Burmese Chess": "Ajedrez birmano", "Capture all of a kind": "Capturar todo del mismo tipo", + "Capture and release hostages": "Captura y libera a los rehenes", "Capture both colors": "Captura ambos colores", "Capture en passant": "Capturar en passant", "Capture on the edge": "Capturar en el borde", @@ -251,7 +252,8 @@ export const translations = { "Pawns capture backward": "Los peones capturan hacia atrás", "Pawns move diagonally": "Los peones se mueven en diagonal", "Pieces upside down": "Piezas al revés", - "Play at the same time": "Jugar al mismo tiempo", + "Play at the same time (v1)": "Jugar al mismo tiempo (v1)", + "Play at the same time (v2)": "Jugar al mismo tiempo (v2)", "Play more and more moves (v1)": "Jugar más y más movimientos (v1)", "Play more and more moves (v2)": "Jugar más y más movimientos (v2)", "Play opponent's pieces": "Jugar piezas opuestas", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 7552b3a2..3adb35ef 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -179,6 +179,7 @@ export const translations = { "Both sides of the mirror": "Les deux côté du miroir", "Burmese Chess": "Échecs birmans", "Capture all of a kind": "Capturez tout d'un même type", + "Capture and release hostages": "Capturez et libérez les otages", "Capture both colors": "Capturer les deux couleurs", "Capture en passant": "Capturer en passant", "Capture on the edge": "Capturer sur le bord", @@ -251,7 +252,8 @@ export const translations = { "Pawns capture backward": "Les pions capturent en arrière", "Pawns move diagonally": "Les pions vont en diagonale", "Pieces upside down": "Pièces à l'envers", - "Play at the same time": "Jouer en même temps", + "Play at the same time (v1)": "Jouer en même temps (v1)", + "Play at the same time (v2)": "Jouer en même temps (v2)", "Play more and more moves (v1)": "Jouez de plus en plus de coups (v1)", "Play more and more moves (v2)": "Jouez de plus en plus de coups (v2)", "Play opponent's pieces": "Jouez les pièces adverses", diff --git a/client/src/translations/rules/Otage/en.pug b/client/src/translations/rules/Otage/en.pug new file mode 100644 index 00000000..ff50199d --- /dev/null +++ b/client/src/translations/rules/Otage/en.pug @@ -0,0 +1,13 @@ +p.boxed + | A piece "A" captured by another piece "B" remain on the board, + | following "B". It can be freed by capturing "B" at any moment. + +p + | This variant is inspired by + a(href="/#/variants/Pacosako") Paco-Sako + | , where union pieces are controlled by both players. + | Here, an "union" can be moved only by the last player capturing it. + +p. + The king can thus capture without losing the game. + Everything else is exactly the same as in Paco-Sako. diff --git a/client/src/translations/rules/Otage/es.pug b/client/src/translations/rules/Otage/es.pug new file mode 100644 index 00000000..330abc3f --- /dev/null +++ b/client/src/translations/rules/Otage/es.pug @@ -0,0 +1,13 @@ +p.boxed + | Una pieza "A" capturada por otra pieza "B" permanece en el tablero, + | siguiendo "B". Puede ser liberada capturando "B" en cualquier momento. + +p + | Esta variante está inspirada de + a(href="/#/variants/Pacosako") Paco-Sako + | , donde las piezas-unión son controladas por ambos jugadores. + | Aquí, una "unión" solo puede moverse por el último jugador que lo capturó. + +p. + El rey puede capturar sin perder el juego. + Todo lo demás ocurre exactamente como en Paco-Sako. diff --git a/client/src/translations/rules/Otage/fr.pug b/client/src/translations/rules/Otage/fr.pug new file mode 100644 index 00000000..51104b68 --- /dev/null +++ b/client/src/translations/rules/Otage/fr.pug @@ -0,0 +1,14 @@ +p.boxed + | Une pièce "A" capturée par une autre pièce "B" reste sur l'échiquier, + | suivant "B". Elle peut être libérée en capturant "B" à tout moment. + +p + | Cette variante est inspirée de + a(href="/#/variants/Pacosako") Paco-Sako + | , où les pièces-union sont contrôlées par les deux joueurs. + | Ici, une "union" ne peut être déplacée que par + | le dernier joueur l'ayant capturée. + +p. + Le roi peut alors capturer sans perdre la partie. + Tout le reste se déroule exactement comme à Paco-Sako. diff --git a/client/src/translations/rules/Pacosako/en.pug b/client/src/translations/rules/Pacosako/en.pug index 51383e06..9e08caf7 100644 --- a/client/src/translations/rules/Pacosako/en.pug +++ b/client/src/translations/rules/Pacosako/en.pug @@ -92,8 +92,8 @@ ul dancing with a queen, and makes the move e5 to g3, the other player cannot move it back to e5 just after. li. - Pawns can advance two squares only if they never moved (by themselves - or as part of an union). + Pawns (or pawns in unions) can advance two squares from their initial + position, but you may only do that once per file. li. If you form an union with your king but end dancing with the other king on the other end of the chain, the game is a draw. diff --git a/client/src/translations/rules/Pacosako/es.pug b/client/src/translations/rules/Pacosako/es.pug index 618f38d4..3a41c529 100644 --- a/client/src/translations/rules/Pacosako/es.pug +++ b/client/src/translations/rules/Pacosako/es.pug @@ -95,8 +95,8 @@ ul baila con una reina y hace un movimiento de e5 a g3, el otro jugador no puede lo reemplace en e5 inmediatamente después. li. - Los peones pueden avanzar dos espacios solo si nunca han - movido (por sí mismos o como miembro de una unión). + Los peones (o peones en unión) pueden avanzar dos espacios desde su + posición inicial, pero solo puede hacerlo una vez por columna. li. Si formas una unión con tu rey pero terminas bailando con el rey oponente en el otro extremo de la cadena, el juego se empata. diff --git a/client/src/translations/rules/Pacosako/fr.pug b/client/src/translations/rules/Pacosako/fr.pug index de85735f..e18cdd12 100644 --- a/client/src/translations/rules/Pacosako/fr.pug +++ b/client/src/translations/rules/Pacosako/fr.pug @@ -95,8 +95,8 @@ ul une dame, et effectue un déplacement de e5 en g3, l'autre joueur ne peut pas la replacer en e5 immédiatement après. li. - Les pions peuvent avancer de deux cases seulement s'ils n'ont jamais - bougé (par eux-mêmes ou comme membre d'une union). + Les pions (ou pions en union) peuvent avancer de deux cases depuis leur + position initiale, mais vous ne pouvez le faire qu'une fois par colonne. li. Si vous formez une union avec votre roi mais terminez par danser avec le roi adverse à l'autre bout de la chaîne, la partie est nulle. diff --git a/client/src/translations/rules/Synchrone/en.pug b/client/src/translations/rules/Synchrone1/en.pug similarity index 100% rename from client/src/translations/rules/Synchrone/en.pug rename to client/src/translations/rules/Synchrone1/en.pug diff --git a/client/src/translations/rules/Synchrone/es.pug b/client/src/translations/rules/Synchrone1/es.pug similarity index 100% rename from client/src/translations/rules/Synchrone/es.pug rename to client/src/translations/rules/Synchrone1/es.pug diff --git a/client/src/translations/rules/Synchrone/fr.pug b/client/src/translations/rules/Synchrone1/fr.pug similarity index 100% rename from client/src/translations/rules/Synchrone/fr.pug rename to client/src/translations/rules/Synchrone1/fr.pug diff --git a/client/src/translations/rules/Synchrone2/en.pug b/client/src/translations/rules/Synchrone2/en.pug new file mode 100644 index 00000000..15050ab7 --- /dev/null +++ b/client/src/translations/rules/Synchrone2/en.pug @@ -0,0 +1,9 @@ +p.boxed TODO + +p No en passant captures. + +p No anticipated recaptures. + +p But, a deterministic "capturing turn" added. + +p http://www.hexenspiel.de/engl/synchronous-chess/ diff --git a/client/src/translations/rules/Synchrone2/es.pug b/client/src/translations/rules/Synchrone2/es.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Synchrone2/es.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/translations/rules/Synchrone2/fr.pug b/client/src/translations/rules/Synchrone2/fr.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Synchrone2/fr.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug index 9baec9d1..7eeb43fd 100644 --- a/client/src/translations/variants/en.pug +++ b/client/src/translations/variants/en.pug @@ -202,7 +202,8 @@ p. "Dark", "Hidden", "Hiddenqueen", - "Synchrone" + "Synchrone1", + "Synchrone2" ] ul for v in varlist @@ -402,6 +403,7 @@ p. "Hamilton", "Isardam", "Magnetic", + "Otage", "Pacosako", "Parachute", "Screen", diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug index e1eb1902..ad4fa473 100644 --- a/client/src/translations/variants/es.pug +++ b/client/src/translations/variants/es.pug @@ -209,7 +209,8 @@ p. "Dark", "Hidden", "Hiddenqueen", - "Synchrone" + "Synchrone1", + "Synchrone2" ] ul for v in varlist @@ -413,6 +414,7 @@ p. "Hamilton", "Isardam", "Magnetic", + "Otage", "Pacosako", "Parachute", "Screen", diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug index 209211ae..28d7876c 100644 --- a/client/src/translations/variants/fr.pug +++ b/client/src/translations/variants/fr.pug @@ -208,7 +208,8 @@ p. "Dark", "Hidden", "Hiddenqueen", - "Synchrone" + "Synchrone1", + "Synchrone2" ] ul for v in varlist @@ -412,6 +413,7 @@ p. "Hamilton", "Isardam", "Magnetic", + "Otage", "Pacosako", "Parachute", "Screen", diff --git a/client/src/variants/Otage.js b/client/src/variants/Otage.js new file mode 100644 index 00000000..84970983 --- /dev/null +++ b/client/src/variants/Otage.js @@ -0,0 +1,809 @@ +import { ChessRules, PiPo, Move } from "@/base_rules"; +import { randInt } from "@/utils/alea"; +import { ArrayFun } from "@/utils/array"; + +export class OtageRules extends ChessRules { + + static get IMAGE_EXTENSION() { + return ".png"; + } + + // Hostage / Capturer combinations + // + letter among a, b, v, w to indicate colors + config: + // a: black first, black controls + // b: white first, black controls + // v: black first, white controls + // w: white first, white controls + static get UNIONS() { + return { + a: ['p', 'p'], + c: ['p', 'r'], + d: ['p', 'n'], + e: ['p', 'b'], + f: ['p', 'q'], + g: ['p', 'k'], + h: ['r', 'r'], + i: ['r', 'n'], + j: ['r', 'b'], + l: ['r', 'q'], + m: ['r', 'k'], + o: ['n', 'n'], + s: ['n', 'b'], + t: ['n', 'q'], + u: ['n', 'k'], + v: ['b', 'b'], + w: ['b', 'q'], + x: ['b', 'k'], + y: ['q', 'q'], + z: ['q', 'k'], + '_': ['k', 'k'] + }; + } + + static board2fen(b) { + if (ChessRules.PIECES.includes(b[1])) return ChessRules.board2fen(b); + // Show symbol first (no collisions) + return b[1] + b[0]; + } + + static fen2board(f) { + if (f.length == 1) return ChessRules.fen2board(f); + return f[1] + f[0]; //"color" first + } + + static IsGoodPosition(position) { + if (position.length == 0) return false; + const rows = position.split("/"); + if (rows.length != V.size.x) return false; + let kingSymb = ['k', 'g', 'm', 'u', 'x', '_']; + let kings = { 'k': 0, 'K': 0 }; + for (let row of rows) { + let sumElts = 0; + for (let i = 0; i < row.length; i++) { + const lowR = row[i].toLowerCase + const readNext = !(ChessRules.PIECES.includes(lowR)); + if (!!(lowR.match(/[a-z_]/))) { + sumElts++; + if (kingSymb.includes(row[i])) kings['k']++; + // Not "else if", if two kings dancing together + if (kingSymb.some(s => row[i] == s.toUpperCase())) kings['K']++; + if (readNext) i++; + } + else { + const num = parseInt(row[i], 10); + if (isNaN(num) || num <= 0) return false; + sumElts += num; + } + } + if (sumElts != V.size.y) return false; + } + // Both kings should be on board. Exactly one per color. + if (Object.values(kings).some(v => v != 1)) return false; + return true; + } + + static GetBoard(position) { + const rows = position.split("/"); + let board = ArrayFun.init(V.size.x, V.size.y, ""); + for (let i = 0; i < rows.length; i++) { + let j = 0; + for (let indexInRow = 0; indexInRow < rows[i].length; indexInRow++) { + const character = rows[i][indexInRow]; + const num = parseInt(character, 10); + // If num is a number, just shift j: + if (!isNaN(num)) j += num; + else { + // Something at position i,j + const lowC = character.toLowerCase(); + if (ChessRules.PIECES.includes(lowC)) + board[i][j++] = V.fen2board(character); + else + board[i][j++] = V.fen2board(lowC + rows[i][++indexInRow]); + } + } + } + return board; + } + + getPpath(b) { + return "Otage/" + b; + } + + getPPpath(m) { + if (ChessRules.PIECES.includes(m.appear[0].p)) return super.getPPpath(m); + // For an "union", show only relevant piece: + // The color must be deduced from the move: reaching final rank of who? + const color = (m.appear[0].x == 0 ? 'w' : 'b'); + const up = this.getUnionPieces(m.appear[0].c, m.appear[0].p); + return "Pacosako/" + color + up[color]; + } + + canTake([x1, y1], [x2, y2]) { + const p1 = this.board[x1][y1].charAt(1); + if (!(ChessRules.PIECES.includes(p1))) return false; + const p2 = this.board[x2][y2].charAt(1); + if (!(ChessRules.PIECES.includes(p2))) return true; + const c1 = this.board[x1][y1].charAt(0); + const c2 = this.board[x2][y2].charAt(0); + return (c1 != c2); + } + + canIplay(side, [x, y]) { + const c = this.board[x][y].charAt(0); + const compSide = (side == 'w' ? 'v' : 'a'); + return (this.turn == side && [side, compSide].includes(c)); + } + + scanKings(fen) { + this.kingPos = { w: [-1, -1], b: [-1, -1] }; + const fenRows = V.ParseFen(fen).position.split("/"); + const startRow = { 'w': V.size.x - 1, 'b': 0 }; + const kingSymb = ['k', 'g', 'm', 'u', 'x', '_']; + for (let i = 0; i < fenRows.length; i++) { + let k = 0; + for (let j = 0; j < fenRows[i].length; j++) { + const c = fenRows[i].charAt(j); + const lowR = c.toLowerCase(); + const readNext = !(ChessRules.PIECES.includes(lowR)); + if (!!(lowR.match(/[a-z_]/))) { + if (kingSymb.includes(c)) + this.kingPos["b"] = [i, k]; + // Not "else if", in case of two kings dancing together + if (kingSymb.some(s => c == s.toUpperCase())) + this.kingPos["w"] = [i, k]; + if (readNext) j++; + } + else { + const num = parseInt(fenRows[i].charAt(j), 10); + if (!isNaN(num)) k += num - 1; + } + k++; + } + } + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + // Stack of "last move" only for intermediate chaining + this.lastMoveEnd = [null]; + } + + static IsGoodFlags(flags) { + // 4 for castle + 16 for pawns + return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/); + } + + setFlags(fenflags) { + super.setFlags(fenflags); //castleFlags + this.pawnFlags = { + w: [...Array(8)], //pawns can move 2 squares? + b: [...Array(8)] + }; + const flags = fenflags.substr(4); //skip first 4 letters, for castle + for (let c of ["w", "b"]) { + for (let i = 0; i < 8; i++) + this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1"; + } + } + + aggregateFlags() { + return [this.castleFlags, this.pawnFlags]; + } + + disaggregateFlags(flags) { + this.castleFlags = flags[0]; + this.pawnFlags = flags[1]; + } + + static GenRandInitFen(randomness) { + // Add 16 pawns flags: + return ChessRules.GenRandInitFen(randomness) + .slice(0, -2) + "1111111111111111 -"; + } + + getFlagsFen() { + let fen = super.getFlagsFen(); + // Add pawns flags + for (let c of ["w", "b"]) + for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0"); + return fen; + } + + getPiece(i, j) { + const p = this.board[i][j].charAt(1); + if (ChessRules.PIECES.includes(p)) return p; + const c = this.board[i][j].charAt(0); + const idx = (['a', 'w'].includes(c) ? 0 : 1); + return V.UNIONS[p][idx]; + } + + getUnionPieces(color, code) { + const pieces = V.UNIONS[code]; + return { + w: pieces[ ['b', 'w'].includes(color) ? 0 : 1 ], + b: pieces[ ['a', 'v'].includes(color) ? 0 : 1 ] + }; + } + + // p1: white piece, p2: black piece, capturer: (temporary) owner + getUnionCode(p1, p2, capturer) { + let uIdx = ( + Object.values(V.UNIONS).findIndex(v => v[0] == p1 && v[1] == p2) + ); + let c = ''; + if (capturer == 'w') c = (uIdx >= 0 ? 'w' : 'v'); + else c = (uIdx >= 0 ? 'b' : 'a'); + if (uIdx == -1) { + uIdx = ( + Object.values(V.UNIONS).findIndex(v => v[0] == p2 && v[1] == p1) + ); + } + return { c: c, p: Object.keys(V.UNIONS)[uIdx] }; + } + + getBasicMove([sx, sy], [ex, ey], tr) { + const L = this.lastMoveEnd.length; + const lm = this.lastMoveEnd[L-1]; + const piece = (!!lm ? lm.p : null); + const initColor = (!!piece ? this.turn : this.board[sx][sy].charAt(0)); + const initPiece = (piece || this.board[sx][sy].charAt(1)); + const c = this.turn; + const oppCol = V.GetOppCol(c); + if (!!tr && !(ChessRules.PIECES.includes(initPiece))) { + // Transformation computed without taking union into account + const up = this.getUnionPieces(initColor, initPiece); + let args = [tr.p, up[oppCol]]; + if (['a', 'v'].includes(initColor)) args = args.reverse(); + const capturer = (['a', 'b'].includes(initColor) ? 'b' : 'w'); + const cp = this.getUnionCode(args[0], args[1], capturer); + tr.c = cp.c; + tr.p = cp.p; + } + // 4 cases : moving + // - union to free square (other cases are illegal: return null) + // - normal piece to free square, + // to enemy normal piece, or + // to union (releasing our piece) + let mv = new Move({ + start: { x: sx, y: sy }, + end: { x: ex, y: ey }, + vanish: [] + }); + if (!piece) { + mv.vanish = [ + new PiPo({ + x: sx, + y: sy, + c: initColor, + p: initPiece + }) + ]; + } + // Treat free square cases first: + if (this.board[ex][ey] == V.EMPTY) { + mv.appear = [ + new PiPo({ + x: ex, + y: ey, + c: !!tr ? tr.c : initColor, + p: !!tr ? tr.p : initPiece + }) + ]; + return mv; + } + // Now the two cases with union / release: + const destColor = this.board[ex][ey].charAt(0); + const destPiece = this.board[ex][ey].charAt(1); + mv.vanish.push( + new PiPo({ + x: ex, + y: ey, + c: destColor, + p: destPiece + }) + ); + if (ChessRules.PIECES.includes(destPiece)) { + // Normal piece: just create union + let args = [!!tr ? tr.p : initPiece, destPiece]; + if (c == 'b') args = args.reverse(); + const cp = this.getUnionCode(args[0], args[1], c); + mv.appear = [ + new PiPo({ + x: ex, + y: ey, + c: cp.c, + p: cp.p + }) + ]; + return mv; + } + // Releasing a piece in an union: keep track of released piece + const up = this.getUnionPieces(destColor, destPiece); + let args = [!!tr ? tr.p : initPiece, up[oppCol]]; + if (c == 'b') args = args.reverse(); + const cp = this.getUnionCode(args[0], args[1], c); + mv.appear = [ + new PiPo({ + x: ex, + y: ey, + c: cp.c, + p: cp.p + }) + ]; + mv.end.released = up[c]; + return mv; + } + + // noCastle arg: when detecting king attacks + getPotentialMovesFrom([x, y], noCastle) { + const L = this.lastMoveEnd.length; + const lm = this.lastMoveEnd[L-1]; + if (!!lm && (x != lm.x || y != lm.y)) return []; + const piece = (!!lm ? lm.p : this.getPiece(x, y)); + if (!!lm) { + var saveSquare = this.board[x][y]; + this.board[x][y] = this.turn + piece; + } + let baseMoves = []; + const c = this.turn; + switch (piece || this.getPiece(x, y)) { + case V.PAWN: { + const firstRank = (c == 'w' ? 7 : 0); + baseMoves = this.getPotentialPawnMoves([x, y]).filter(m => { + // Skip forbidden 2-squares jumps (except from first rank) + // Also skip unions capturing en-passant (not allowed). + return ( + ( + m.start.x == firstRank || + Math.abs(m.end.x - m.start.x) == 1 || + this.pawnFlags[c][m.start.y] + ) + && + ( + this.board[x][y].charAt(1) == V.PAWN || + m.start.y == m.end.y + ) + ); + }); + break; + } + case V.ROOK: + baseMoves = this.getPotentialRookMoves([x, y]); + break; + case V.KNIGHT: + baseMoves = this.getPotentialKnightMoves([x, y]); + break; + case V.BISHOP: + baseMoves = this.getPotentialBishopMoves([x, y]); + break; + case V.QUEEN: + baseMoves = this.getPotentialQueenMoves([x, y]); + break; + case V.KING: + baseMoves = this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + if (!noCastle && this.castleFlags[this.turn].some(v => v < V.size.y)) + baseMoves = baseMoves.concat(this.getCastleMoves(sq)); + break; + } + // When a pawn in an union reaches final rank with a non-standard + // promotion move: apply promotion anyway + let moves = []; + const oppCol = V.GetOppCol(c); + const oppLastRank = (c == 'w' ? 7 : 0); + baseMoves.forEach(m => { + if ( + m.end.x == oppLastRank && + ['c', 'd', 'e', 'f', 'g'].includes(m.appear[0].p) + ) { + // Move to first rank, which is last rank for opponent's pawn. + // => Show promotion choices. + // Find our piece in union (not a pawn) + const up = this.getUnionPieces(m.appear[0].c, m.appear[0].p); + // merge with all potential promotion pieces + push (loop) + for (let promotionPiece of [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]) { + let args = [up[c], promotionPiece]; + if (c == 'b') args = args.reverse(); + const cp = this.getUnionCode(args[0], args[1], c); + let cpMove = JSON.parse(JSON.stringify(m)); + cpMove.appear[0].c = cp.c; + cpMove.appear[0].p = cp.p; + moves.push(cpMove); + } + } + else { + if ( + m.vanish.length > 0 && + m.vanish[0].p == V.PAWN && + m.start.y != m.end.y && + this.board[m.end.x][m.end.y] == V.EMPTY + ) { + if (!!lm) + // No en-passant inside a chaining + return; + // Fix en-passant capture: union type, maybe released piece too + const cs = [m.end.x + (c == 'w' ? 1 : -1), m.end.y]; + const code = this.board[cs[0]][cs[1]].charAt(1); + if (code == V.PAWN) { + // Simple en-passant capture (usual: just form union) + m.appear[0].c = c; + m.appear[0].p = 'a'; + } + else { + // An union pawn + something just moved two squares + const color = this.board[cs[0]][cs[1]].charAt(0); + const up = this.getUnionPieces(color, code); + m.end.released = up[c]; + let args = [V.PAWN, up[oppCol]]; + if (c == 'b') args = args.reverse(); + const cp = this.getUnionCode(args[0], args[1], c); + m.appear[0].c = cp.c; + m.appear[0].p = cp.p; + } + } + moves.push(m); + } + }); + if (!!lm) this.board[x][y] = saveSquare; + return moves; + } + + getEpSquare(moveOrSquare) { + if (typeof moveOrSquare === "string") { + const square = moveOrSquare; + if (square == "-") return undefined; + return V.SquareToCoords(square); + } + const move = moveOrSquare; + const s = move.start, + e = move.end; + if ( + s.y == e.y && + Math.abs(s.x - e.x) == 2 && + this.getPiece(s.x, s.y) == V.PAWN + ) { + return { + x: (s.x + e.x) / 2, + y: s.y + }; + } + return undefined; + } + + getCastleMoves([x, y]) { + const c = this.turn; + const accepted = (c == 'w' ? ['v', 'w'] : ['a', 'b']); + const oppCol = V.GetOppCol(c); + let moves = []; + const finalSquares = [ [2, 3], [6, 5] ]; + castlingCheck: for (let castleSide = 0; castleSide < 2; castleSide++) { + if (this.castleFlags[c][castleSide] >= 8) continue; + const rookPos = this.castleFlags[c][castleSide]; + const castlingColor = this.board[x][rookPos].charAt(0); + const castlingPiece = this.board[x][rookPos].charAt(1); + + // Nothing on the path of the king ? + const finDist = finalSquares[castleSide][0] - y; + let step = finDist / Math.max(1, Math.abs(finDist)); + let i = y; + let kingSquares = [y]; + do { + if ( + this.board[x][i] != V.EMPTY && + !accepted.includes(this.getColor(x, i)) + ) { + continue castlingCheck; + } + i += step; + kingSquares.push(i); + } while (i != finalSquares[castleSide][0]); + // No checks on the path of the king ? + if (this.isAttacked(kingSquares, oppCol)) continue castlingCheck; + + // Nothing on the path to the rook? + step = castleSide == 0 ? -1 : 1; + for (i = y + step; i != rookPos; i += step) { + if (this.board[x][i] != V.EMPTY) continue castlingCheck; + } + + // Nothing on final squares, except maybe king and castling rook? + for (i = 0; i < 2; i++) { + if ( + finalSquares[castleSide][i] != rookPos && + this.board[x][finalSquares[castleSide][i]] != V.EMPTY && + ( + finalSquares[castleSide][i] != y || + // TODO: next test seems superflu + !accepted.includes(this.getColor(x, finalSquares[castleSide][i])) + ) + ) { + continue castlingCheck; + } + } + + moves.push( + new Move({ + appear: [ + new PiPo({ + x: x, + y: finalSquares[castleSide][0], + p: V.KING, + c: c + }), + new PiPo({ + x: x, + y: finalSquares[castleSide][1], + p: castlingPiece, + c: castlingColor + }) + ], + vanish: [ + // King might be initially disguised (Titan...) + new PiPo({ x: x, y: y, p: V.KING, c: c }), + new PiPo({ x: x, y: rookPos, p: castlingPiece, c: castlingColor }) + ], + end: + Math.abs(y - rookPos) <= 2 + ? { x: x, y: rookPos } + : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } + }) + ); + } + + return moves; + } + + getEnpassantCaptures(sq, shiftX) { + // HACK: when artificially change turn, do not consider en-passant + const mcMod2 = this.movesCount % 2; + const c = this.turn; + if ((c == 'w' && mcMod2 == 1) || (c == 'b' && mcMod2 == 0)) return []; + return super.getEnpassantCaptures(sq, shiftX); + } + + isAttacked_aux(files, color, positions, fromSquare, released) { + // "positions" = array of FENs to detect infinite loops. Example: + // r1q1k2r/p1Pb1ppp/5n2/1f1p4/AV5P/P1eDP3/3B1PP1/R3K1NR, + // Bxd2 Bxc3 Bxb4 Bxc3 Bxb4 etc. + const newPos = { fen: super.getBaseFen(), piece: released }; + if (positions.some(p => p.piece == newPos.piece && p.fen == newPos.fen)) + // Start of an infinite loop: exit + return false; + positions.push(newPos); + const rank = (color == 'w' ? 0 : 7); + const moves = this.getPotentialMovesFrom(fromSquare); + if (moves.some(m => m.end.x == rank && files.includes(m.end.y))) + // Found an attack! + return true; + for (let m of moves) { + if (!!m.end.released) { + // Turn won't change since !!m.released + this.play(m); + const res = this.isAttacked_aux( + files, color, positions, [m.end.x, m.end.y], m.end.released); + this.undo(m); + if (res) return true; + } + } + return false; + } + + isAttacked(files, color) { + const rank = (color == 'w' ? 0 : 7); + // Since it's too difficult (impossible?) to search from the square itself, + // let's adopt a suboptimal but working strategy: find all attacks. + const c = this.turn; + // Artificial turn change is required: + this.turn = color; + let res = false; + outerLoop: for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + // Attacks must start from a normal piece, not an union. + // Therefore, the following test is correct. + if ( + this.board[i][j] != V.EMPTY && + [V.KING, V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN].includes( + this.board[i][j].charAt(1)) && + this.board[i][j].charAt(0) == color + ) { + // Try from here. + const moves = this.getPotentialMovesFrom([i, j], "noCastle"); + if (moves.some(m => m.end.x == rank && files.includes(m.end.y))) { + res = true; + break outerLoop; + } + for (let m of moves) { + if (!!m.end.released) { + // Turn won't change since !!m.released + this.play(m); + let positions = []; + res = this.isAttacked_aux( + files, color, positions, [m.end.x, m.end.y], m.end.released); + this.undo(m); + if (res) break outerLoop; + } + } + } + } + } + this.turn = c; + return res; + } + + // Do not consider checks, except to forbid castling + getCheckSquares() { + return []; + } + filterValid(moves) { + return moves; + } + + updateCastleFlags(move, piece) { + const c = this.turn; + const firstRank = (c == "w" ? 7 : 0); + if (piece == V.KING && move.appear.length > 0) + this.castleFlags[c] = [V.size.y, V.size.y]; + else if ( + move.start.x == firstRank && + this.castleFlags[c].includes(move.start.y) + ) { + const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = V.size.y; + } + else if ( + move.end.x == firstRank && + this.castleFlags[c].includes(move.end.y) + ) { + // Move to our rook: necessary normal piece, to union, releasing + // (or the rook was moved before!) + const flagIdx = (move.end.y == this.castleFlags[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = V.size.y; + } + } + + prePlay(move) { + // Easier before move is played in this case (flags are saved) + const c = this.turn; + const L = this.lastMoveEnd.length; + const lm = this.lastMoveEnd[L-1]; + const piece = (!!lm ? lm.p : move.vanish[0].p); + if (piece == V.KING) + this.kingPos[c] = [move.appear[0].x, move.appear[0].y]; + this.updateCastleFlags(move, piece); + const pawnFirstRank = (c == 'w' ? 6 : 1); + if (move.start.x == pawnFirstRank) + // This move (potentially) turns off a 2-squares pawn flag + this.pawnFlags[c][move.start.y] = false; + } + + play(move) { + move.flags = JSON.stringify(this.aggregateFlags()); + this.prePlay(move); + this.epSquares.push(this.getEpSquare(move)); + // Check if the move is the last of the turn: all cases except releases + if (!move.end.released) { + // No more union releases available + this.turn = V.GetOppCol(this.turn); + this.movesCount++; + this.lastMoveEnd.push(null); + } + else + this.lastMoveEnd.push(Object.assign({ p: move.end.released }, move.end)); + V.PlayOnBoard(this.board, move); + } + + undo(move) { + this.epSquares.pop(); + this.disaggregateFlags(JSON.parse(move.flags)); + V.UndoOnBoard(this.board, move); + this.lastMoveEnd.pop(); + if (!move.end.released) { + this.turn = V.GetOppCol(this.turn); + this.movesCount--; + } + this.postUndo(move); + } + + postUndo(move) { + if (this.getPiece(move.start.x, move.start.y) == V.KING) + this.kingPos[this.turn] = [move.start.x, move.start.y]; + } + + getCurrentScore() { + // Check kings: if one is captured, the side lost + for (let c of ['w', 'b']) { + const kp = this.kingPos[c]; + const cell = this.board[kp[0]][kp[1]]; + if ( + cell[1] != V.KING && + ( + (c == 'w' && ['a', 'b'].includes(cell[0])) || + (c == 'b' && ['v', 'w'].includes(cell[0])) + ) + ) { + // King is captured + return (c == 'w' ? "0-1" : "1-0"); + } + } + return "*"; + } + + getComputerMove() { + let initMoves = this.getAllValidMoves(); + if (initMoves.length == 0) return null; + // Loop until valid move is found (no blocked pawn released...) + while (true) { + let moves = JSON.parse(JSON.stringify(initMoves)); + let mvArray = []; + let mv = null; + // Just play random moves (for now at least. TODO?) + while (moves.length > 0) { + mv = moves[randInt(moves.length)]; + mvArray.push(mv); + this.play(mv); + if (!!mv.end.released) + // A piece was just released from an union + moves = this.getPotentialMovesFrom([mv.end.x, mv.end.y]); + else break; + } + for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); + if (!mv.end.released) return (mvArray.length > 1 ? mvArray : mvArray[0]); + } + } + + // NOTE: evalPosition() is wrong, but unused since bot plays at random + + getNotation(move) { + if (move.appear.length == 2 && move.appear[0].p == V.KING) + return (move.end.y < move.start.y ? "0-0-0" : "0-0"); + + const c = this.turn; + const L = this.lastMoveEnd.length; + const lm = this.lastMoveEnd[L-1]; + let piece = null; + if (!lm && move.vanish.length == 0) + // When importing a game, the info move.released is lost + piece = move.appear[0].p; + else piece = (!!lm ? lm.p : move.vanish[0].p); + if (!(ChessRules.PIECES.includes(piece))) { + // Decode (moving) union + const up = this.getUnionPieces( + move.vanish.length > 0 ? move.vanish[0].c : move.appear[0].c, piece); + piece = up[c] + } + + // Basic move notation: + let notation = piece.toUpperCase(); + if ( + this.board[move.end.x][move.end.y] != V.EMPTY || + (piece == V.PAWN && move.start.y != move.end.y) + ) { + notation += "x"; + } + const finalSquare = V.CoordsToSquare(move.end); + notation += finalSquare; + + // Add potential promotion indications: + const firstLastRank = (c == 'w' ? [7, 0] : [0, 7]); + if (move.end.x == firstLastRank[1] && piece == V.PAWN) { + const up = this.getUnionPieces(move.appear[0].c, move.appear[0].p); + notation += "=" + up[c].toUpperCase(); + } + else if ( + move.end.x == firstLastRank[0] && + move.vanish.length > 0 && + ['c', 'd', 'e', 'f', 'g'].includes(move.vanish[0].p) + ) { + // We promoted an opponent's pawn + const oppCol = V.GetOppCol(c); + const up = this.getUnionPieces(move.appear[0].c, move.appear[0].p); + notation += "=" + up[oppCol].toUpperCase(); + } + + return notation; + } + +}; diff --git a/client/src/variants/Pacosako.js b/client/src/variants/Pacosako.js index ae4fe6ff..9f0abf88 100644 --- a/client/src/variants/Pacosako.js +++ b/client/src/variants/Pacosako.js @@ -48,7 +48,6 @@ export class PacosakoRules extends ChessRules { for (let row of rows) { let sumElts = 0; for (let i = 0; i < row.length; i++) { - const lowR = row[i].toLowerCase if (!!(row[i].toLowerCase().match(/[a-z_]/))) { sumElts++; if (kingSymb.includes(row[i])) kings['k']++; @@ -181,7 +180,8 @@ export class PacosakoRules extends ChessRules { getUmove(move) { if ( move.vanish.length == 1 && - !(ChessRules.PIECES.includes(move.appear[0].p)) + !(ChessRules.PIECES.includes(move.appear[0].p)) && + move.appear[0].p == move.vanish[0].p //not a promotion ) { // An union moving return { start: move.start, end: move.end }; @@ -354,7 +354,8 @@ export class PacosakoRules extends ChessRules { p: cp.p }) ]; - mv.released = up[c]; + // In move.end, to be sent to the server + mv.end.released = up[c]; return mv; } @@ -443,7 +444,6 @@ export class PacosakoRules extends ChessRules { return; // Fix en-passant capture: union type, maybe released piece too const cs = [m.end.x + (c == 'w' ? 1 : -1), m.end.y]; - const color = this.board[cs[0]][cs[1]].charAt(0); const code = this.board[cs[0]][cs[1]].charAt(1); if (code == V.PAWN) { // Simple en-passant capture (usual: just form union) @@ -451,9 +451,10 @@ export class PacosakoRules extends ChessRules { m.appear[0].p = 'a'; } else { - // An union pawn + something juste moved two squares + // An union pawn + something just moved two squares + const color = this.board[cs[0]][cs[1]].charAt(0); const up = this.getUnionPieces(color, code); - m.released = up[c]; + m.end.released = up[c]; let args = [V.PAWN, up[oppCol]]; if (c == 'b') args = args.reverse(); const cp = this.getUnionCode(args[0], args[1]); @@ -477,11 +478,10 @@ export class PacosakoRules extends ChessRules { const move = moveOrSquare; const s = move.start, e = move.end; - const oppCol = V.GetOppCol(this.turn); if ( s.y == e.y && Math.abs(s.x - e.x) == 2 && - this.getPiece(s.x, s.y, oppCol) == V.PAWN + this.getPiece(s.x, s.y, this.turn) == V.PAWN ) { return { x: (s.x + e.x) / 2, @@ -497,7 +497,7 @@ export class PacosakoRules extends ChessRules { !!m1 && !(ChessRules.PIECES.includes(m2.appear[0].p)) && m2.vanish.length == 1 && - !m2.released && + !m2.end.released && m1.start.x == m2.end.x && m1.end.x == m2.start.x && m1.start.y == m2.end.y && @@ -611,11 +611,11 @@ export class PacosakoRules extends ChessRules { // Found an attack! return true; for (let m of moves) { - if (!!m.released) { + if (!!m.end.released) { // Turn won't change since !!m.released this.play(m); const res = this.isAttacked_aux( - files, color, positions, [m.end.x, m.end.y], m.released); + files, color, positions, [m.end.x, m.end.y], m.end.released); this.undo(m); if (res) return true; } @@ -649,12 +649,12 @@ export class PacosakoRules extends ChessRules { break outerLoop; } for (let m of moves) { - if (!!m.released) { + if (!!m.end.released) { // Turn won't change since !!m.released this.play(m); let positions = []; res = this.isAttacked_aux( - files, color, positions, [m.end.x, m.end.y], m.released); + files, color, positions, [m.end.x, m.end.y], m.end.released); this.undo(m); if (res) break outerLoop; } @@ -719,13 +719,19 @@ export class PacosakoRules extends ChessRules { this.prePlay(move); this.epSquares.push(this.getEpSquare(move)); // Check if the move is the last of the turn: all cases except releases - if (!move.released) { + if (!move.end.released) { // No more union releases available this.turn = V.GetOppCol(this.turn); this.movesCount++; this.lastMoveEnd.push(null); } - else this.lastMoveEnd.push(Object.assign({ p: move.released }, move.end)); + else { + this.lastMoveEnd.push({ + p: move.end.released, + x: move.end.x, + y: move.end.y + }); + } V.PlayOnBoard(this.board, move); this.umoves.push(this.getUmove(move)); } @@ -735,7 +741,7 @@ export class PacosakoRules extends ChessRules { this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); this.lastMoveEnd.pop(); - if (!move.released) { + if (!move.end.released) { this.turn = V.GetOppCol(this.turn); this.movesCount--; } @@ -775,13 +781,13 @@ export class PacosakoRules extends ChessRules { mv = moves[randInt(moves.length)]; mvArray.push(mv); this.play(mv); - if (!!mv.released) + if (!!mv.end.released) // A piece was just released from an union moves = this.getPotentialMovesFrom([mv.end.x, mv.end.y]); else break; } for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); - if (!mv.released) return (mvArray.length > 1 ? mvArray : mvArray[0]); + if (!mv.end.released) return (mvArray.length > 1 ? mvArray : mvArray[0]); } } diff --git a/client/src/variants/Synchrone.js b/client/src/variants/Synchrone1.js similarity index 98% rename from client/src/variants/Synchrone.js rename to client/src/variants/Synchrone1.js index 365dd9aa..ba9cf9f2 100644 --- a/client/src/variants/Synchrone.js +++ b/client/src/variants/Synchrone1.js @@ -1,7 +1,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; -export class SynchroneRules extends ChessRules { +export class Synchrone1Rules extends ChessRules { static get CanAnalyze() { return false; @@ -48,8 +48,8 @@ export class SynchroneRules extends ChessRules { static ParseFen(fen) { const fenParts = fen.split(" "); return Object.assign( - ChessRules.ParseFen(fen), - { whiteMove: fenParts[5] } + { whiteMove: fenParts[5] }, + ChessRules.ParseFen(fen) ); } @@ -71,7 +71,7 @@ export class SynchroneRules extends ChessRules { const epArray = parsedFen.enpassant.split(","); this.epSquares = []; epArray.forEach(epsq => this.epSquares.push(this.getEpSquare(epsq))); - super.scanKings(fen); + this.scanKings(); // Also init whiteMove this.whiteMove = parsedFen.whiteMove != "-" @@ -299,7 +299,8 @@ export class SynchroneRules extends ChessRules { // Castle smove.appear.push(m1.appear[1]); smove.vanish.push(m1.vanish[1]); - } else if ( + } + else if ( m1.vanish.length == 2 && ( m1.vanish[1].x != m2.start.x || @@ -312,7 +313,8 @@ export class SynchroneRules extends ChessRules { // Castle smove.appear.push(m2.appear[1]); smove.vanish.push(m2.vanish[1]); - } else if ( + } + else if ( m2.vanish.length == 2 && ( m2.vanish[1].x != m1.start.x || @@ -321,7 +323,8 @@ export class SynchroneRules extends ChessRules { ) { smove.vanish.push(m2.vanish[1]); } - } else { + } + else { // Collision: if (m1.vanish.length == 1 && m2.vanish.length == 1) { // Easy case: both disappear except if one is a king @@ -335,7 +338,8 @@ export class SynchroneRules extends ChessRules { c: (p1 == V.KING ? 'w' : 'b') }); } - } else { + } + else { // One move is a self-capture and the other a normal capture: // only the self-capture appears const selfCaptureMove = @@ -434,7 +438,8 @@ export class SynchroneRules extends ChessRules { this.scanKings(); // Also reset whiteMove this.whiteMove = null; - } else this.whiteMove = move.whiteMove; + } + else this.whiteMove = move.whiteMove; } getCheckSquares() { diff --git a/client/src/variants/Synchrone2.js b/client/src/variants/Synchrone2.js new file mode 100644 index 00000000..7f623982 --- /dev/null +++ b/client/src/variants/Synchrone2.js @@ -0,0 +1,122 @@ +import { ChessRules } from "@/base_rules"; +import { Synchrone1Rules } from "@/variants/Synchrone1"; +import { randInt } from "@/utils/alea"; + +export class Synchrone2Rules extends Synchrone1Rules { + + static get CanAnalyze() { + return true;//false; + } + + static get HasEnpassant() { + return false; + } + + static IsGoodFen(fen) { + if (!Synchrone1Rules.IsGoodFen(fen)) return false; + const fenParsed = V.ParseFen(fen); + // 5) Check initFen (not really... TODO?) + if (!fenParsed.initFen || fenParsed.initFen == "-") return false; + return true; + } + + static ParseFen(fen) { + const fenParts = fen.split(" "); + return Object.assign( + { + initFen: fenParts[4], + whiteMove: fenParts[5] + }, + ChessRules.ParseFen(fen) + ); + } + + getInitfenFen() { + if (!this.whiteMove) return "-"; + return JSON.stringify({ + start: this.whiteMove.start, + end: this.whiteMove.end, + appear: this.whiteMove.appear, + vanish: this.whiteMove.vanish + }); + } + + getFen() { + return ( + super.getFen() + " " + + this.getInitfenFen() + " " + + this.getWhitemoveFen() + ); + } + + static GenRandInitFen(randomness) { + const res = ChessRules.GenRandInitFen(randomness); + // Add initFen field: + return res.slice(0, -1) + " " + res.split(' ')[1] + " -"; + } + + setOtherVariables(fen) { + const parsedFen = V.ParseFen(fen); + this.setFlags(parsedFen.flags); + super.scanKings(fen); + // Also init whiteMove + this.whiteMove = + parsedFen.whiteMove != "-" + ? JSON.parse(parsedFen.whiteMove) + : null; + // And initFen (not empty) + this.initFen = parsedFen.initFen; + } + + getPotentialMovesFrom([x, y]) { + if (this.movesCount % 4 <= 1) return super.getPotentialMovesFrom([x, y]); + // TODO: either add a "blackMove' field in FEN (bof...), + // or write an helper function to detect from diff positions, + // which piece moved (if not disappeared!), which moves are valid. + // + do not forget pass move (king 2 king): always possible at stage 2. + return []; + } + + play(move) { + move.flags = JSON.stringify(this.aggregateFlags()); + // Do not play on board (would reveal the move...) + this.turn = V.GetOppCol(this.turn); + this.movesCount++; + this.postPlay(move); + } + + undo(move) { + this.disaggregateFlags(JSON.parse(move.flags)); + if (this.turn == 'w') + // Back to the middle of the move + V.UndoOnBoard(this.board, move.smove); + this.turn = V.GetOppCol(this.turn); + this.movesCount--; + this.postUndo(move); + } + + getCurrentScore() { + if (this.movesCount % 4 != 0) + // Turn (2 x white + black) not over yet + return "*"; + // Was a king captured? + if (this.kingPos['w'][0] < 0) return "0-1"; + if (this.kingPos['b'][0] < 0) return "1-0"; + const whiteCanMove = this.atLeastOneMove('w'); + const blackCanMove = this.atLeastOneMove('b'); + if (whiteCanMove && blackCanMove) return "*"; + // Game over + const whiteInCheck = this.underCheck('w'); + const blackInCheck = this.underCheck('b'); + if ( + (whiteCanMove && !this.underCheck('b')) || + (blackCanMove && !this.underCheck('w')) + ) { + return "1/2"; + } + // Checkmate: could be mutual + if (!whiteCanMove && !blackCanMove) return "1/2"; + return (whiteCanMove ? "1-0" : "0-1"); + } + +}; diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 7d7f234f..c91e1270 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -982,7 +982,7 @@ export default { .getElementById("btnC" + newChall.type) .classList.add("somethingnew"); } - if (chall.to == this.st.user.name) { + if (!!chall.to && chall.to == this.st.user.name) { notify( "New challenge", // fromValues.name should exist since the player is online, but diff --git a/server/db/populate.sql b/server/db/populate.sql index e1156e5d..88a9003f 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -7,7 +7,8 @@ insert or ignore into Variants (name, description, noProblems) values ('Dice', 'Roll the dice', true), ('Hidden', 'Unidentified pieces', true), ('Hiddenqueen', 'Queen disguised as a pawn', true), - ('Synchrone', 'Play at the same time', true); + ('Synchrone1', 'Play at the same time (v1)', true); + ('Synchrone2', 'Play at the same time (v2)', true); insert or ignore into Variants (name, description) values ('Absorption', 'Absorb powers'), @@ -88,6 +89,7 @@ insert or ignore into Variants (name, description) values ('Omega', 'A wizard in the corner'), ('Orda', 'Mongolian Horde (v1)'), ('Ordamirror', 'Mongolian Horde (v2)'), + ('Otage', 'Capture and release hostages'), ('Pacifist1', 'Convert & support (v1)'), ('Pacifist2', 'Convert & support (v2)'), ('Pacosako', 'Dance with the King'),