From 67f43ab63257b1f5c34354731c2a37d0fc576f71 Mon Sep 17 00:00:00 2001 From: Daimas Date: Thu, 15 Oct 2015 21:23:35 +0200 Subject: [PATCH] Moved jni code in a new folder. --- .gitignore | 13 + android-pachi.iml | 19 + debug.keystore | Bin 0 -> 2146 bytes gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 + gradlew.bat | 90 + jni/Android.mk | 1 + jni/Application.mk | 4 + jni/pachi/.gitignore | 9 + jni/pachi/Android.mk | 78 + jni/pachi/COPYING | 340 + jni/pachi/CREDITS | 29 + jni/pachi/HACKING | 306 + jni/pachi/Makefile | 137 + jni/pachi/README | 220 + jni/pachi/TODO | 31 + jni/pachi/android/util.c | 15 + jni/pachi/android/util.h | 7 + jni/pachi/board.c | 1614 + jni/pachi/board.h | 592 + jni/pachi/book.dat.bad | 1 + jni/pachi/book.dat.extra | 30 + jni/pachi/chat.c | 50 + jni/pachi/chat.h | 17 + jni/pachi/debug.h | 28 + jni/pachi/distributed/Makefile | 12 + jni/pachi/distributed/distributed.c | 526 + jni/pachi/distributed/distributed.h | 104 + jni/pachi/distributed/merge.c | 347 + jni/pachi/distributed/merge.h | 9 + jni/pachi/distributed/protocol.c | 681 + jni/pachi/distributed/protocol.h | 98 + jni/pachi/engine.h | 53 + jni/pachi/fbook.c | 200 + jni/pachi/fbook.h | 28 + jni/pachi/fixp.h | 23 + jni/pachi/gtp.c | 616 + jni/pachi/gtp.h | 25 + jni/pachi/joseki/Makefile | 12 + jni/pachi/joseki/README | 32 + jni/pachi/joseki/base.c | 78 + jni/pachi/joseki/base.h | 25 + jni/pachi/joseki/joseki.c | 212 + jni/pachi/joseki/joseki.h | 8 + jni/pachi/joseki/sgfvar2gtp.pl | 54 + jni/pachi/joseki19.pdict | 31551 ++++++++++++++++ jni/pachi/media/pachi-small.png | Bin 0 -> 42206 bytes jni/pachi/media/pachi.jpg | Bin 0 -> 506466 bytes jni/pachi/montecarlo/Makefile | 12 + jni/pachi/montecarlo/internal.h | 32 + jni/pachi/montecarlo/montecarlo.c | 273 + jni/pachi/montecarlo/montecarlo.h | 8 + jni/pachi/move.c | 60 + jni/pachi/move.h | 107 + jni/pachi/mq.h | 85 + jni/pachi/network.c | 232 + jni/pachi/network.h | 15 + jni/pachi/ownermap.c | 104 + jni/pachi/ownermap.h | 50 + jni/pachi/pachi.c | 232 + jni/pachi/pattern.c | 584 + jni/pachi/pattern.h | 179 + jni/pachi/pattern3.c | 272 + jni/pachi/pattern3.h | 126 + jni/pachi/patternplay/Makefile | 12 + jni/pachi/patternplay/patternplay.c | 128 + jni/pachi/patternplay/patternplay.h | 8 + jni/pachi/patternprob.c | 118 + jni/pachi/patternprob.h | 75 + jni/pachi/patternscan/Makefile | 12 + jni/pachi/patternscan/patternscan.c | 323 + jni/pachi/patternscan/patternscan.h | 8 + jni/pachi/patternsp.c | 472 + jni/pachi/patternsp.h | 165 + jni/pachi/playout.c | 167 + jni/pachi/playout.h | 108 + jni/pachi/playout/Makefile | 12 + jni/pachi/playout/light.c | 32 + jni/pachi/playout/light.h | 9 + jni/pachi/playout/moggy.c | 1066 + jni/pachi/playout/moggy.h | 10 + jni/pachi/probdist.c | 55 + jni/pachi/probdist.h | 79 + jni/pachi/random.c | 101 + jni/pachi/random.h | 30 + jni/pachi/random/Makefile | 12 + jni/pachi/random/random.c | 47 + jni/pachi/random/random.h | 8 + jni/pachi/replay/Makefile | 12 + jni/pachi/replay/{replay.c~ => replay.c} | 0 jni/pachi/replay/replay.h | 8 + jni/pachi/stats.h | 101 + jni/pachi/stone.c | 25 + jni/pachi/stone.h | 47 + jni/pachi/t-play/TESTS | 622 + jni/pachi/t-play/autotest/README | 128 + jni/pachi/t-play/autotest/TODO | 13 + jni/pachi/t-play/autotest/autotest-client | 27 + jni/pachi/t-play/autotest/autotest-clients | 12 + jni/pachi/t-play/autotest/autotest-gather | 48 + jni/pachi/t-play/autotest/autotest-lib | 17 + jni/pachi/t-play/autotest/autotest-prune | 18 + jni/pachi/t-play/autotest/autotest-show | 54 + jni/pachi/t-play/autotest/autotest-worker | 123 + jni/pachi/t-play/autotest/rc | 58 + jni/pachi/t-play/resum | 9 + jni/pachi/t-play/test_in_context.sh | 15 + jni/pachi/t-regress/README | 42 + jni/pachi/t-regress/TODO | 88 + .../by-falseeye/2011-06-05-Zen19-pachi2.sgf | Bin 0 -> 80 bytes .../2011-06-05-tazaki-pachi30s.sgf | Bin 0 -> 86 bytes .../2011-06-09-botkiller2-pachi30s.sgf | Bin 0 -> 94 bytes .../2011-06-10-pachi30s-samba-2.sgf | Bin 0 -> 88 bytes .../2011-06-18-dorabon-pachi2-4.sgf | Bin 0 -> 88 bytes .../by-falseeye/2011-07-28-pachi2-Novicer.sgf | Bin 0 -> 84 bytes .../2011-08-12-xiaosugi-pachi2.sgf | Bin 0 -> 86 bytes .../by-ko/2011-06-09-jinen-pachi30s-2.sgf | Bin 0 -> 88 bytes .../by-ladder/2011-01-11-llopl-pachi2.sgf | Bin 0 -> 80 bytes .../by-ladder/2011-06-06-tyzef-pachi30s.sgf | Bin 0 -> 84 bytes .../by-ladder/2011-06-13-pachi30s-Jep.sgf | Bin 0 -> 80 bytes .../by-ladder/2011-07-28-pachi2-pkunzip-2.sgf | Bin 0 -> 88 bytes .../by-ladder/2011-08-08-Dallas-pachi2-2.sgf | Bin 0 -> 86 bytes .../by-ladder/2011-08-09-pachi2-BlueSpark.sgf | Bin 0 -> 88 bytes .../by-ladder/2011-08-09-somrak-pachi2-2.sgf | Bin 0 -> 86 bytes .../by-ladder/2011-08-24-StoneGrid-pachi2.sgf | Bin 0 -> 88 bytes .../by-ladder/2011-09-04-pachi2-stv.sgf | Bin 0 -> 76 bytes .../by-ladder/2012-03-16-IMC-pachi2.sgf | Bin 0 -> 76 bytes .../by-semeai/2011-01-15-pachi2-rollingon.sgf | Bin 0 -> 88 bytes .../by-semeai/2011-06-05-Zen19-pachi2.sgf | Bin 0 -> 80 bytes .../by-semeai/2011-06-05-tazaki-pachi30s.sgf | Bin 0 -> 86 bytes .../by-semeai/2011-06-18-dorabon-pachi2-4.sgf | Bin 0 -> 88 bytes .../by-semeai/2012-03-15-pachi2-Leech.sgf | Bin 0 -> 80 bytes .../games/2010-12-05-pachi-CzechBot.sgf | 126 + .../games/2011-01-11-llopl-pachi2.sgf | 54 + .../games/2011-01-15-pachi2-rollingon.sgf | 189 + .../games/2011-06-05-Zen19-pachi2.sgf | 429 + .../games/2011-06-05-tazaki-pachi30s.sgf | 401 + .../games/2011-06-06-tyzef-pachi30s.sgf | 31 + .../games/2011-06-07-fidibus-pachi30s.sgf | 233 + .../games/2011-06-09-botkiller2-pachi30s.sgf | 372 + .../games/2011-06-09-jinen-pachi30s-2.sgf | 257 + .../games/2011-06-10-pachi30s-samba-2.sgf | 335 + .../games/2011-06-13-pachi30s-Jep.sgf | 188 + .../games/2011-06-18-dorabon-pachi2-4.sgf | 344 + .../games/2011-07-28-pachi2-Novicer.sgf | 325 + .../games/2011-07-28-pachi2-pkunzip-2.sgf | 303 + .../games/2011-08-08-Dallas-pachi2-2.sgf | 193 + .../games/2011-08-09-pachi2-BlueSpark.sgf | 158 + .../games/2011-08-09-somrak-pachi2-2.sgf | 193 + .../games/2011-08-12-xiaosugi-pachi2.sgf | 320 + .../games/2011-08-24-StoneGrid-pachi2.sgf | 391 + .../t-regress/games/2011-09-04-pachi2-stv.sgf | 123 + .../games/2012-01-17-pachi2-Soromon-3.sgf | 291 + .../games/2012-03-15-pachi2-Leech.sgf | 247 + .../t-regress/games/2012-03-16-IMC-pachi2.sgf | 100 + .../games/2012-03-21-pachi2-Hujisawa-3.sgf | 531 + jni/pachi/t-regress/test-game.sh | 21 + jni/pachi/t-unit/Makefile | 12 + jni/pachi/t-unit/README | 8 + jni/pachi/t-unit/sar.t | 237 + jni/pachi/t-unit/test.c | 112 + jni/pachi/t-unit/test.h | 6 + jni/pachi/tactics/1lib.c | 160 + jni/pachi/tactics/1lib.h | 24 + jni/pachi/tactics/2lib.c | 251 + jni/pachi/tactics/2lib.h | 15 + jni/pachi/tactics/Makefile | 12 + jni/pachi/tactics/ladder.c | 229 + jni/pachi/tactics/ladder.h | 45 + jni/pachi/tactics/nakade.c | 82 + jni/pachi/tactics/nakade.h | 14 + jni/pachi/tactics/nlib.c | 87 + jni/pachi/tactics/nlib.h | 13 + jni/pachi/tactics/selfatari.c | 566 + jni/pachi/tactics/selfatari.h | 36 + jni/pachi/tactics/util.c | 124 + jni/pachi/tactics/util.h | 88 + jni/pachi/timeinfo.c | 446 + jni/pachi/timeinfo.h | 116 + jni/pachi/tools/autobook/README | 29 + jni/pachi/tools/autobook/autobook.sh | 19 + jni/pachi/tools/autobook/autobook2fbook.sh | 31 + jni/pachi/tools/autobook/eval.sh | 24 + jni/pachi/tools/autobook/expand.sh | 25 + jni/pachi/tools/autobook/walk.sh | 96 + jni/pachi/tools/complete-tromptaylor.gtp | 66 + jni/pachi/tools/complete.gtp | 252 + jni/pachi/tools/genmove.gtp | 5 + jni/pachi/tools/genmove19.gtp | 5 + jni/pachi/tools/gentbook.sh | 33 + jni/pachi/tools/kgslog2gtp.pl | 30 + jni/pachi/tools/pattern3_show.pl | 18 + jni/pachi/tools/pattern_bayes_gen.sh | 31 + jni/pachi/tools/pattern_bayes_merge.sh | 21 + jni/pachi/tools/pattern_byplayer.sh | 46 + jni/pachi/tools/pattern_getdrops.pl | 40 + jni/pachi/tools/pattern_spatial_gen.sh | 32 + jni/pachi/tools/pattern_spatial_show.pl | 41 + jni/pachi/tools/sgf-analyse.pl | 65 + jni/pachi/tools/sgf-ratemove.sh | 31 + jni/pachi/tools/sgf2gtp.pl | 83 + jni/pachi/tools/sgf2gtp.py | 152 + jni/pachi/tools/sgflib/.gitignore | 1 + jni/pachi/tools/sgflib/__init__.py | 1 + jni/pachi/tools/sgflib/sgflib.py | 652 + jni/pachi/tools/sgflib/typelib.py | 546 + jni/pachi/tools/spirit.gtp | 37 + jni/pachi/tools/twogtp.py | 703 + jni/pachi/uct/Makefile | 12 + jni/pachi/uct/dynkomi.c | 601 + jni/pachi/uct/dynkomi.h | 63 + jni/pachi/uct/internal.h | 173 + jni/pachi/uct/plugin.h | 66 + jni/pachi/uct/plugin/example.c | 150 + jni/pachi/uct/plugin/wolf.c | 386 + jni/pachi/uct/plugins.c | 126 + jni/pachi/uct/plugins.h | 20 + jni/pachi/uct/policy/Makefile | 12 + jni/pachi/uct/policy/generic.c | 60 + jni/pachi/uct/policy/generic.h | 79 + jni/pachi/uct/policy/ucb1.c | 116 + jni/pachi/uct/policy/ucb1amaf.c | 407 + jni/pachi/uct/prior.c | 328 + jni/pachi/uct/prior.h | 46 + jni/pachi/uct/search.c | 519 + jni/pachi/uct/search.h | 77 + jni/pachi/uct/slave.c | 554 + jni/pachi/uct/slave.h | 17 + jni/pachi/uct/tree.c | 805 + jni/pachi/uct/tree.h | 194 + jni/pachi/uct/uct.c | 1207 + jni/pachi/uct/uct.h | 14 + jni/pachi/uct/walk.c | 633 + jni/pachi/uct/walk.h | 15 + jni/pachi/util.h | 78 + jni/pachi/version.h | 39 + 237 files changed, 64140 insertions(+) create mode 100644 .gitignore create mode 100644 android-pachi.iml create mode 100644 debug.keystore create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 jni/Android.mk create mode 100644 jni/Application.mk create mode 100644 jni/pachi/.gitignore create mode 100644 jni/pachi/Android.mk create mode 100644 jni/pachi/COPYING create mode 100644 jni/pachi/CREDITS create mode 100644 jni/pachi/HACKING create mode 100644 jni/pachi/Makefile create mode 100644 jni/pachi/README create mode 100644 jni/pachi/TODO create mode 100644 jni/pachi/android/util.c create mode 100644 jni/pachi/android/util.h create mode 100644 jni/pachi/board.c create mode 100644 jni/pachi/board.h create mode 100644 jni/pachi/book.dat.bad create mode 100644 jni/pachi/book.dat.extra create mode 100644 jni/pachi/chat.c create mode 100644 jni/pachi/chat.h create mode 100644 jni/pachi/debug.h create mode 100644 jni/pachi/distributed/Makefile create mode 100644 jni/pachi/distributed/distributed.c create mode 100644 jni/pachi/distributed/distributed.h create mode 100644 jni/pachi/distributed/merge.c create mode 100644 jni/pachi/distributed/merge.h create mode 100644 jni/pachi/distributed/protocol.c create mode 100644 jni/pachi/distributed/protocol.h create mode 100644 jni/pachi/engine.h create mode 100644 jni/pachi/fbook.c create mode 100644 jni/pachi/fbook.h create mode 100644 jni/pachi/fixp.h create mode 100644 jni/pachi/gtp.c create mode 100644 jni/pachi/gtp.h create mode 100644 jni/pachi/joseki/Makefile create mode 100644 jni/pachi/joseki/README create mode 100644 jni/pachi/joseki/base.c create mode 100644 jni/pachi/joseki/base.h create mode 100644 jni/pachi/joseki/joseki.c create mode 100644 jni/pachi/joseki/joseki.h create mode 100644 jni/pachi/joseki/sgfvar2gtp.pl create mode 100644 jni/pachi/joseki19.pdict create mode 100644 jni/pachi/media/pachi-small.png create mode 100644 jni/pachi/media/pachi.jpg create mode 100644 jni/pachi/montecarlo/Makefile create mode 100644 jni/pachi/montecarlo/internal.h create mode 100644 jni/pachi/montecarlo/montecarlo.c create mode 100644 jni/pachi/montecarlo/montecarlo.h create mode 100644 jni/pachi/move.c create mode 100644 jni/pachi/move.h create mode 100644 jni/pachi/mq.h create mode 100644 jni/pachi/network.c create mode 100644 jni/pachi/network.h create mode 100644 jni/pachi/ownermap.c create mode 100644 jni/pachi/ownermap.h create mode 100644 jni/pachi/pachi.c create mode 100644 jni/pachi/pattern.c create mode 100644 jni/pachi/pattern.h create mode 100644 jni/pachi/pattern3.c create mode 100644 jni/pachi/pattern3.h create mode 100644 jni/pachi/patternplay/Makefile create mode 100644 jni/pachi/patternplay/patternplay.c create mode 100644 jni/pachi/patternplay/patternplay.h create mode 100644 jni/pachi/patternprob.c create mode 100644 jni/pachi/patternprob.h create mode 100644 jni/pachi/patternscan/Makefile create mode 100644 jni/pachi/patternscan/patternscan.c create mode 100644 jni/pachi/patternscan/patternscan.h create mode 100644 jni/pachi/patternsp.c create mode 100644 jni/pachi/patternsp.h create mode 100644 jni/pachi/playout.c create mode 100644 jni/pachi/playout.h create mode 100644 jni/pachi/playout/Makefile create mode 100644 jni/pachi/playout/light.c create mode 100644 jni/pachi/playout/light.h create mode 100644 jni/pachi/playout/moggy.c create mode 100644 jni/pachi/playout/moggy.h create mode 100644 jni/pachi/probdist.c create mode 100644 jni/pachi/probdist.h create mode 100644 jni/pachi/random.c create mode 100644 jni/pachi/random.h create mode 100644 jni/pachi/random/Makefile create mode 100644 jni/pachi/random/random.c create mode 100644 jni/pachi/random/random.h create mode 100644 jni/pachi/replay/Makefile rename jni/pachi/replay/{replay.c~ => replay.c} (100%) create mode 100644 jni/pachi/replay/replay.h create mode 100644 jni/pachi/stats.h create mode 100644 jni/pachi/stone.c create mode 100644 jni/pachi/stone.h create mode 100644 jni/pachi/t-play/TESTS create mode 100644 jni/pachi/t-play/autotest/README create mode 100644 jni/pachi/t-play/autotest/TODO create mode 100644 jni/pachi/t-play/autotest/autotest-client create mode 100644 jni/pachi/t-play/autotest/autotest-clients create mode 100644 jni/pachi/t-play/autotest/autotest-gather create mode 100644 jni/pachi/t-play/autotest/autotest-lib create mode 100644 jni/pachi/t-play/autotest/autotest-prune create mode 100644 jni/pachi/t-play/autotest/autotest-show create mode 100644 jni/pachi/t-play/autotest/autotest-worker create mode 100644 jni/pachi/t-play/autotest/rc create mode 100644 jni/pachi/t-play/resum create mode 100644 jni/pachi/t-play/test_in_context.sh create mode 100644 jni/pachi/t-regress/README create mode 100644 jni/pachi/t-regress/TODO create mode 100644 jni/pachi/t-regress/by-falseeye/2011-06-05-Zen19-pachi2.sgf create mode 100644 jni/pachi/t-regress/by-falseeye/2011-06-05-tazaki-pachi30s.sgf create mode 100644 jni/pachi/t-regress/by-falseeye/2011-06-09-botkiller2-pachi30s.sgf create mode 100644 jni/pachi/t-regress/by-falseeye/2011-06-10-pachi30s-samba-2.sgf create mode 100644 jni/pachi/t-regress/by-falseeye/2011-06-18-dorabon-pachi2-4.sgf create mode 100644 jni/pachi/t-regress/by-falseeye/2011-07-28-pachi2-Novicer.sgf create mode 100644 jni/pachi/t-regress/by-falseeye/2011-08-12-xiaosugi-pachi2.sgf create mode 100644 jni/pachi/t-regress/by-ko/2011-06-09-jinen-pachi30s-2.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-01-11-llopl-pachi2.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-06-06-tyzef-pachi30s.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-06-13-pachi30s-Jep.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-07-28-pachi2-pkunzip-2.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-08-08-Dallas-pachi2-2.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-08-09-pachi2-BlueSpark.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-08-09-somrak-pachi2-2.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-08-24-StoneGrid-pachi2.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2011-09-04-pachi2-stv.sgf create mode 100644 jni/pachi/t-regress/by-ladder/2012-03-16-IMC-pachi2.sgf create mode 100644 jni/pachi/t-regress/by-semeai/2011-01-15-pachi2-rollingon.sgf create mode 100644 jni/pachi/t-regress/by-semeai/2011-06-05-Zen19-pachi2.sgf create mode 100644 jni/pachi/t-regress/by-semeai/2011-06-05-tazaki-pachi30s.sgf create mode 100644 jni/pachi/t-regress/by-semeai/2011-06-18-dorabon-pachi2-4.sgf create mode 100644 jni/pachi/t-regress/by-semeai/2012-03-15-pachi2-Leech.sgf create mode 100644 jni/pachi/t-regress/games/2010-12-05-pachi-CzechBot.sgf create mode 100644 jni/pachi/t-regress/games/2011-01-11-llopl-pachi2.sgf create mode 100644 jni/pachi/t-regress/games/2011-01-15-pachi2-rollingon.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-05-Zen19-pachi2.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-05-tazaki-pachi30s.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-06-tyzef-pachi30s.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-07-fidibus-pachi30s.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-09-botkiller2-pachi30s.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-09-jinen-pachi30s-2.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-10-pachi30s-samba-2.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-13-pachi30s-Jep.sgf create mode 100644 jni/pachi/t-regress/games/2011-06-18-dorabon-pachi2-4.sgf create mode 100644 jni/pachi/t-regress/games/2011-07-28-pachi2-Novicer.sgf create mode 100644 jni/pachi/t-regress/games/2011-07-28-pachi2-pkunzip-2.sgf create mode 100644 jni/pachi/t-regress/games/2011-08-08-Dallas-pachi2-2.sgf create mode 100644 jni/pachi/t-regress/games/2011-08-09-pachi2-BlueSpark.sgf create mode 100644 jni/pachi/t-regress/games/2011-08-09-somrak-pachi2-2.sgf create mode 100644 jni/pachi/t-regress/games/2011-08-12-xiaosugi-pachi2.sgf create mode 100644 jni/pachi/t-regress/games/2011-08-24-StoneGrid-pachi2.sgf create mode 100644 jni/pachi/t-regress/games/2011-09-04-pachi2-stv.sgf create mode 100644 jni/pachi/t-regress/games/2012-01-17-pachi2-Soromon-3.sgf create mode 100644 jni/pachi/t-regress/games/2012-03-15-pachi2-Leech.sgf create mode 100644 jni/pachi/t-regress/games/2012-03-16-IMC-pachi2.sgf create mode 100644 jni/pachi/t-regress/games/2012-03-21-pachi2-Hujisawa-3.sgf create mode 100644 jni/pachi/t-regress/test-game.sh create mode 100644 jni/pachi/t-unit/Makefile create mode 100644 jni/pachi/t-unit/README create mode 100644 jni/pachi/t-unit/sar.t create mode 100644 jni/pachi/t-unit/test.c create mode 100644 jni/pachi/t-unit/test.h create mode 100644 jni/pachi/tactics/1lib.c create mode 100644 jni/pachi/tactics/1lib.h create mode 100644 jni/pachi/tactics/2lib.c create mode 100644 jni/pachi/tactics/2lib.h create mode 100644 jni/pachi/tactics/Makefile create mode 100644 jni/pachi/tactics/ladder.c create mode 100644 jni/pachi/tactics/ladder.h create mode 100644 jni/pachi/tactics/nakade.c create mode 100644 jni/pachi/tactics/nakade.h create mode 100644 jni/pachi/tactics/nlib.c create mode 100644 jni/pachi/tactics/nlib.h create mode 100644 jni/pachi/tactics/selfatari.c create mode 100644 jni/pachi/tactics/selfatari.h create mode 100644 jni/pachi/tactics/util.c create mode 100644 jni/pachi/tactics/util.h create mode 100644 jni/pachi/timeinfo.c create mode 100644 jni/pachi/timeinfo.h create mode 100644 jni/pachi/tools/autobook/README create mode 100644 jni/pachi/tools/autobook/autobook.sh create mode 100644 jni/pachi/tools/autobook/autobook2fbook.sh create mode 100644 jni/pachi/tools/autobook/eval.sh create mode 100644 jni/pachi/tools/autobook/expand.sh create mode 100644 jni/pachi/tools/autobook/walk.sh create mode 100644 jni/pachi/tools/complete-tromptaylor.gtp create mode 100644 jni/pachi/tools/complete.gtp create mode 100644 jni/pachi/tools/genmove.gtp create mode 100644 jni/pachi/tools/genmove19.gtp create mode 100644 jni/pachi/tools/gentbook.sh create mode 100644 jni/pachi/tools/kgslog2gtp.pl create mode 100644 jni/pachi/tools/pattern3_show.pl create mode 100644 jni/pachi/tools/pattern_bayes_gen.sh create mode 100644 jni/pachi/tools/pattern_bayes_merge.sh create mode 100644 jni/pachi/tools/pattern_byplayer.sh create mode 100644 jni/pachi/tools/pattern_getdrops.pl create mode 100644 jni/pachi/tools/pattern_spatial_gen.sh create mode 100644 jni/pachi/tools/pattern_spatial_show.pl create mode 100644 jni/pachi/tools/sgf-analyse.pl create mode 100644 jni/pachi/tools/sgf-ratemove.sh create mode 100644 jni/pachi/tools/sgf2gtp.pl create mode 100644 jni/pachi/tools/sgf2gtp.py create mode 100644 jni/pachi/tools/sgflib/.gitignore create mode 100644 jni/pachi/tools/sgflib/__init__.py create mode 100644 jni/pachi/tools/sgflib/sgflib.py create mode 100644 jni/pachi/tools/sgflib/typelib.py create mode 100644 jni/pachi/tools/spirit.gtp create mode 100644 jni/pachi/tools/twogtp.py create mode 100644 jni/pachi/uct/Makefile create mode 100644 jni/pachi/uct/dynkomi.c create mode 100644 jni/pachi/uct/dynkomi.h create mode 100644 jni/pachi/uct/internal.h create mode 100644 jni/pachi/uct/plugin.h create mode 100644 jni/pachi/uct/plugin/example.c create mode 100644 jni/pachi/uct/plugin/wolf.c create mode 100644 jni/pachi/uct/plugins.c create mode 100644 jni/pachi/uct/plugins.h create mode 100644 jni/pachi/uct/policy/Makefile create mode 100644 jni/pachi/uct/policy/generic.c create mode 100644 jni/pachi/uct/policy/generic.h create mode 100644 jni/pachi/uct/policy/ucb1.c create mode 100644 jni/pachi/uct/policy/ucb1amaf.c create mode 100644 jni/pachi/uct/prior.c create mode 100644 jni/pachi/uct/prior.h create mode 100644 jni/pachi/uct/search.c create mode 100644 jni/pachi/uct/search.h create mode 100644 jni/pachi/uct/slave.c create mode 100644 jni/pachi/uct/slave.h create mode 100644 jni/pachi/uct/tree.c create mode 100644 jni/pachi/uct/tree.h create mode 100644 jni/pachi/uct/uct.c create mode 100644 jni/pachi/uct/uct.h create mode 100644 jni/pachi/uct/walk.c create mode 100644 jni/pachi/uct/walk.h create mode 100644 jni/pachi/util.h create mode 100644 jni/pachi/version.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c0d54c --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.gradle +.DS_Store +/local.properties +/.idea +/.gradle +/captures +/build +*/build +/libs +/obj + +make_release.bat +release.keystore diff --git a/android-pachi.iml b/android-pachi.iml new file mode 100644 index 0000000..9d0597c --- /dev/null +++ b/android-pachi.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/debug.keystore b/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..1779342655f1862d66aabff795b614b5b95a8a29 GIT binary patch literal 2146 zcmbW1Ydq5p8^`zG!D6;zV{#nE&17wCNjdE<5+OOO(PEaI4MxNr}Z9WcY;7LfW(1rl29UIS|R`xAb=$R00x3`p!4qo$^O>C_4usX zedt&_I=&`!t>;*`>3eS*%x{HR@dpQ-m*T$nI9cIF8B(&FEsMAFtg|l2lnS}l1gUZ1 zL*5Ib_poC^3I%2V2NkaR_DZx)UhnN2Jz4}q`+J?!PN6yjZS@=5P6t%633I9g$GO7t zm$mMOccb7#6^-+QqpCiFTXP?rT_LFKBv^v0=t7!Dkk$t?QqU)M;{zshe^HFx5$mS!AK? zdIZs$C2Q(G5QQzh8yP{FCLeDXi>&SYGDuwewqhn4sV|FBntbc)sWRV0u^GYIVsreG z6LEyHEd^kpm5J9hNDXu@6(l)s@P}>;GfZEb7T@6R-G3@|bC6LyjP|*(nt`;a36l?| zU8lX7yY%$8YNKZUc6z%tVgHkuPQ(qkv1!g6+CcFv0%@cwmIqtdi@Namk;mi%+^Lxl|!*(-$NZ}AEh zn)oy6kQh4+660$jcZ8n7m@Gc^M-#I{?|YfEDej2hLA*=^g1}_cWAKevqXZ+D`)e-O z%ziw`@#Qrfxk$WgVF{PXl1)cxA!*E9U;CM%ln#}D>}@yNoJAvN^b z;e5HEXF<5mwwTm3CCn1?IHPwq@ByJeE|Pp$MEQ&7<_mQ%D^mk2KVd~iuX8E0tN3DI z%HHca&PnX4GoMpO?7}y*H!~I?sg|K=qS94;Q}OCES&OP5&@Rg1s))Hy2;ZbvSvHf<4Bu9yvd@jRB%nJ@$9*v1V?Q&ED zq*`HQ{@8_G>I?4JJ;y+~f!?9;@X9M!4;}UKu#hF_W{&G_PzR}^+T*JS!WgUuj>98~ z(fgf#Hqg4FhGkWj2TUQXERdWX_d}{r?(2w#HGvum!HjfmFjC7FPc^ z@tBheIX&8URbZY@5*bAW;gekh;^P!rrAER{O{PF1DMCv(|0Y64@1gK0-AHF6h}ZhA zJBI{(*Rx^S8E+N)cDV6Q%jce$7gq$?TbfTA=hkaa5&O5?A^w4ast^U1_(FH`N!BH& zOs-2EKPIz!tnM<+ueLl*a@p5zr94=KHrtW@x#&mpLVAhCEqmz$1Bwk}OIEK6f*!V% zRVl5doGF7p#bcVd)#l84(8!QeNwC#gZbJYfPiJW=DDIl(%H_CT^?RBAdGd+8^znHa z`{uKx%HNY`yiluax1RS|j3@fgrUZE8y<=f$EzGdlM2mJ4KMmuko-FX~xA>ml`xBIo zJhXy?OvauFzlIl7``vo{q)F3ovcCZz^`nSKTkpP~oM0;!6E_Agsnt*X|q`T>#%njS2G0EWBU+OlUTX_ure2b$t;lMIcM@a}>kc+DxmFtHr_FA2m%0~{F0`8qz!P5!1UmZ9kK6RTF_48-6Lc)aX zn7xD5Xf}soRGbe9NgS!nkWqchj#XU zNS5qS)?9z{#PPNMUu|Zu$*Dcvf#l%wJauojMWhSA67}R!^?2K~^ENWk!2Bc|Pk-lL z8eJomzo~Dl7hDx6vFWN;vKDHQ62S~mpFvxzE{T7kjpiB4J9Q93818{PVJe8xi@qPt z-Sy`JG+NEDUz~dU%*h_6fqb9mG)sdw>cuE`g**Qw{q=rFVJFLc^7 zPHMlBOfjTK3*rqA_eG1$OgHAnZY&R4RvB$)PkX4Q6q@fB-uj;IaYTa4=B|_J*)FFJ z$LV^_OqhPCZxq2+@9B2iWp=`FsI`Y)xG2!*1N`~t@ieMZUVtXT&z#n_cIUDiZ Ye5IyPGSUrxCqQo6nd)QKV$g0EC2ui literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 0000000..9a04b8e --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1,4 @@ +APP_PROJECT_PATH := $(call my-dir)/.. +APP_MODULES := pachi +APP_ABI := armeabi armeabi-v7a x86 +APP_OPTIM := release diff --git a/jni/pachi/.gitignore b/jni/pachi/.gitignore new file mode 100644 index 0000000..0828443 --- /dev/null +++ b/jni/pachi/.gitignore @@ -0,0 +1,9 @@ +.deps +.*.swp +*.o +*.a +pachi +tags +book.dat +patterns.prob +patterns.spat diff --git a/jni/pachi/Android.mk b/jni/pachi/Android.mk new file mode 100644 index 0000000..8b86e41 --- /dev/null +++ b/jni/pachi/Android.mk @@ -0,0 +1,78 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +APP_PLATFORM := android-5 +LOCAL_MODULE := pachi + +LOCAL_LDFLAGS += -fPIE -pie +LOCAL_CPP_FLAGS := -fno-exceptions -fPIE -pie +LOCAL_CFLAGS := -std=c99 -fPIE -pie \ +-I$(LOCAL_PATH) \ +-I$(LOCAL_PATH)/joseki \ +-I$(LOCAL_PATH)/distributed \ +-I$(LOCAL_PATH)/playout \ +-I$(LOCAL_PATH)/montecarlo \ +-I$(LOCAL_PATH)/random \ +-I$(LOCAL_PATH)/patternplay \ +-I$(LOCAL_PATH)/patternscan \ +-I$(LOCAL_PATH)/replay \ +-I$(LOCAL_PATH)/tactics \ +-I$(LOCAL_PATH)/uct \ +-I$(LOCAL_PATH)/uct/policy + +LOCAL_SRC_FILES := \ +\ +android/util.c \ +fbook.c \ +board.c \ +network.c \ +gtp.c \ +chat.c \ +move.c \ +ownermap.c \ +pachi.c \ +pattern.c \ +pattern3.c \ +patternprob.c \ +patternsp.c \ +playout.c \ +probdist.c \ +random.c \ +stone.c \ +timeinfo.c \ +t-unit/test.c \ +montecarlo/montecarlo.c \ +joseki/base.c \ +joseki/joseki.c \ +distributed/distributed.c \ +distributed/merge.c \ +distributed/protocol.c \ +patternplay/patternplay.c \ +patternscan/patternscan.c \ +playout/light.c \ +playout/moggy.c \ +random/random.c \ +replay/replay.c \ +tactics/1lib.c \ +tactics/2lib.c \ +tactics/ladder.c \ +tactics/nakade.c \ +tactics/nlib.c \ +tactics/selfatari.c \ +tactics/util.c \ +uct/policy/generic.c \ +uct/policy/ucb1.c \ +uct/policy/ucb1amaf.c \ +uct/dynkomi.c \ +uct/plugins.c \ +uct/prior.c \ +uct/search.c \ +uct/slave.c \ +uct/tree.c \ +uct/uct.c \ +uct/walk.c \ +#uct/plugin/example.c \ +#uct/plugin/wolf.c \ + +include $(BUILD_EXECUTABLE) diff --git a/jni/pachi/COPYING b/jni/pachi/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/jni/pachi/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/jni/pachi/CREDITS b/jni/pachi/CREDITS new file mode 100644 index 0000000..472b210 --- /dev/null +++ b/jni/pachi/CREDITS @@ -0,0 +1,29 @@ +Pachi stands on research shoulders of giants, specifically: + +Modification of UCT with Patterns in Monte-Carlo Go + Sylvain Gelly, Yizao Wang, Remi Munos & Olivier Teytaud + +Combining Online and Offline Knowledge in UCT + Sylvain Gelly, David Silver + + +Pachi contains unpublished methods we claim we have invented +independently and are preparing for publication: + +* The dynamic komi feature. +* The local tree feature. + + +These people have made significant contributions to the code: + +Petr Baudis + Pachi the Bot ;-) + +Jean-loup Gaily + Distributed engine + Time allocation algorithm + Efficient UCT memory allocator + + +Pachi uses data extracted from the Kogo joseki dictionary as its +joseki database: http://waterfire.us/joseki.htm diff --git a/jni/pachi/HACKING b/jni/pachi/HACKING new file mode 100644 index 0000000..1cfa226 --- /dev/null +++ b/jni/pachi/HACKING @@ -0,0 +1,306 @@ +This is brief developer-oriented overview in Pachi structure. + +Pachi is completely Go-specific (c.f. Fuego; though e.g. atari go support +should be easy to add), but fairly modular. It has been built with focus +on MonteCarlo-based play, but it can in principle be used for other +play engines as well. + + +Basic architecture +================== + +Pachi consists of the following components: + + + +------+ +--------+ +---------+ +------------------+ + | core | -- | engine | -- | playout | -- | tactical library | + +------+ +--------+ +---------+ +------------------+ + | | + +-------------+ + | aux library | + +-------------+ + +* "core" takes care of the program's lifetime, GTP interface and basic + fast Go board implementation + + pachi.c global initialization and the main loop + version.h current version information + debug.h debugging infrastructure + random.[ch] fast random number generator + gtp.[ch] GTP protocol interface + network.[ch] Network interface (useful for distributed engine) + timeinfo.[ch] Time-keeping information + stone.[ch] one board point coloring definition + move.[ch] one board move definition + board.[ch] board definition and basic interface + +* "aux library" provides extra functions like static tactical evaluation + and pattern matching; it is somewhat interwound with "core" component + + mq.h "move queue" data structure + stats.h "move statistics" data structure + probdist.[ch] "probability distribution" data structure + ownermap.[ch] simulation-based finalpos. "owner map" data structure + pattern3.[ch] fast 3x3 spatial pattern matcher + pattern.[ch] general multi-feature pattern matcher + +* "tactical library" provides extended interfaces for the go board, + most important non-trivial tactical information + + tactics/ + +* "engine" receives notifications about opponent moves and is asked + to generate a move to play on given board + + engine.h abstract engine interface + random/ example "random move generator" engine + replay/ example "playout move generator" engine + montecarlo/ simple treeless Monte Carlo engine, quite bitrotten + uct/ the main UCT-player engine, see below + distributed/ "meta-engine" for distributed play by orchestrating + several UCT engines on different computers + patternscan/ auxiliary engine for harvesting patterns from + existing games + joseki/ auxiliary engine for generating joseki patterns + +* "playout" policy is asked to generate moves to play during the Monte Carlo + simulations, and to provide rough evaluation of moves feasibility for + the engine + + playout.[ch] abstract playout policy interface, + Monte Carlo simulation execution + playout/light uniformly random playout policy + playout/moggy rule-based "Mogo-like" playout policy + +* Also, several ways of testing Pachi are provided: + + t-unit/ interface for writing unit-tests for specific + functionality, mainly tactics + t-play/ interface for testing performance by playing games + against a fixed opponent (e.g. GNUGo) + + +UCT architecture +================ + +The UCT engine (the proper name should be MCTS as it does not have that +much common with classic UCT now) has non-trivial structure by itself: + + +-------------+ +-----+ +-------------------+ + | node policy | -- | UCT | --- | node prior-hinter | + +-------------+ +-----+ +-------------------+ + | | + +---------+ | + | playout | -----' + +---------+ + +* "UCT" is the core of the engine + + uct.[ch] engine initialization, public interface + internal.h internal state and data structures + tree.[ch] minimax move tree with success statistics + walk.[ch] filling the tree by walking it many times + and running MC simulations from leaves + slave.[ch] engine interface for the distributed engine + +* "node prior-hinter" assigns newly created nodes preliminary success + statistics ("prior values") to focus the search better + + prior.[ch] variety of methods for setting the priors + +* "node policy" mainly chooses the current node's child to descend + through during the tree walk, based on the already recorded statistics; + it must balance exploration and exploitation well during the selection + + policy/ucb1 the old-school original simple policy + policy/ucb1amaf the AMAF/RAVE-based policy gathering statistics rapidly + +* "dynkomi driver" dynamically determines self-imposed extra virtual komi + + dynkomi.[ch] + + +Board Implementation +==================== + +The infrastructure is optimized for speed to make it well suited +for bruteforce engines, however tradeoffs are made to make it useful +for heavier MonteCarlo playouts as well (e.g. real liberties are +tracked instead of pseudoliberties). If you are looking for raw +light playout speed, libEGO is better choice. + +In general, arbitrary board sizes are supported; however, board sizes +smaller than 9x9 have not been tested and board sizes larger than 25x25 +are not supported by GTP - also, in theory some buffer overflows might +happen with board sizes larger than 19x19. The engine parameters are +primarily tuned for 19x19 play. + +Ruleset +------- + +While the Pachi engines generally play according to Chinese rules, +internally, Pachi uses Tromp-Taylor rules because they are simple, +fast and universal; they are very close to the New Zealand rules. +That means, it simply counts the number of stones and one-point eyes +of each color on the board, plus komi and handicap correction. + +Tromp-Taylor rules also mean that multi-stone suicide is allowed! If you +do not like that (basically if you want to pretend it plays according +to Chinese rules), you need to rule that out in your engine, currently. +The provided engines DO avoid multi-stone suicide, though it is allowed +in the playouts for performance reasons (perhaps we should re-visit that +decision in light of heavy playouts). + +Tromp-Taylor rules have positional superko; the board implementation +will set a flag if it is violated, but play the move anyway. You need +to enforce the superko rule in your engine. + + +GTP Implementation +================== + +...is a very sad hack. ENSURE that only trusted parties talk to Pachi's +GTP interface, as it is totally non-resilient to any kind of overflow +or bad input attacks and allowing arbitrary input to be entered within +is a major security hole. Yes, this needs to be cleaned up. Also, currently +engines cannot plug in their own commands and there is no GoGui interface. + +Pachi supports only few GTP commands now. Most importantly, it does not +support the undo command. The final_status_list command requires engine +support. + + +General Pattern Matcher +======================= + +Pachi has in-development general pattern matcher that can match various +sets of features (spatial and others), inspired by the CrazyStone and +van der Werf - de Groot - Stern pattern model. Please see pattern.h +for detailed description of the pattern concept and recognized features. + +To harvest patterns, use 'pachi -e patternscan' (see patternscan/patternscan.c +for available options). The output of the pattern scanner are two data +structures: The matched patterns + + (feature1:payload feature2:payload ...) + +and spatial dictionary. "Spatial" feature represents a particular +configuration of stones in a circle around the move-to-play; each +configuration has its own record in the dictionary and the spatial +feature references only the id in the dictionary; so you need to keep +both the patterns and the "patterns.spat" file. Normally, 'patternscan' +will only match already existing dictionary entries, but you +can pass it "gen_spat_dict" so that it appends all newly found spatial +features to the dictionary - use "spat_threshold" to limit recording +only to frequently occuring spatial features; to start the dictionary +from scratch, simply remove any existing "patterns.spat" file. + +There are few pre-made scripts to make the initialization of the pattern +matcher easy: + +* tools/pattern_byplayer.sh: Sorts out patterns from given SGF collection + by player names, one player per file in a dedicated directory. This is + useful if you want to use the patterns to e.g. recognize games of a + player by characteristic patterns. Spatial dictionary is autogenerated + in full. + +* tools/pattern_spatial_gen.sh: Initializes spatial dictionary by spatial + features found at least N times in given SGF collection. This is useful + for further gathering of general pattern statistics while keeping + the amount of spatial features manageable. + +* tools/pattern_spatial_show.pl ID: Shows spatial pattern of given id + in 2D plane. + +* tools/pattern_bayes_gen.sh: Generates a Bayesian probability table + for each pattern in a SGF collection. The computed number is the empiric + probability of playing a given pattern when it is available on board. + +* tools/pattern_bayes_merge.sh: Merges Bayesian probability tables + of patterns. This is useful for parallel pattern mining - run multiple + pattern_bayes_gen (perhaps remove the counts >= 2 test) and then merge + the generated tables. + +* tools/pattern_getdrops.pl: Processes game logs to determine moves that + lead to large drop in Pachi's evaluation and extracts patterns that + represent these moves. This might allow Pachi to "learn from its past + mistakes" and recognize these moves in the future. A full pipeline + for this might be: + + cat kgsGtp.log | tools/kgslog2gtp.pl | + tools/pattern_getdrops.pl | tools/pattern_bayes_gen.sh - | + tools/pattern_bayes_merge.sh patterns.prob - >patterns.prob2 + + +Plugin API +========== + +The UCT engine allows external plugins to be loaded and provide external +knowledge for various heuristics - currently just biasing the MCTS priors, +but more can be added easily. The plugins should follow the API in + - see that file for details. + + +Joseki Patterns +=============== + +Joseki patterns can be generated from variations of a SGF file. Josekis +are matched per-quadrant, no other move must be within the quadrant. See +joseki/README for details on usage of the auxiliary engine and a full +recipe for making Pachi play according to joseki sequences extracted from +the Kogo dictionary. + + +Opening Book +============ + +The UCT engine can "pre-read" the starting board position and +dump the core of the built tree to a file, loading it later. This is +called a 'tbook' (as in "tree book") and can be generated using the +tools/gentbook.sh script. The newly generated file is automatically +used by the UCT engine when found. + +Alternatively, there is a support for directly used opening book +(so-called fbook, a.k.a. "forced book" or "fuseki book"). The book +is stored in a text file in Fuego-compatible format and can be loaded +using the ./pachi -f parameter. A naive way to build such a book +based on shell-based, twogtp-based UCT is available through the +tools/autobook/ framework. + + +Local Trees +=========== + +This is a mostly unique idea in Pachi, currently in development; +it does not work too well yet, but Pasky has great hopes for it in +the future. + +A local tree is a tree comprising of (CFG-topologically) local +sequences, built in parallel with the real game tree; there is +a separate tree for black-first and white-first sequences. E.g if +the game tree (on empty board) consists of D4-C6-D6-Q16-O17-D7, +the coresponding local tree sequences are (black) D4-C6-D6, (white) +Q16-O17 and (white) D7. The simulation result is then recorded as +usual in the game tree, but also in the corresponding local trees. + +The goal of this is to dynamically build a cache of various +resolutions of local situations. This is to overcome cases where +the proper resolution of a given situation is easily found in +the tree, but improper heuristical bisaes keep muddying the +evaluation down. + +Exactly what kind of values to store in the local tree nodes is +still an open question. The original approach has been to simply +use simulation results directly or biased on base "value temperature"; +now, we are experimenting with survival rate of the "base stone" +of the branch (the first move in the branch sequence). We also intend +to integrate criticality information with local tree data. + +The local trees are descended in parallel with the main tree. +The values stored in local trees are added to the RAVE term +from the main tree during node selection. We also wish to use +the information from local trees in the simulations, but this is +still subject to research. + +To enable local trees usage, pass local_tree=1 to the UCT engine. +There are many further knobs to twiddle, too. diff --git a/jni/pachi/Makefile b/jni/pachi/Makefile new file mode 100644 index 0000000..5e0fe18 --- /dev/null +++ b/jni/pachi/Makefile @@ -0,0 +1,137 @@ +#### CONFIGURATION + +# Uncomment one of the options below to change the way Pachi is built. +# Alternatively, you can pass the option to make itself, like: +# make MAC=1 DOUBLE=1 + + +# Do you compile on Windows instead of Linux? Please note that the +# performance may not be optimal. +# (XXX: For now, only the mingw target is supported on Windows. +# Patches for others are welcome!) + +# WIN=1 + +# Do you compile on MacOS/X instead of Linux? Please note that the +# performance may not be optimal. +# (XXX: We are looking for volunteers contributing support for other +# targets, like mingw/Windows.) + +# MAC=1 + +# By default, Pachi uses low-precision numbers within the game tree to +# conserve memory. This can become an issue with playout counts >1M, +# e.g. with extremely long thinking times or massive parallelization; +# 24 bits of floating_t mantissa become insufficient then. + +# DOUBLE=1 + +# Enable performance profiling using gprof. Note that this also disables +# inlining, which allows more fine-grained profile, but may also distort +# it somewhat. + +# PROFILING=gprof + +# Enable performance profiling using google-perftools. This is much +# more accurate, fine-grained and nicer than gprof and does not change +# the way the actual binary is compiled and runs. + +# PROFILING=perftools + + +# Target directories when running `make install`. Note that this is NOT +# quite supported yet - Pachi will work fine, but will always look for +# extra data files (such as pattern, joseki or fuseki database) only +# in the current directory, bundled database files will not be installed +# in a system directory or loaded from there. +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin + +# Generic compiler options. You probably do not really want to twiddle +# any of this. +# (N.B. -ffast-math breaks us; -fomit-frame-pointer is added below +# unless PROFILING=gprof.) +CUSTOM_CFLAGS=-Wall -ggdb3 -O3 -std=gnu99 -frename-registers -pthread -Wsign-compare -D_GNU_SOURCE + +### CONFIGURATION END + + +ifdef WIN + SYS_CFLAGS= + LDFLAGS=-pthread + LIBS=-lm -lws2_32 -lregex +else +ifdef MAC + SYS_CFLAGS=-DNO_THREAD_LOCAL + LDFLAGS=-pthread -rdynamic + LIBS=-lm -ldl +else + SYS_CFLAGS=-march=native + LDFLAGS=-pthread -rdynamic + LIBS=-lm -lrt -ldl +endif +endif + +ifdef DOUBLE + CUSTOM_CFLAGS+=-DDOUBLE +endif + +ifeq ($(PROFILING), gprof) + LDFLAGS+=-pg + CUSTOM_CFLAGS+=-pg -fno-inline +else + # Whee, an extra register! + CUSTOM_CFLAGS+=-fomit-frame-pointer +ifeq ($(PROFILING), perftools) + LIBS+=-lprofiler +endif +endif + +ifndef LD +LD=ld +endif + +ifndef AR +AR=ar +endif + +ifndef INSTALL +INSTALL=/usr/bin/install +endif + +export +unexport INCLUDES +INCLUDES=-I. + + +OBJS=board.o gtp.o move.o ownermap.o pattern3.o pattern.o patternsp.o patternprob.o playout.o probdist.o random.o stone.o timeinfo.o network.o fbook.o chat.o +SUBDIRS=random replay patternscan patternplay joseki montecarlo uct uct/policy playout tactics t-unit distributed + +all: all-recursive pachi + +LOCALLIBS=random/random.a replay/replay.a patternscan/patternscan.a patternplay/patternplay.a joseki/joseki.a montecarlo/montecarlo.a uct/uct.a uct/policy/uctpolicy.a playout/playout.a tactics/tactics.a t-unit/test.a distributed/distributed.a +$(LOCALLIBS): all-recursive + @ +pachi: $(OBJS) pachi.o $(LOCALLIBS) + $(call cmd,link) + +# Use runtime gcc profiling for extra optimization. This used to be a large +# bonus but nowadays, it's rarely worth the trouble. +.PHONY: pachi-profiled +pachi-profiled: + @make clean all XLDFLAGS=-fprofile-generate XCFLAGS="-fprofile-generate -fomit-frame-pointer -frename-registers" + ./pachi -t =5000 no_tbook symmetry.type = SYM_NONE. + + +Analysis +-------- + +Pachi can also help you analyze your games by being able to provide +its opinion on various positions. The user interface is very rudimentary, +but the ability is certainly there. + +There are currently several Pachi interfaces provided for this purpose. + +Winrate Development +~~~~~~~~~~~~~~~~~~~ + +Pachi can evaluate all moves within a given game and show how +the winrates for both players evolved - i.e. who was winning at which +game stage. This is implemented using the `tools/sgf-analyse.pl` script. +See the comment on top of the script about its usage. + +Move Ranking +~~~~~~~~~~~~ + +Pachi can evaluate all available moves in a given situation +and for each give a value between 0 and 1 representing perceived +likelihood of winning the game if one would play that move. I.e. it can +suggest which moves would be good and bad in a single given situation. + +To achieve the latter, note the number of move at the situation you +want to evaluate and run the `tools/sgf-ratemove.sh` script. +See the comment on top of the script about its usage. + +Pattern Move Hinting +~~~~~~~~~~~~~~~~~~~~ + +Pachi can show instantenous pattern-based move suggestions very much +like for example Moyo Go Studio (though of course without a GUI). +You can use the Move Ranking method above (tools/sgf-ratemove.sh), +but pass it an extra parameter '-e patternplay'. + + +Framework +--------- + +The aim of the software framework is to make it easy to plug your +engine to the common infrastructure and implement your ideas while +minimalizing the overhead of implementing the GTP, speed-optimized +board implementation, etc. Also, there are premade random playout +and UCT tree engines, so that you can directly tweak only particular +policies. The infrastructure is pretty fast and it should be quite +easy for you (or us) to extend it to provide more facilities for +your engine. + +See the HACKING file for a more detailed developer's view of Pachi. + +Also, if you are interested about Pachi's architecture, algorithms +etc., consider taking a look at Petr Baudis' Master's Thesis: + + http://pasky.or.cz/go/prace.pdf + + +Licence +------- + +Pachi is distributed under the GPLv2 licence (see the COPYING file for +details and full text of the licence); you are welcome to tweak it as +you wish (contributing back upstream is welcome) and distribute +it freely, but only together with the source code. You are welcome +to make private modifications to the code (e.g. try new algorithms and +approaches), use them internally or even to have your bot play on the +internet and enter competitions, but as soon as you want to release it +to the public, you need to release the source code as well. + +One exception is the Autotest framework, which is licenced under the +terms of the MIT licence (close to public domain) - you are free to +use it any way you wish. diff --git a/jni/pachi/TODO b/jni/pachi/TODO new file mode 100644 index 0000000..9b35316 --- /dev/null +++ b/jni/pachi/TODO @@ -0,0 +1,31 @@ +Docs: +* Manual page - full usage documentation +* GTP interface documentation + +Base: +* Further optimize board implementation, oprofile fun +* Clean up GTP interface, allow custom GTP commands for modules +* Implement parameter setup over GTP +* gogui-friendly GTP interface + +General improvements: +* Opening book +* Killer moves +* MM local-based patterns +* Balanced local-based patterns? +* Local tree forcing +* Liberty maps +* Reverse status learning + Run on game corpus. Start at final position, watch development + of status of all stones. The moment the final status and expected + status changes, analyze, especially if move choice differs. Use + learnt status-fixing moves in simulations somehow. + +Playouts: +* Split playout aspects to custom-stackable pieces +* Possibly replay top-rated unplayed moves from the tree + in the playout? + +UCB1AMAF: +* Fix RAVE for good (still noticeably weaker than fuego?) +* Separate constants for prior and amaf elements? diff --git a/jni/pachi/android/util.c b/jni/pachi/android/util.c new file mode 100644 index 0000000..7b31ca0 --- /dev/null +++ b/jni/pachi/android/util.c @@ -0,0 +1,15 @@ +#include "util.h" + + +char * +stpcpy(char *dest, const char *src) +{ + register char *d = dest; + register const char *s = src; + + do + *d++ = *s; + while (*s++ != '\0'); + + return d - 1; +} diff --git a/jni/pachi/android/util.h b/jni/pachi/android/util.h new file mode 100644 index 0000000..dd95484 --- /dev/null +++ b/jni/pachi/android/util.h @@ -0,0 +1,7 @@ +#ifndef PACHI_ANDROID_UTIL_H +#define PACHI_ANDROID_UTIL_H + +// The function "stpcpy" doesn't exist in the Android NDK (version r8b) +char* stpcpy(char *dest, const char *src); + +#endif diff --git a/jni/pachi/board.c b/jni/pachi/board.c new file mode 100644 index 0000000..96a00eb --- /dev/null +++ b/jni/pachi/board.c @@ -0,0 +1,1614 @@ +#include +#include +#include +#include +#include + +//#define DEBUG +#include "board.h" +#include "debug.h" +#include "fbook.h" +#include "mq.h" +#include "random.h" + +#ifdef BOARD_SPATHASH +#include "patternsp.h" +#endif +#ifdef BOARD_PAT3 +#include "pattern3.h" +#endif +#ifdef BOARD_TRAITS +static void board_trait_recompute(struct board *board, coord_t coord); +#include "tactics/selfatari.h" +#endif + + +#if 0 +#define profiling_noinline __attribute__((noinline)) +#else +#define profiling_noinline +#endif + +#define gi_granularity 4 +#define gi_allocsize(gids) ((1 << gi_granularity) + ((gids) >> gi_granularity) * (1 << gi_granularity)) + + +static void +board_setup(struct board *b) +{ + memset(b, 0, sizeof(*b)); + + struct move m = { pass, S_NONE }; + b->last_move = b->last_move2 = b->last_move3 = b->last_move4 = b->last_ko = b->ko = m; +} + +struct board * +board_init(char *fbookfile) +{ + struct board *b = malloc2(sizeof(struct board)); + board_setup(b); + + b->fbookfile = fbookfile; + + // Default setup + b->size = 9 + 2; + board_clear(b); + + return b; +} + +static size_t +board_alloc(struct board *board) +{ + /* We do not allocate the board structure itself but we allocate + * all the arrays with board contents. */ + + int bsize = board_size2(board) * sizeof(*board->b); + int gsize = board_size2(board) * sizeof(*board->g); + int fsize = board_size2(board) * sizeof(*board->f); + int nsize = board_size2(board) * sizeof(*board->n); + int psize = board_size2(board) * sizeof(*board->p); + int hsize = board_size2(board) * 2 * sizeof(*board->h); + int gisize = board_size2(board) * sizeof(*board->gi); +#ifdef WANT_BOARD_C + int csize = board_size2(board) * sizeof(*board->c); +#else + int csize = 0; +#endif +#ifdef BOARD_SPATHASH + int ssize = board_size2(board) * sizeof(*board->spathash); +#else + int ssize = 0; +#endif +#ifdef BOARD_PAT3 + int p3size = board_size2(board) * sizeof(*board->pat3); +#else + int p3size = 0; +#endif +#ifdef BOARD_TRAITS + int tsize = board_size2(board) * sizeof(*board->t); + int tqsize = board_size2(board) * sizeof(*board->t); +#else + int tsize = 0; + int tqsize = 0; +#endif + int cdsize = board_size2(board) * sizeof(*board->coord); + + size_t size = bsize + gsize + fsize + psize + nsize + hsize + gisize + csize + ssize + p3size + tsize + tqsize + cdsize; + void *x = malloc2(size); + + /* board->b must come first */ + board->b = x; x += bsize; + board->g = x; x += gsize; + board->f = x; x += fsize; + board->p = x; x += psize; + board->n = x; x += nsize; + board->h = x; x += hsize; + board->gi = x; x += gisize; +#ifdef WANT_BOARD_C + board->c = x; x += csize; +#endif +#ifdef BOARD_SPATHASH + board->spathash = x; x += ssize; +#endif +#ifdef BOARD_PAT3 + board->pat3 = x; x += p3size; +#endif +#ifdef BOARD_TRAITS + board->t = x; x += tsize; + board->tq = x; x += tqsize; +#endif + board->coord = x; x += cdsize; + + return size; +} + +struct board * +board_copy(struct board *b2, struct board *b1) +{ + memcpy(b2, b1, sizeof(struct board)); + + size_t size = board_alloc(b2); + memcpy(b2->b, b1->b, size); + + // XXX: Special semantics. + b2->fbook = NULL; + + return b2; +} + +void +board_done_noalloc(struct board *board) +{ + if (board->b) free(board->b); + if (board->fbook) fbook_done(board->fbook); +} + +void +board_done(struct board *board) +{ + board_done_noalloc(board); + free(board); +} + +void +board_resize(struct board *board, int size) +{ +#ifdef BOARD_SIZE + assert(board_size(board) == size + 2); +#endif + assert(size <= BOARD_MAX_SIZE); + board->size = size + 2 /* S_OFFBOARD margin */; + board->size2 = board_size(board) * board_size(board); + + board->bits2 = 1; + while ((1 << board->bits2) < board->size2) board->bits2++; + + if (board->b) + free(board->b); + + size_t asize = board_alloc(board); + memset(board->b, 0, asize); +} + +static void +board_init_data(struct board *board) +{ + int size = board_size(board); + + board_setup(board); + board_resize(board, size - 2 /* S_OFFBOARD margin */); + + /* Setup neighborhood iterators */ + board->nei8[0] = -size - 1; // (-1,-1) + board->nei8[1] = 1; + board->nei8[2] = 1; + board->nei8[3] = size - 2; // (-1,0) + board->nei8[4] = 2; + board->nei8[5] = size - 2; // (-1,1) + board->nei8[6] = 1; + board->nei8[7] = 1; + board->dnei[0] = -size - 1; + board->dnei[1] = 2; + board->dnei[2] = size*2 - 2; + board->dnei[3] = 2; + + /* Setup initial symmetry */ + if (size % 2) { + board->symmetry.d = 1; + board->symmetry.x1 = board->symmetry.y1 = board_size(board) / 2; + board->symmetry.x2 = board->symmetry.y2 = board_size(board) - 1; + board->symmetry.type = SYM_FULL; + } else { + /* TODO: We do not handle board symmetry on boards + * with no tengen yet. */ + board->symmetry.d = 0; + board->symmetry.x1 = board->symmetry.y1 = 1; + board->symmetry.x2 = board->symmetry.y2 = board_size(board) - 1; + board->symmetry.type = SYM_NONE; + } + + /* Set up coordinate cache */ + foreach_point(board) { + board->coord[c][0] = c % board_size(board); + board->coord[c][1] = c / board_size(board); + } foreach_point_end; + + /* Draw the offboard margin */ + int top_row = board_size2(board) - board_size(board); + int i; + for (i = 0; i < board_size(board); i++) + board->b[i] = board->b[top_row + i] = S_OFFBOARD; + for (i = 0; i <= top_row; i += board_size(board)) + board->b[i] = board->b[board_size(board) - 1 + i] = S_OFFBOARD; + + foreach_point(board) { + coord_t coord = c; + if (board_at(board, coord) == S_OFFBOARD) + continue; + foreach_neighbor(board, c, { + inc_neighbor_count_at(board, coord, board_at(board, c)); + } ); + } foreach_point_end; + + /* All positions are free! Except the margin. */ + for (i = board_size(board); i < (board_size(board) - 1) * board_size(board); i++) + if (i % board_size(board) != 0 && i % board_size(board) != board_size(board) - 1) + board->f[board->flen++] = i; + + /* Initialize zobrist hashtable. */ + /* We will need these to be stable across Pachi runs for + * certain kinds of pattern matching, thus we do not use + * fast_random() for this. */ + hash_t hseed = 0x3121110101112131; + foreach_point(board) { + board->h[c * 2] = (hseed *= 16807); + if (!board->h[c * 2]) + board->h[c * 2] = 1; + /* And once again for white */ + board->h[c * 2 + 1] = (hseed *= 16807); + if (!board->h[c * 2 + 1]) + board->h[c * 2 + 1] = 1; + } foreach_point_end; + +#ifdef BOARD_SPATHASH + /* Initialize spatial hashes. */ + foreach_point(board) { + for (int d = 1; d <= BOARD_SPATHASH_MAXD; d++) { + for (int j = ptind[d]; j < ptind[d + 1]; j++) { + ptcoords_at(x, y, c, board, j); + board->spathash[coord_xy(board, x, y)][d - 1][0] ^= + pthashes[0][j][board_at(board, c)]; + board->spathash[coord_xy(board, x, y)][d - 1][1] ^= + pthashes[0][j][stone_other(board_at(board, c))]; + } + } + } foreach_point_end; +#endif +#ifdef BOARD_PAT3 + /* Initialize 3x3 pattern codes. */ + foreach_point(board) { + if (board_at(board, c) == S_NONE) + board->pat3[c] = pattern3_hash(board, c); + } foreach_point_end; +#endif +#ifdef BOARD_TRAITS + /* Initialize traits. */ + foreach_point(board) { + trait_at(board, c, S_BLACK).cap = 0; + trait_at(board, c, S_WHITE).cap = 0; + trait_at(board, c, S_BLACK).cap1 = 0; + trait_at(board, c, S_WHITE).cap1 = 0; +#ifdef BOARD_TRAIT_SAFE + trait_at(board, c, S_BLACK).safe = true; + trait_at(board, c, S_WHITE).safe = true; +#endif + } foreach_point_end; +#endif +} + +void +board_clear(struct board *board) +{ + int size = board_size(board); + floating_t komi = board->komi; + char *fbookfile = board->fbookfile; + enum go_ruleset rules = board->rules; + + board_done_noalloc(board); + + static struct board bcache[BOARD_MAX_SIZE + 2]; + assert(size > 0 && size <= BOARD_MAX_SIZE + 2); + if (bcache[size - 1].size == size) { + board_copy(board, &bcache[size - 1]); + } else { + board_init_data(board); + board_copy(&bcache[size - 1], board); + } + + board->komi = komi; + board->fbookfile = fbookfile; + board->rules = rules; + + if (board->fbookfile) { + board->fbook = fbook_init(board->fbookfile, board); + } +} + +static char * +board_print_top(struct board *board, char *s, char *end, int c) +{ + for (int i = 0; i < c; i++) { + char asdf[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; + s += snprintf(s, end - s, " "); + for (int x = 1; x < board_size(board) - 1; x++) + s += snprintf(s, end - s, "%c ", asdf[x - 1]); + s += snprintf(s, end -s, " "); + } + s += snprintf(s, end - s, "\n"); + for (int i = 0; i < c; i++) { + s += snprintf(s, end - s, " +-"); + for (int x = 1; x < board_size(board) - 1; x++) + s += snprintf(s, end - s, "--"); + s += snprintf(s, end - s, "+"); + } + s += snprintf(s, end - s, "\n"); + return s; +} + +static char * +board_print_bottom(struct board *board, char *s, char *end, int c) +{ + for (int i = 0; i < c; i++) { + s += snprintf(s, end - s, " +-"); + for (int x = 1; x < board_size(board) - 1; x++) + s += snprintf(s, end - s, "--"); + s += snprintf(s, end - s, "+"); + } + s += snprintf(s, end - s, "\n"); + return s; +} + +static char * +board_print_row(struct board *board, int y, char *s, char *end, board_cprint cprint) +{ + s += snprintf(s, end - s, " %2d | ", y); + for (int x = 1; x < board_size(board) - 1; x++) { + if (coord_x(board->last_move.coord, board) == x && coord_y(board->last_move.coord, board) == y) + s += snprintf(s, end - s, "%c)", stone2char(board_atxy(board, x, y))); + else + s += snprintf(s, end - s, "%c ", stone2char(board_atxy(board, x, y))); + } + s += snprintf(s, end - s, "|"); + if (cprint) { + s += snprintf(s, end - s, " %2d | ", y); + for (int x = 1; x < board_size(board) - 1; x++) { + s = cprint(board, coord_xy(board, x, y), s, end); + } + s += snprintf(s, end - s, "|"); + } + s += snprintf(s, end - s, "\n"); + return s; +} + +void +board_print_custom(struct board *board, FILE *f, board_cprint cprint) +{ + char buf[10240]; + char *s = buf; + char *end = buf + sizeof(buf); + s += snprintf(s, end - s, "Move: % 3d Komi: %2.1f Handicap: %d Captures B: %d W: %d\n", + board->moves, board->komi, board->handicap, + board->captures[S_BLACK], board->captures[S_WHITE]); + s = board_print_top(board, s, end, 1 + !!cprint); + for (int y = board_size(board) - 2; y >= 1; y--) + s = board_print_row(board, y, s, end, cprint); + board_print_bottom(board, s, end, 1 + !!cprint); + fprintf(f, "%s\n", buf); +} + +static char * +cprint_group(struct board *board, coord_t c, char *s, char *end) +{ + s += snprintf(s, end - s, "%d ", group_base(group_at(board, c))); + return s; +} + +void +board_print(struct board *board, FILE *f) +{ + board_print_custom(board, f, DEBUGL(6) ? cprint_group : NULL); +} + + +#ifdef BOARD_TRAITS + +#if BOARD_TRAIT_SAFE == 1 +static bool +board_trait_safe(struct board *board, coord_t coord, enum stone color) +{ + return board_safe_to_play(board, coord, color); +} +#elif BOARD_TRAIT_SAFE == 2 +static bool +board_trait_safe(struct board *board, coord_t coord, enum stone color) +{ + return !is_bad_selfatari(board, color, coord); +} +#endif + +static void +board_trait_recompute(struct board *board, coord_t coord) +{ + int sfb = -1, sfw = -1; +#ifdef BOARD_TRAIT_SAFE + sfb = trait_at(board, coord, S_BLACK).safe = board_trait_safe(board, coord, S_BLACK); + sfw = trait_at(board, coord, S_WHITE).safe = board_trait_safe(board, coord, S_WHITE); +#endif + if (DEBUGL(8)) { + fprintf(stderr, "traits[%s:%s lib=%d] (black cap=%d cap1=%d safe=%d) (white cap=%d cap1=%d safe=%d)\n", + coord2sstr(coord, board), stone2str(board_at(board, coord)), immediate_liberty_count(board, coord), + trait_at(board, coord, S_BLACK).cap, trait_at(board, coord, S_BLACK).cap1, sfb, + trait_at(board, coord, S_WHITE).cap, trait_at(board, coord, S_WHITE).cap1, sfw); + } +} +#endif + +/* Recompute traits for dirty points that we have previously touched + * somehow (libs of their neighbors changed or so). */ +static void +board_traits_recompute(struct board *board) +{ +#ifdef BOARD_TRAITS + for (int i = 0; i < board->tqlen; i++) { + coord_t coord = board->tq[i]; + trait_at(board, coord, S_BLACK).dirty = false; + if (board_at(board, coord) != S_NONE) + continue; + board_trait_recompute(board, coord); + } + board->tqlen = 0; +#endif +} + +/* Queue traits of given point for recomputing. */ +static void +board_trait_queue(struct board *board, coord_t coord) +{ +#ifdef BOARD_TRAITS + if (trait_at(board, coord, S_BLACK).dirty) + return; + board->tq[board->tqlen++] = coord; + trait_at(board, coord, S_BLACK).dirty = true; +#endif +} + + +/* Update board hash with given coordinate. */ +static void profiling_noinline +board_hash_update(struct board *board, coord_t coord, enum stone color) +{ + board->hash ^= hash_at(board, coord, color); + board->qhash[coord_quadrant(coord, board)] ^= hash_at(board, coord, color); + if (DEBUGL(8)) + fprintf(stderr, "board_hash_update(%d,%d,%d) ^ %"PRIhash" -> %"PRIhash"\n", color, coord_x(coord, board), coord_y(coord, board), hash_at(board, coord, color), board->hash); + +#ifdef BOARD_SPATHASH + /* Gridcular metric is reflective, so we update all hashes + * of appropriate ditance in OUR circle. */ + for (int d = 1; d <= BOARD_SPATHASH_MAXD; d++) { + for (int j = ptind[d]; j < ptind[d + 1]; j++) { + ptcoords_at(x, y, coord, board, j); + /* We either changed from S_NONE to color + * or vice versa; doesn't matter. */ + board->spathash[coord_xy(board, x, y)][d - 1][0] ^= + pthashes[0][j][color] ^ pthashes[0][j][S_NONE]; + board->spathash[coord_xy(board, x, y)][d - 1][1] ^= + pthashes[0][j][stone_other(color)] ^ pthashes[0][j][S_NONE]; + } + } +#endif + +#if defined(BOARD_PAT3) + /* @color is not what we need in case of capture. */ + static const int ataribits[8] = { -1, 0, -1, 1, 2, -1, 3, -1 }; + enum stone new_color = board_at(board, coord); + bool in_atari = false; + if (new_color == S_NONE) { + board->pat3[coord] = pattern3_hash(board, coord); + } else { + in_atari = (board_group_info(board, group_at(board, coord)).libs == 1); + } + foreach_8neighbor(board, coord) { + /* Internally, the loop uses fn__i=[0..7]. We can use + * it directly to address bits within the bitmap of the + * neighbors since the bitmap order is reverse to the + * loop order. */ + if (board_at(board, c) != S_NONE) + continue; + board->pat3[c] &= ~(3 << (fn__i*2)); + board->pat3[c] |= new_color << (fn__i*2); + if (ataribits[fn__i] >= 0) { + board->pat3[c] &= ~(1 << (16 + ataribits[fn__i])); + board->pat3[c] |= in_atari << (16 + ataribits[fn__i]); + } +#if defined(BOARD_TRAITS) + board_trait_queue(board, c); +#endif + } foreach_8neighbor_end; +#endif +} + +/* Commit current board hash to history. */ +static void profiling_noinline +board_hash_commit(struct board *board) +{ + if (DEBUGL(8)) + fprintf(stderr, "board_hash_commit %"PRIhash"\n", board->hash); + if (likely(board->history_hash[board->hash & history_hash_mask]) == 0) { + board->history_hash[board->hash & history_hash_mask] = board->hash; + } else { + hash_t i = board->hash; + while (board->history_hash[i & history_hash_mask]) { + if (board->history_hash[i & history_hash_mask] == board->hash) { + if (DEBUGL(5)) + fprintf(stderr, "SUPERKO VIOLATION noted at %d,%d\n", + coord_x(board->last_move.coord, board), coord_y(board->last_move.coord, board)); + board->superko_violation = true; + return; + } + i = history_hash_next(i); + } + board->history_hash[i & history_hash_mask] = board->hash; + } +} + + +void +board_symmetry_update(struct board *b, struct board_symmetry *symmetry, coord_t c) +{ + if (likely(symmetry->type == SYM_NONE)) { + /* Fully degenerated already. We do not support detection + * of restoring of symmetry, assuming that this is too rare + * a case to handle. */ + return; + } + + int x = coord_x(c, b), y = coord_y(c, b), t = board_size(b) / 2; + int dx = board_size(b) - 1 - x; /* for SYM_DOWN */ + if (DEBUGL(6)) { + fprintf(stderr, "SYMMETRY [%d,%d,%d,%d|%d=%d] update for %d,%d\n", + symmetry->x1, symmetry->y1, symmetry->x2, symmetry->y2, + symmetry->d, symmetry->type, x, y); + } + + switch (symmetry->type) { + case SYM_FULL: + if (x == t && y == t) { + /* Tengen keeps full symmetry. */ + return; + } + /* New symmetry now? */ + if (x == y) { + symmetry->type = SYM_DIAG_UP; + symmetry->x1 = symmetry->y1 = 1; + symmetry->x2 = symmetry->y2 = board_size(b) - 1; + symmetry->d = 1; + } else if (dx == y) { + symmetry->type = SYM_DIAG_DOWN; + symmetry->x1 = symmetry->y1 = 1; + symmetry->x2 = symmetry->y2 = board_size(b) - 1; + symmetry->d = 1; + } else if (x == t) { + symmetry->type = SYM_HORIZ; + symmetry->y1 = 1; + symmetry->y2 = board_size(b) - 1; + symmetry->d = 0; + } else if (y == t) { + symmetry->type = SYM_VERT; + symmetry->x1 = 1; + symmetry->x2 = board_size(b) - 1; + symmetry->d = 0; + } else { +break_symmetry: + symmetry->type = SYM_NONE; + symmetry->x1 = symmetry->y1 = 1; + symmetry->x2 = symmetry->y2 = board_size(b) - 1; + symmetry->d = 0; + } + break; + case SYM_DIAG_UP: + if (x == y) + return; + goto break_symmetry; + case SYM_DIAG_DOWN: + if (dx == y) + return; + goto break_symmetry; + case SYM_HORIZ: + if (x == t) + return; + goto break_symmetry; + case SYM_VERT: + if (y == t) + return; + goto break_symmetry; + case SYM_NONE: + assert(0); + break; + } + + if (DEBUGL(6)) { + fprintf(stderr, "NEW SYMMETRY [%d,%d,%d,%d|%d=%d]\n", + symmetry->x1, symmetry->y1, symmetry->x2, symmetry->y2, + symmetry->d, symmetry->type); + } + /* Whew. */ +} + + +void +board_handicap_stone(struct board *board, int x, int y, FILE *f) +{ + struct move m; + m.color = S_BLACK; m.coord = coord_xy(board, x, y); + + board_play(board, &m); + /* Simulate white passing; otherwise, UCT search can get confused since + * tree depth parity won't match the color to move. */ + board->moves++; + + char *str = coord2str(m.coord, board); + if (DEBUGL(1)) + fprintf(stderr, "choosing handicap %s (%d,%d)\n", str, x, y); + if (f) fprintf(f, "%s ", str); + free(str); +} + +void +board_handicap(struct board *board, int stones, FILE *f) +{ + int margin = 3 + (board_size(board) >= 13); + int min = margin; + int mid = board_size(board) / 2; + int max = board_size(board) - 1 - margin; + const int places[][2] = { + { min, min }, { max, max }, { min, max }, { max, min }, + { min, mid }, { max, mid }, + { mid, min }, { mid, max }, + { mid, mid }, + }; + + board->handicap = stones; + + if (stones == 5 || stones == 7) { + board_handicap_stone(board, mid, mid, f); + stones--; + } + + int i; + for (i = 0; i < stones; i++) + board_handicap_stone(board, places[i][0], places[i][1], f); +} + + +static void __attribute__((noinline)) +check_libs_consistency(struct board *board, group_t g) +{ +#ifdef DEBUG + if (!g) return; + struct group *gi = &board_group_info(board, g); + for (int i = 0; i < GROUP_KEEP_LIBS; i++) + if (gi->lib[i] && board_at(board, gi->lib[i]) != S_NONE) { + fprintf(stderr, "BOGUS LIBERTY %s of group %d[%s]\n", coord2sstr(gi->lib[i], board), g, coord2sstr(group_base(g), board)); + assert(0); + } +#endif +} + +static void +check_pat3_consistency(struct board *board, coord_t coord) +{ +#ifdef DEBUG + foreach_8neighbor(board, coord) { + if (board_at(board, c) == S_NONE && pattern3_hash(board, c) != board->pat3[c]) { + board_print(board, stderr); + fprintf(stderr, "%s(%d)->%s(%d) computed %x != stored %x (%d)\n", coord2sstr(coord, board), coord, coord2sstr(c, board), c, pattern3_hash(board, c), board->pat3[c], fn__i); + assert(0); + } + } foreach_8neighbor_end; +#endif +} + +static void +board_capturable_add(struct board *board, group_t group, coord_t lib, bool onestone) +{ + //fprintf(stderr, "group %s cap %s\n", coord2sstr(group, board), coord2sstr(lib, boarD)); +#ifdef BOARD_TRAITS + /* Increase capturable count trait of my last lib. */ + enum stone capturing_color = stone_other(board_at(board, group)); + assert(capturing_color == S_BLACK || capturing_color == S_WHITE); + foreach_neighbor(board, lib, { + if (DEBUGL(8) && group_at(board, c) == group) + fprintf(stderr, "%s[%d] %s cap bump bc of %s(%d) member %s onestone %d\n", coord2sstr(lib, board), trait_at(board, lib, capturing_color).cap, stone2str(capturing_color), coord2sstr(group, board), board_group_info(board, group).libs, coord2sstr(c, board), onestone); + trait_at(board, lib, capturing_color).cap += (group_at(board, c) == group); + trait_at(board, lib, capturing_color).cap1 += (group_at(board, c) == group && onestone); + }); + board_trait_queue(board, lib); +#endif + +#ifdef BOARD_PAT3 + int fn__i = 0; + foreach_neighbor(board, lib, { + board->pat3[lib] |= (group_at(board, c) == group) << (16 + 3 - fn__i); + fn__i++; + }); +#endif + +#ifdef WANT_BOARD_C + /* Update the list of capturable groups. */ + assert(group); + assert(board->clen < board_size2(board)); + board->c[board->clen++] = group; +#endif +} +static void +board_capturable_rm(struct board *board, group_t group, coord_t lib, bool onestone) +{ + //fprintf(stderr, "group %s nocap %s\n", coord2sstr(group, board), coord2sstr(lib, board)); +#ifdef BOARD_TRAITS + /* Decrease capturable count trait of my previously-last lib. */ + enum stone capturing_color = stone_other(board_at(board, group)); + assert(capturing_color == S_BLACK || capturing_color == S_WHITE); + foreach_neighbor(board, lib, { + if (DEBUGL(8) && group_at(board, c) == group) + fprintf(stderr, "%s[%d] cap dump bc of %s(%d) member %s onestone %d\n", coord2sstr(lib, board), trait_at(board, lib, capturing_color).cap, coord2sstr(group, board), board_group_info(board, group).libs, coord2sstr(c, board), onestone); + trait_at(board, lib, capturing_color).cap -= (group_at(board, c) == group); + trait_at(board, lib, capturing_color).cap1 -= (group_at(board, c) == group && onestone); + }); + board_trait_queue(board, lib); +#endif + +#ifdef BOARD_PAT3 + int fn__i = 0; + foreach_neighbor(board, lib, { + board->pat3[lib] &= ~((group_at(board, c) == group) << (16 + 3 - fn__i)); + fn__i++; + }); +#endif + +#ifdef WANT_BOARD_C + /* Update the list of capturable groups. */ + for (int i = 0; i < board->clen; i++) { + if (unlikely(board->c[i] == group)) { + board->c[i] = board->c[--board->clen]; + return; + } + } + fprintf(stderr, "rm of bad group %d\n", group_base(group)); + assert(0); +#endif +} + +static void +board_atariable_add(struct board *board, group_t group, coord_t lib1, coord_t lib2) +{ +#ifdef BOARD_TRAITS + board_trait_queue(board, lib1); + board_trait_queue(board, lib2); +#endif +} +static void +board_atariable_rm(struct board *board, group_t group, coord_t lib1, coord_t lib2) +{ +#ifdef BOARD_TRAITS + board_trait_queue(board, lib1); + board_trait_queue(board, lib2); +#endif +} + +static void +board_group_addlib(struct board *board, group_t group, coord_t coord) +{ + if (DEBUGL(7)) { + fprintf(stderr, "Group %d[%s] %d: Adding liberty %s\n", + group_base(group), coord2sstr(group_base(group), board), + board_group_info(board, group).libs, coord2sstr(coord, board)); + } + + check_libs_consistency(board, group); + + struct group *gi = &board_group_info(board, group); + bool onestone = group_is_onestone(board, group); + if (gi->libs < GROUP_KEEP_LIBS) { + for (int i = 0; i < GROUP_KEEP_LIBS; i++) { +#if 0 + /* Seems extra branch just slows it down */ + if (!gi->lib[i]) + break; +#endif + if (unlikely(gi->lib[i] == coord)) + return; + } + if (gi->libs == 0) { + board_capturable_add(board, group, coord, onestone); + } else if (gi->libs == 1) { + board_capturable_rm(board, group, gi->lib[0], onestone); + board_atariable_add(board, group, gi->lib[0], coord); + } else if (gi->libs == 2) { + board_atariable_rm(board, group, gi->lib[0], gi->lib[1]); + } + gi->lib[gi->libs++] = coord; + } + + check_libs_consistency(board, group); +} + +static void +board_group_find_extra_libs(struct board *board, group_t group, struct group *gi, coord_t avoid) +{ + /* Add extra liberty from the board to our liberty list. */ + unsigned char watermark[board_size2(board) / 8]; + memset(watermark, 0, sizeof(watermark)); +#define watermark_get(c) (watermark[c >> 3] & (1 << (c & 7))) +#define watermark_set(c) watermark[c >> 3] |= (1 << (c & 7)) + + for (int i = 0; i < GROUP_KEEP_LIBS - 1; i++) + watermark_set(gi->lib[i]); + watermark_set(avoid); + + foreach_in_group(board, group) { + coord_t coord2 = c; + foreach_neighbor(board, coord2, { + if (board_at(board, c) + watermark_get(c) != S_NONE) + continue; + watermark_set(c); + gi->lib[gi->libs++] = c; + if (unlikely(gi->libs >= GROUP_KEEP_LIBS)) + return; + } ); + } foreach_in_group_end; +#undef watermark_get +#undef watermark_set +} + +static void +board_group_rmlib(struct board *board, group_t group, coord_t coord) +{ + if (DEBUGL(7)) { + fprintf(stderr, "Group %d[%s] %d: Removing liberty %s\n", + group_base(group), coord2sstr(group_base(group), board), + board_group_info(board, group).libs, coord2sstr(coord, board)); + } + + struct group *gi = &board_group_info(board, group); + bool onestone = group_is_onestone(board, group); + for (int i = 0; i < GROUP_KEEP_LIBS; i++) { +#if 0 + /* Seems extra branch just slows it down */ + if (!gi->lib[i]) + break; +#endif + if (likely(gi->lib[i] != coord)) + continue; + + coord_t lib = gi->lib[i] = gi->lib[--gi->libs]; + gi->lib[gi->libs] = 0; + + check_libs_consistency(board, group); + + /* Postpone refilling lib[] until we need to. */ + assert(GROUP_REFILL_LIBS > 1); + if (gi->libs > GROUP_REFILL_LIBS) + return; + if (gi->libs == GROUP_REFILL_LIBS) + board_group_find_extra_libs(board, group, gi, coord); + + if (gi->libs == 2) { + board_atariable_add(board, group, gi->lib[0], gi->lib[1]); + } else if (gi->libs == 1) { + board_capturable_add(board, group, gi->lib[0], onestone); + board_atariable_rm(board, group, gi->lib[0], lib); + } else if (gi->libs == 0) + board_capturable_rm(board, group, lib, onestone); + return; + } + + /* This is ok even if gi->libs < GROUP_KEEP_LIBS since we + * can call this multiple times per coord. */ + check_libs_consistency(board, group); + return; +} + + +/* This is a low-level routine that doesn't maintain consistency + * of all the board data structures. */ +static void +board_remove_stone(struct board *board, group_t group, coord_t c) +{ + enum stone color = board_at(board, c); + board_at(board, c) = S_NONE; + group_at(board, c) = 0; + board_hash_update(board, c, color); +#ifdef BOARD_TRAITS + /* We mark as cannot-capture now. If this is a ko/snapback, + * we will get incremented later in board_group_addlib(). */ + trait_at(board, c, S_BLACK).cap = trait_at(board, c, S_BLACK).cap1 = 0; + trait_at(board, c, S_WHITE).cap = trait_at(board, c, S_WHITE).cap1 = 0; + board_trait_queue(board, c); +#endif + + /* Increase liberties of surrounding groups */ + coord_t coord = c; + foreach_neighbor(board, coord, { + dec_neighbor_count_at(board, c, color); + board_trait_queue(board, c); + group_t g = group_at(board, c); + if (g && g != group) + board_group_addlib(board, g, coord); + }); + +#ifdef BOARD_PAT3 + /* board_hash_update() might have seen the freed up point as able + * to capture another group in atari that only after the loop + * above gained enough liberties. Reset pat3 again. */ + board->pat3[c] = pattern3_hash(board, c); +#endif + + if (DEBUGL(6)) + fprintf(stderr, "pushing free move [%d]: %d,%d\n", board->flen, coord_x(c, board), coord_y(c, board)); + board->f[board->flen++] = c; +} + +static int profiling_noinline +board_group_capture(struct board *board, group_t group) +{ + int stones = 0; + + foreach_in_group(board, group) { + board->captures[stone_other(board_at(board, c))]++; + board_remove_stone(board, group, c); + stones++; + } foreach_in_group_end; + + struct group *gi = &board_group_info(board, group); + assert(gi->libs == 0); + memset(gi, 0, sizeof(*gi)); + + return stones; +} + + +static void profiling_noinline +add_to_group(struct board *board, group_t group, coord_t prevstone, coord_t coord) +{ +#ifdef BOARD_TRAITS + struct group *gi = &board_group_info(board, group); + bool onestone = group_is_onestone(board, group); + + if (gi->libs == 1) { + /* Our group is temporarily in atari; make sure the capturable + * counts also correspond to the newly added stone before we + * start adding liberties again so bump-dump ops match. */ + enum stone capturing_color = stone_other(board_at(board, group)); + assert(capturing_color == S_BLACK || capturing_color == S_WHITE); + + coord_t lib = board_group_info(board, group).lib[0]; + if (coord_is_adjecent(lib, coord, board)) { + if (DEBUGL(8)) + fprintf(stderr, "add_to_group %s: %s[%d] bump\n", coord2sstr(group, board), coord2sstr(lib, board), trait_at(board, lib, capturing_color).cap); + trait_at(board, lib, capturing_color).cap++; + /* This is never a 1-stone group, obviously. */ + board_trait_queue(board, lib); + } + + if (onestone) { + /* We are not 1-stone group anymore, update the cap1 + * counter specifically. */ + foreach_neighbor(board, group, { + if (board_at(board, c) != S_NONE) continue; + trait_at(board, c, capturing_color).cap1--; + board_trait_queue(board, c); + }); + } + } +#endif + + group_at(board, coord) = group; + groupnext_at(board, coord) = groupnext_at(board, prevstone); + groupnext_at(board, prevstone) = coord; + + foreach_neighbor(board, coord, { + if (board_at(board, c) == S_NONE) + board_group_addlib(board, group, c); + }); + + if (DEBUGL(8)) + fprintf(stderr, "add_to_group: added (%d,%d ->) %d,%d (-> %d,%d) to group %d\n", + coord_x(prevstone, board), coord_y(prevstone, board), + coord_x(coord, board), coord_y(coord, board), + groupnext_at(board, coord) % board_size(board), groupnext_at(board, coord) / board_size(board), + group_base(group)); +} + +static void profiling_noinline +merge_groups(struct board *board, group_t group_to, group_t group_from) +{ + if (DEBUGL(7)) + fprintf(stderr, "board_play_raw: merging groups %d -> %d\n", + group_base(group_from), group_base(group_to)); + struct group *gi_from = &board_group_info(board, group_from); + struct group *gi_to = &board_group_info(board, group_to); + bool onestone_from = group_is_onestone(board, group_from); + bool onestone_to = group_is_onestone(board, group_to); + + /* We do this early before the group info is rewritten. */ + if (gi_from->libs == 2) + board_atariable_rm(board, group_from, gi_from->lib[0], gi_from->lib[1]); + else if (gi_from->libs == 1) + board_capturable_rm(board, group_from, gi_from->lib[0], onestone_from); + + if (DEBUGL(7)) + fprintf(stderr,"---- (froml %d, tol %d)\n", gi_from->libs, gi_to->libs); + + if (gi_to->libs < GROUP_KEEP_LIBS) { + for (int i = 0; i < gi_from->libs; i++) { + for (int j = 0; j < gi_to->libs; j++) + if (gi_to->lib[j] == gi_from->lib[i]) + goto next_from_lib; + if (gi_to->libs == 0) { + board_capturable_add(board, group_to, gi_from->lib[i], onestone_to); + } else if (gi_to->libs == 1) { + board_capturable_rm(board, group_to, gi_to->lib[0], onestone_to); + board_atariable_add(board, group_to, gi_to->lib[0], gi_from->lib[i]); + } else if (gi_to->libs == 2) { + board_atariable_rm(board, group_to, gi_to->lib[0], gi_to->lib[1]); + } + gi_to->lib[gi_to->libs++] = gi_from->lib[i]; + if (gi_to->libs >= GROUP_KEEP_LIBS) + break; +next_from_lib:; + } + } + + if (gi_to->libs == 1) { + coord_t lib = board_group_info(board, group_to).lib[0]; +#ifdef BOARD_TRAITS + enum stone capturing_color = stone_other(board_at(board, group_to)); + assert(capturing_color == S_BLACK || capturing_color == S_WHITE); + + /* Our group is currently in atari; make sure we properly + * count in even the neighbors from the other group in the + * capturable counter. */ + foreach_neighbor(board, lib, { + if (DEBUGL(8) && group_at(board, c) == group_from) + fprintf(stderr, "%s[%d] cap bump\n", coord2sstr(lib, board), trait_at(board, lib, capturing_color).cap); + trait_at(board, lib, capturing_color).cap += (group_at(board, c) == group_from); + /* This is never a 1-stone group, obviously. */ + }); + board_trait_queue(board, lib); + + if (onestone_to) { + /* We are not 1-stone group anymore, update the cap1 + * counter specifically. */ + foreach_neighbor(board, group_to, { + if (board_at(board, c) != S_NONE) continue; + trait_at(board, c, capturing_color).cap1--; + board_trait_queue(board, c); + }); + } +#endif +#ifdef BOARD_PAT3 + if (gi_from->libs == 1) { + /* We removed group_from from capturable groups, + * therefore switching the atari flag off. + * We need to set it again since group_to is also + * capturable. */ + int fn__i = 0; + foreach_neighbor(board, lib, { + board->pat3[lib] |= (group_at(board, c) == group_from) << (16 + 3 - fn__i); + fn__i++; + }); + } +#endif + } + + coord_t last_in_group; + foreach_in_group(board, group_from) { + last_in_group = c; + group_at(board, c) = group_to; + } foreach_in_group_end; + groupnext_at(board, last_in_group) = groupnext_at(board, group_base(group_to)); + groupnext_at(board, group_base(group_to)) = group_base(group_from); + memset(gi_from, 0, sizeof(struct group)); + + if (DEBUGL(7)) + fprintf(stderr, "board_play_raw: merged group: %d\n", + group_base(group_to)); +} + +static group_t profiling_noinline +new_group(struct board *board, coord_t coord) +{ + group_t group = coord; + struct group *gi = &board_group_info(board, group); + foreach_neighbor(board, coord, { + if (board_at(board, c) == S_NONE) + /* board_group_addlib is ridiculously expensive for us */ +#if GROUP_KEEP_LIBS < 4 + if (gi->libs < GROUP_KEEP_LIBS) +#endif + gi->lib[gi->libs++] = c; + }); + + group_at(board, coord) = group; + groupnext_at(board, coord) = 0; + + if (gi->libs == 2) + board_atariable_add(board, group, gi->lib[0], gi->lib[1]); + else if (gi->libs == 1) + board_capturable_add(board, group, gi->lib[0], true); + check_libs_consistency(board, group); + + if (DEBUGL(8)) + fprintf(stderr, "new_group: added %d,%d to group %d\n", + coord_x(coord, board), coord_y(coord, board), + group_base(group)); + + return group; +} + +static inline group_t +play_one_neighbor(struct board *board, + coord_t coord, enum stone color, enum stone other_color, + coord_t c, group_t group) +{ + enum stone ncolor = board_at(board, c); + group_t ngroup = group_at(board, c); + + inc_neighbor_count_at(board, c, color); + /* We can be S_NONE, in that case we need to update the safety + * trait since we might be left with only one liberty. */ + board_trait_queue(board, c); + + if (!ngroup) + return group; + + board_group_rmlib(board, ngroup, coord); + if (DEBUGL(7)) + fprintf(stderr, "board_play_raw: reducing libs for group %d (%d:%d,%d)\n", + group_base(ngroup), ncolor, color, other_color); + + if (ncolor == color && ngroup != group) { + if (!group) { + group = ngroup; + add_to_group(board, group, c, coord); + } else { + merge_groups(board, group, ngroup); + } + } else if (ncolor == other_color) { + if (DEBUGL(8)) { + struct group *gi = &board_group_info(board, ngroup); + fprintf(stderr, "testing captured group %d[%s]: ", group_base(ngroup), coord2sstr(group_base(ngroup), board)); + for (int i = 0; i < GROUP_KEEP_LIBS; i++) + fprintf(stderr, "%s ", coord2sstr(gi->lib[i], board)); + fprintf(stderr, "\n"); + } + if (unlikely(board_group_captured(board, ngroup))) + board_group_capture(board, ngroup); + } + return group; +} + +/* We played on a place with at least one liberty. We will become a member of + * some group for sure. */ +static group_t profiling_noinline +board_play_outside(struct board *board, struct move *m, int f) +{ + coord_t coord = m->coord; + enum stone color = m->color; + enum stone other_color = stone_other(color); + group_t group = 0; + + board->f[f] = board->f[--board->flen]; + if (DEBUGL(6)) + fprintf(stderr, "popping free move [%d->%d]: %d\n", board->flen, f, board->f[f]); + +#if defined(BOARD_TRAITS) && defined(DEBUG) + /* Sanity check that cap matches reality. */ + { + int a = 0, b = 0; + foreach_neighbor(board, coord, { + group_t g = group_at(board, c); + a += g && (board_at(board, c) == other_color && board_group_info(board, g).libs == 1); + b += g && (board_at(board, c) == other_color && board_group_info(board, g).libs == 1) && group_is_onestone(board, g); + }); + assert(a == trait_at(board, coord, color).cap); + assert(b == trait_at(board, coord, color).cap1); +#ifdef BOARD_TRAIT_SAFE + assert(board_trait_safe(board, coord, color) == trait_at(board, coord, color).safe); +#endif + } +#endif + foreach_neighbor(board, coord, { + group = play_one_neighbor(board, coord, color, other_color, c, group); + }); + + board_at(board, coord) = color; + if (unlikely(!group)) + group = new_group(board, coord); + + board->last_move2 = board->last_move; + board->last_move = *m; + board->moves++; + board_hash_update(board, coord, color); + board_symmetry_update(board, &board->symmetry, coord); + struct move ko = { pass, S_NONE }; + board->ko = ko; + + check_pat3_consistency(board, coord); + + return group; +} + +/* We played in an eye-like shape. Either we capture at least one of the eye + * sides in the process of playing, or return -1. */ +static int profiling_noinline +board_play_in_eye(struct board *board, struct move *m, int f) +{ + coord_t coord = m->coord; + enum stone color = m->color; + /* Check ko: Capture at a position of ko capture one move ago */ + if (unlikely(color == board->ko.color && coord == board->ko.coord)) { + if (DEBUGL(5)) + fprintf(stderr, "board_check: ko at %d,%d color %d\n", coord_x(coord, board), coord_y(coord, board), color); + return -1; + } else if (DEBUGL(6)) { + fprintf(stderr, "board_check: no ko at %d,%d,%d - ko is %d,%d,%d\n", + color, coord_x(coord, board), coord_y(coord, board), + board->ko.color, coord_x(board->ko.coord, board), coord_y(board->ko.coord, board)); + } + + struct move ko = { pass, S_NONE }; + + int captured_groups = 0; + + foreach_neighbor(board, coord, { + group_t g = group_at(board, c); + if (DEBUGL(7)) + fprintf(stderr, "board_check: group %d has %d libs\n", + g, board_group_info(board, g).libs); + captured_groups += (board_group_info(board, g).libs == 1); + }); + + if (likely(captured_groups == 0)) { + if (DEBUGL(5)) { + if (DEBUGL(6)) + board_print(board, stderr); + fprintf(stderr, "board_check: one-stone suicide\n"); + } + + return -1; + } +#ifdef BOARD_TRAITS + /* We _will_ for sure capture something. */ + assert(trait_at(board, coord, color).cap > 0); +#ifdef BOARD_TRAIT_SAFE + assert(trait_at(board, coord, color).safe == board_trait_safe(board, coord, color)); +#endif +#endif + + board->f[f] = board->f[--board->flen]; + if (DEBUGL(6)) + fprintf(stderr, "popping free move [%d->%d]: %d\n", board->flen, f, board->f[f]); + + int ko_caps = 0; + coord_t cap_at = pass; + foreach_neighbor(board, coord, { + inc_neighbor_count_at(board, c, color); + /* Originally, this could not have changed any trait + * since no neighbors were S_NONE, however by now some + * of them might be removed from the board. */ + board_trait_queue(board, c); + + group_t group = group_at(board, c); + if (!group) + continue; + + board_group_rmlib(board, group, coord); + if (DEBUGL(7)) + fprintf(stderr, "board_play_raw: reducing libs for group %d\n", + group_base(group)); + + if (board_group_captured(board, group)) { + ko_caps += board_group_capture(board, group); + cap_at = c; + } + }); + if (ko_caps == 1) { + ko.color = stone_other(color); + ko.coord = cap_at; // unique + board->last_ko = ko; + board->last_ko_age = board->moves; + if (DEBUGL(5)) + fprintf(stderr, "guarding ko at %d,%s\n", ko.color, coord2sstr(ko.coord, board)); + } + + board_at(board, coord) = color; + group_t group = new_group(board, coord); + + board->last_move2 = board->last_move; + board->last_move = *m; + board->moves++; + board_hash_update(board, coord, color); + board_hash_commit(board); + board_traits_recompute(board); + board_symmetry_update(board, &board->symmetry, coord); + board->ko = ko; + + check_pat3_consistency(board, coord); + + return !!group; +} + +static int __attribute__((flatten)) +board_play_f(struct board *board, struct move *m, int f) +{ + if (DEBUGL(7)) { + fprintf(stderr, "board_play(%s): ---- Playing %d,%d\n", coord2sstr(m->coord, board), coord_x(m->coord, board), coord_y(m->coord, board)); + } + if (likely(!board_is_eyelike(board, m->coord, stone_other(m->color)))) { + /* NOT playing in an eye. Thus this move has to succeed. (This + * is thanks to New Zealand rules. Otherwise, multi-stone + * suicide might fail.) */ + group_t group = board_play_outside(board, m, f); + if (unlikely(board_group_captured(board, group))) { + board_group_capture(board, group); + } + board_hash_commit(board); + board_traits_recompute(board); + return 0; + } else { + return board_play_in_eye(board, m, f); + } +} + +int +board_play(struct board *board, struct move *m) +{ + if (unlikely(is_pass(m->coord) || is_resign(m->coord))) { + if (is_pass(m->coord) && board->rules == RULES_SIMING) { + /* On pass, the player gives a pass stone + * to the opponent. */ + board->captures[stone_other(m->color)]++; + } + struct move nomove = { pass, S_NONE }; + board->ko = nomove; + board->last_move4 = board->last_move3; + board->last_move3 = board->last_move2; + board->last_move2 = board->last_move; + board->last_move = *m; + return 0; + } + + int f; + for (f = 0; f < board->flen; f++) + if (board->f[f] == m->coord) + return board_play_f(board, m, f); + + if (DEBUGL(7)) + fprintf(stderr, "board_check: stone exists\n"); + return -1; +} + +/* Undo, supported only for pass moves. This form of undo is required by KGS + * to settle disputes on dead groups. (Undo of real moves would be more complex + * particularly for capturing moves.) */ +int board_undo(struct board *board) +{ + if (!is_pass(board->last_move.coord)) + return -1; + if (board->rules == RULES_SIMING) { + /* Return pass stone to the passing player. */ + board->captures[stone_other(board->last_move.color)]--; + } + board->last_move = board->last_move2; + board->last_move2 = board->last_move3; + board->last_move3 = board->last_move4; + if (board->last_ko_age == board->moves) + board->ko = board->last_ko; + return 0; +} + +static inline bool +board_try_random_move(struct board *b, enum stone color, coord_t *coord, int f, ppr_permit permit, void *permit_data) +{ + *coord = b->f[f]; + struct move m = { *coord, color }; + if (DEBUGL(6)) + fprintf(stderr, "trying random move %d: %d,%d %s %d\n", f, coord_x(*coord, b), coord_y(*coord, b), coord2sstr(*coord, b), board_is_valid_move(b, &m)); + if (unlikely(board_is_one_point_eye(b, *coord, color)) /* bad idea to play into one, usually */ + || !board_is_valid_move(b, &m) + || (permit && !permit(permit_data, b, &m))) + return false; + if (m.coord == *coord) { + return likely(board_play_f(b, &m, f) >= 0); + } else { + *coord = m.coord; // permit modified the coordinate + return likely(board_play(b, &m) >= 0); + } +} + +void +board_play_random(struct board *b, enum stone color, coord_t *coord, ppr_permit permit, void *permit_data) +{ + if (unlikely(b->flen == 0)) + goto pass; + + int base = fast_random(b->flen), f; + for (f = base; f < b->flen; f++) + if (board_try_random_move(b, color, coord, f, permit, permit_data)) + return; + for (f = 0; f < base; f++) + if (board_try_random_move(b, color, coord, f, permit, permit_data)) + return; + +pass: + *coord = pass; + struct move m = { pass, color }; + board_play(b, &m); +} + + +bool +board_is_false_eyelike(struct board *board, coord_t coord, enum stone eye_color) +{ + enum stone color_diag_libs[S_MAX] = {0, 0, 0, 0}; + + /* XXX: We attempt false eye detection but we will yield false + * positives in case of http://senseis.xmp.net/?TwoHeadedDragon :-( */ + + foreach_diag_neighbor(board, coord) { + color_diag_libs[(enum stone) board_at(board, c)]++; + } foreach_diag_neighbor_end; + /* For false eye, we need two enemy stones diagonally in the + * middle of the board, or just one enemy stone at the edge + * or in the corner. */ + color_diag_libs[stone_other(eye_color)] += !!color_diag_libs[S_OFFBOARD]; + return color_diag_libs[stone_other(eye_color)] >= 2; +} + +bool +board_is_one_point_eye(struct board *board, coord_t coord, enum stone eye_color) +{ + return board_is_eyelike(board, coord, eye_color) + && !board_is_false_eyelike(board, coord, eye_color); +} + +enum stone +board_get_one_point_eye(struct board *board, coord_t coord) +{ + if (board_is_one_point_eye(board, coord, S_WHITE)) + return S_WHITE; + else if (board_is_one_point_eye(board, coord, S_BLACK)) + return S_BLACK; + else + return S_NONE; +} + + +floating_t +board_fast_score(struct board *board) +{ + int scores[S_MAX]; + memset(scores, 0, sizeof(scores)); + + foreach_point(board) { + enum stone color = board_at(board, c); + if (color == S_NONE && board->rules != RULES_STONES_ONLY) + color = board_get_one_point_eye(board, c); + scores[color]++; + // fprintf(stderr, "%d, %d ++%d = %d\n", coord_x(c, board), coord_y(c, board), color, scores[color]); + } foreach_point_end; + + return board->komi + (board->rules != RULES_SIMING ? board->handicap : 0) + scores[S_WHITE] - scores[S_BLACK]; +} + +/* Owner map: 0: undecided; 1: black; 2: white; 3: dame */ + +/* One flood-fill iteration; returns true if next iteration + * is required. */ +static bool +board_tromp_taylor_iter(struct board *board, int *ownermap) +{ + bool needs_update = false; + foreach_free_point(board) { + /* Ignore occupied and already-dame positions. */ + assert(board_at(board, c) == S_NONE); + if (board->rules == RULES_STONES_ONLY) + ownermap[c] = 3; + if (ownermap[c] == 3) + continue; + /* Count neighbors. */ + int nei[4] = {0}; + foreach_neighbor(board, c, { + nei[ownermap[c]]++; + }); + /* If we have neighbors of both colors, or dame, + * we are dame too. */ + if ((nei[1] && nei[2]) || nei[3]) { + ownermap[c] = 3; + /* Speed up the propagation. */ + foreach_neighbor(board, c, { + if (board_at(board, c) == S_NONE) + ownermap[c] = 3; + }); + needs_update = true; + continue; + } + /* If we have neighbors of one color, we are owned + * by that color, too. */ + if (!ownermap[c] && (nei[1] || nei[2])) { + int newowner = nei[1] ? 1 : 2; + ownermap[c] = newowner; + /* Speed up the propagation. */ + foreach_neighbor(board, c, { + if (board_at(board, c) == S_NONE && !ownermap[c]) + ownermap[c] = newowner; + }); + needs_update = true; + continue; + } + } foreach_free_point_end; + return needs_update; +} + +/* Tromp-Taylor Counting */ +floating_t +board_official_score(struct board *board, struct move_queue *q) +{ + + /* A point P, not colored C, is said to reach C, if there is a path of + * (vertically or horizontally) adjacent points of P's color from P to + * a point of color C. + * + * A player's score is the number of points of her color, plus the + * number of empty points that reach only her color. */ + + int ownermap[board_size2(board)]; + int s[4] = {0}; + const int o[4] = {0, 1, 2, 0}; + foreach_point(board) { + ownermap[c] = o[board_at(board, c)]; + s[board_at(board, c)]++; + } foreach_point_end; + + if (q) { + /* Process dead groups. */ + for (unsigned int i = 0; i < q->moves; i++) { + foreach_in_group(board, q->move[i]) { + enum stone color = board_at(board, c); + ownermap[c] = o[stone_other(color)]; + s[color]--; s[stone_other(color)]++; + } foreach_in_group_end; + } + } + + /* We need to special-case empty board. */ + if (!s[S_BLACK] && !s[S_WHITE]) + return board->komi; + + while (board_tromp_taylor_iter(board, ownermap)) + /* Flood-fill... */; + + int scores[S_MAX]; + memset(scores, 0, sizeof(scores)); + + foreach_point(board) { + assert(board_at(board, c) == S_OFFBOARD || ownermap[c] != 0); + if (ownermap[c] == 3) + continue; + scores[ownermap[c]]++; + } foreach_point_end; + + return board->komi + (board->rules != RULES_SIMING ? board->handicap : 0) + scores[S_WHITE] - scores[S_BLACK]; +} + +bool +board_set_rules(struct board *board, char *name) +{ + if (!strcasecmp(name, "japanese")) { + board->rules = RULES_JAPANESE; + } else if (!strcasecmp(name, "chinese")) { + board->rules = RULES_CHINESE; + } else if (!strcasecmp(name, "aga")) { + board->rules = RULES_AGA; + } else if (!strcasecmp(name, "new_zealand")) { + board->rules = RULES_NEW_ZEALAND; + } else if (!strcasecmp(name, "siming") || !strcasecmp(name, "simplified_ing")) { + board->rules = RULES_SIMING; + } else { + return false; + } + return true; +} diff --git a/jni/pachi/board.h b/jni/pachi/board.h new file mode 100644 index 0000000..e6c47d4 --- /dev/null +++ b/jni/pachi/board.h @@ -0,0 +1,592 @@ +/* probdist.h must be included before the include goard since we require + * proper including order. */ +#include "probdist.h" + +#ifndef PACHI_BOARD_H +#define PACHI_BOARD_H + +#include +#include +#include + +#include "util.h" +#include "stone.h" +#include "move.h" + +struct fbook; + + +/* Maximum supported board size. (Without the S_OFFBOARD edges.) */ +#define BOARD_MAX_SIZE 19 + + +/* The board implementation has bunch of optional features. + * Turn them on below: */ + +#define WANT_BOARD_C // capturable groups queue + +//#define BOARD_SIZE 9 // constant board size, allows better optimization + +//#define BOARD_SPATHASH // incremental patternsp.h hashes +#define BOARD_SPATHASH_MAXD 3 // maximal diameter + +#define BOARD_PAT3 // incremental 3x3 pattern codes + +//#define BOARD_TRAITS 1 // incremental point traits (see struct btraits) +//#define BOARD_TRAIT_SAFE 1 // include btraits.safe (rather expensive, unused) +//#define BOARD_TRAIT_SAFE 2 // include btraits.safe based on full is_bad_selfatari() + + +#define BOARD_MAX_MOVES (BOARD_MAX_SIZE * BOARD_MAX_SIZE) +#define BOARD_MAX_GROUPS (BOARD_MAX_SIZE * BOARD_MAX_SIZE / 2) + + +/* Some engines might normalize their reading and skip symmetrical + * moves. We will tell them how can they do it. */ +struct board_symmetry { + /* Playground is in this rectangle. */ + int x1, x2, y1, y2; + /* d == 0: Full rectangle + * d == 1: Top triangle */ + int d; + /* General symmetry type. */ + /* Note that the above is redundant to this, but just provided + * for easier usage. */ + enum { + SYM_FULL, + SYM_DIAG_UP, + SYM_DIAG_DOWN, + SYM_HORIZ, + SYM_VERT, + SYM_NONE + } type; +}; + + +typedef uint64_t hash_t; +#define PRIhash PRIx64 + +/* XXX: This really belongs in pattern3.h, unfortunately that would mean + * a dependency hell. */ +typedef uint32_t hash3_t; // 3x3 pattern hash + + +/* Note that "group" is only chain of stones that is solidly + * connected for us. */ +typedef coord_t group_t; + +struct group { + /* We keep track of only up to GROUP_KEEP_LIBS; over that, we + * don't care. */ + /* _Combination_ of these two values can make some difference + * in performance - fine-tune. */ +#define GROUP_KEEP_LIBS 10 + // refill lib[] only when we hit this; this must be at least 2! + // Moggy requires at least 3 - see below for semantic impact. +#define GROUP_REFILL_LIBS 5 + coord_t lib[GROUP_KEEP_LIBS]; + /* libs is only LOWER BOUND for the number of real liberties!!! + * It denotes only number of items in lib[], thus you can rely + * on it to store real liberties only up to <= GROUP_REFILL_LIBS. */ + int libs; +}; + +struct neighbor_colors { + char colors[S_MAX]; +}; + + +/* Point traits bitmap; we update this information incrementally, + * it can be used e.g. for fast pattern features matching. */ +struct btraits { + /* Number of neighbors we can capture. 0=this move is + * not capturing, 1..4=this many neighbors we can capture + * (can be multiple neighbors of same group). */ + unsigned cap:3; + /* Number of 1-stone neighbors we can capture. */ + unsigned cap1:3; +#ifdef BOARD_TRAIT_SAFE + /* Whether it is SAFE to play here. This is essentially just + * cached result of board_safe_to_play(). (Of course the concept + * of "safety" is not perfect here, but it's the cheapest + * reasonable thing we can do.) */ + bool safe:1; +#endif + /* Whether we need to re-compute this coordinate; used to + * weed out duplicates. Maintained only for S_BLACK. */ + bool dirty:1; +}; + + +/* You should treat this struct as read-only. Always call functions below if + * you want to change it. */ + +struct board { + int size; /* Including S_OFFBOARD margin - see below. */ + int size2; /* size^2 */ + int bits2; /* ceiling(log2(size2)) */ + int captures[S_MAX]; + floating_t komi; + int handicap; + /* The ruleset is currently almost never taken into account; + * the board implementation is basically Chinese rules (handicap + * stones compensation) w/ suicide (or you can look at it as + * New Zealand w/o handi stones compensation), while the engine + * enforces no-suicide, making for real Chinese rules. + * However, we accept suicide moves by the opponent, so we + * should work with rules allowing suicide, just not taking + * full advantage of them. */ + enum go_ruleset { + RULES_CHINESE, /* default value */ + RULES_AGA, + RULES_NEW_ZEALAND, + RULES_JAPANESE, + RULES_STONES_ONLY, /* do not count eyes */ + /* http://home.snafu.de/jasiek/siming.html */ + /* Simplified ING rules - RULES_CHINESE with handicaps + * counting as points and pass stones. Also should + * allow suicide, but Pachi will never suicide + * nevertheless. */ + /* XXX: I couldn't find the point about pass stones + * in the rule text, but it is Robert Jasiek's + * interpretation of them... These rules were + * used e.g. at the EGC2012 13x13 tournament. + * They are not supported by KGS. */ + RULES_SIMING, + } rules; + + char *fbookfile; + struct fbook *fbook; + + /* Iterator offsets for foreach_neighbor*() */ + int nei8[8], dnei[4]; + + int moves; + struct move last_move; + struct move last_move2; /* second-to-last move */ + struct move last_move3; /* just before last_move2, only set if last_move is pass */ + struct move last_move4; /* just before last_move3, only set if last_move & last_move2 are pass */ + /* Whether we tried to add a hash twice; board_play*() can + * set this, but it will still carry out the move as well! */ + bool superko_violation; + + /* The following two structures are goban maps and are indexed by + * coord.pos. The map is surrounded by a one-point margin from + * S_OFFBOARD stones in order to speed up some internal loops. + * Some of the foreach iterators below might include these points; + * you need to handle them yourselves, if you need to. */ + + /* Stones played on the board */ + enum stone *b; /* enum stone */ + /* Group id the stones are part of; 0 == no group */ + group_t *g; + /* Positions of next stones in the stone group; 0 == last stone */ + coord_t *p; + /* Neighboring colors; numbers of neighbors of index color */ + struct neighbor_colors *n; + /* Zobrist hash for each position */ + hash_t *h; +#ifdef BOARD_SPATHASH + /* For spatial hashes, we use only 24 bits. */ + /* [0] is d==1, we don't keep hash for d==0. */ + /* We keep hashes for black-to-play ([][0]) and white-to-play + * ([][1], reversed stone colors since we match all patterns as + * black-to-play). */ + uint32_t (*spathash)[BOARD_SPATHASH_MAXD][2]; +#endif +#ifdef BOARD_PAT3 + /* 3x3 pattern code for each position; see pattern3.h for encoding + * specification. The information is only valid for empty points. */ + hash3_t *pat3; +#endif +#ifdef BOARD_TRAITS + /* Incrementally matched point traits information, black-to-play + * ([][0]) and white-to-play ([][1]). */ + /* The information is only valid for empty points. */ + struct btraits (*t)[2]; +#endif + /* Cached information on x-y coordinates so that we avoid division. */ + uint8_t (*coord)[2]; + + /* Group information - indexed by gid (which is coord of base group stone) */ + struct group *gi; + + /* Positions of free positions - queue (not map) */ + /* Note that free position here is any valid move; including single-point eyes! + * However, pass is not included. */ + coord_t *f; int flen; + +#ifdef WANT_BOARD_C + /* Queue of capturable groups */ + group_t *c; int clen; +#endif + +#ifdef BOARD_TRAITS + /* Queue of positions that need their traits updated */ + coord_t *tq; int tqlen; +#endif + + /* Symmetry information */ + struct board_symmetry symmetry; + + /* Last ko played on the board. */ + struct move last_ko; + int last_ko_age; + + /* Basic ko check */ + struct move ko; + + /* Engine-specific state; persistent through board development, + * is reset only at clear_board. */ + void *es; + + /* Playout-specific state; persistent through board development, + * but its lifetime is maintained in play_random_game(); it should + * not be set outside of it. */ + void *ps; + + + /* --- PRIVATE DATA --- */ + + /* For superko check: */ + + /* Board "history" - hashes encountered. Size of the hash should be + * >> board_size^2. */ +#define history_hash_bits 12 +#define history_hash_mask ((1 << history_hash_bits) - 1) +#define history_hash_prev(i) ((i - 1) & history_hash_mask) +#define history_hash_next(i) ((i + 1) & history_hash_mask) + hash_t history_hash[1 << history_hash_bits]; + /* Hash of current board position. */ + hash_t hash; + /* Hash of current board position quadrants. */ + hash_t qhash[4]; +}; + +#ifdef BOARD_SIZE +/* Avoid unused variable warnings */ +#define board_size(b_) (((b_) == (b_)) ? BOARD_SIZE + 2 : 0) +#define board_size2(b_) (board_size(b_) * board_size(b_)) +#else +#define board_size(b_) ((b_)->size) +#define board_size2(b_) ((b_)->size2) +#endif + +/* This is a shortcut for taking different action on smaller + * and large boards (e.g. picking different variable defaults). + * This is of course less optimal than fine-tuning dependency + * function of values on board size, but that is difficult and + * possibly not very rewarding if you are interested just in + * 9x9 and 19x19. */ +#define board_large(b_) (board_size(b_)-2 >= 15) +#define board_small(b_) (board_size(b_)-2 <= 9) + +#if BOARD_SIZE == 19 +# define board_bits2(b_) 9 +#elif BOARD_SIZE == 13 +# define board_bits2(b_) 8 +#elif BOARD_SIZE == 9 +# define board_bits2(b_) 7 +#else +# define board_bits2(b_) ((b_)->bits2) +#endif + +#define board_at(b_, c) ((b_)->b[c]) +#define board_atxy(b_, x, y) ((b_)->b[(x) + board_size(b_) * (y)]) + +#define group_at(b_, c) ((b_)->g[c]) +#define group_atxy(b_, x, y) ((b_)->g[(x) + board_size(b_) * (y)]) + +/* Warning! Neighbor count is kept up-to-date for S_NONE! */ +#define neighbor_count_at(b_, coord, color) ((b_)->n[coord].colors[(enum stone) color]) +#define set_neighbor_count_at(b_, coord, color, count) (neighbor_count_at(b_, coord, color) = (count)) +#define inc_neighbor_count_at(b_, coord, color) (neighbor_count_at(b_, coord, color)++) +#define dec_neighbor_count_at(b_, coord, color) (neighbor_count_at(b_, coord, color)--) +#define immediate_liberty_count(b_, coord) (4 - neighbor_count_at(b_, coord, S_BLACK) - neighbor_count_at(b_, coord, S_WHITE) - neighbor_count_at(b_, coord, S_OFFBOARD)) + +#define trait_at(b_, coord, color) (b_)->t[coord][(color) - 1] + +#define groupnext_at(b_, c) ((b_)->p[c]) +#define groupnext_atxy(b_, x, y) ((b_)->p[(x) + board_size(b_) * (y)]) + +#define group_base(g_) (g_) +#define group_is_onestone(b_, g_) (groupnext_at(b_, group_base(g_)) == 0) +#define board_group_info(b_, g_) ((b_)->gi[(g_)]) +#define board_group_captured(b_, g_) (board_group_info(b_, g_).libs == 0) +/* board_group_other_lib() makes sense only for groups with two liberties. */ +#define board_group_other_lib(b_, g_, l_) (board_group_info(b_, g_).lib[board_group_info(b_, g_).lib[0] != (l_) ? 0 : 1]) + +#define hash_at(b_, coord, color) ((b_)->h[((color) == S_BLACK ? board_size2(b_) : 0) + coord]) + +struct board *board_init(char *fbookfile); +struct board *board_copy(struct board *board2, struct board *board1); +void board_done_noalloc(struct board *board); +void board_done(struct board *board); +/* size here is without the S_OFFBOARD margin. */ +void board_resize(struct board *board, int size); +void board_clear(struct board *board); + +struct FILE; +typedef char *(*board_cprint)(struct board *b, coord_t c, char *s, char *end); +void board_print(struct board *board, FILE *f); +void board_print_custom(struct board *board, FILE *f, board_cprint cprint); + +/* Place given handicap on the board; coordinates are printed to f. */ +void board_handicap(struct board *board, int stones, FILE *f); + +/* Returns group id, 0 on allowed suicide, pass or resign, -1 on error */ +int board_play(struct board *board, struct move *m); +/* Like above, but plays random move; the move coordinate is recorded + * to *coord. This method will never fill your own eye. pass is played + * when no move can be played. You can impose extra restrictions if you + * supply your own permit function; the permit function can also modify + * the move coordinate to redirect the move elsewhere. */ +typedef bool (*ppr_permit)(void *data, struct board *b, struct move *m); +void board_play_random(struct board *b, enum stone color, coord_t *coord, ppr_permit permit, void *permit_data); + +/*Undo, supported only for pass moves. Returns -1 on error, 0 otherwise. */ +int board_undo(struct board *board); + +/* Returns true if given move can be played. */ +static bool board_is_valid_play(struct board *b, enum stone color, coord_t coord); +static bool board_is_valid_move(struct board *b, struct move *m); +/* Returns true if ko was just taken. */ +static bool board_playing_ko_threat(struct board *b); +/* Returns 0 or ID of neighboring group in atari. */ +static group_t board_get_atari_neighbor(struct board *b, coord_t coord, enum stone group_color); +/* Returns true if the move is not obvious self-atari. */ +static bool board_safe_to_play(struct board *b, coord_t coord, enum stone color); + +/* Determine number of stones in a group, up to @max stones. */ +static int group_stone_count(struct board *b, group_t group, int max); + +/* Adjust symmetry information as if given coordinate has been played. */ +void board_symmetry_update(struct board *b, struct board_symmetry *symmetry, coord_t c); +/* Check if coordinates are within symmetry base. (If false, they can + * be derived from the base.) */ +static bool board_coord_in_symmetry(struct board *b, coord_t c); + +/* Returns true if given coordinate has all neighbors of given color or the edge. */ +static bool board_is_eyelike(struct board *board, coord_t coord, enum stone eye_color); +/* Returns true if given coordinate could be a false eye; this check makes + * sense only if you already know the coordinate is_eyelike(). */ +bool board_is_false_eyelike(struct board *board, coord_t coord, enum stone eye_color); +/* Returns true if given coordinate is a 1-pt eye (checks against false eyes, or + * at least tries to). */ +bool board_is_one_point_eye(struct board *board, coord_t c, enum stone eye_color); +/* Returns color of a 1pt eye owner, S_NONE if not an eye. */ +enum stone board_get_one_point_eye(struct board *board, coord_t c); + +/* board_official_score() is the scoring method for yielding score suitable + * for external presentation. For fast scoring of entirely filled boards + * (e.g. playouts), use board_fast_score(). */ +/* Positive: W wins */ +/* Compare number of stones + 1pt eyes. */ +floating_t board_fast_score(struct board *board); +/* Tromp-Taylor scoring, assuming given groups are actually dead. */ +struct move_queue; +floating_t board_official_score(struct board *board, struct move_queue *mq); + +/* Set board rules according to given string. Returns false in case + * of unknown ruleset name. */ +bool board_set_rules(struct board *board, char *name); + +/** Iterators */ + +#define foreach_point(board_) \ + do { \ + coord_t c = 0; \ + for (; c < board_size(board_) * board_size(board_); c++) +#define foreach_point_and_pass(board_) \ + do { \ + coord_t c = pass; \ + for (; c < board_size(board_) * board_size(board_); c++) +#define foreach_point_end \ + } while (0) + +#define foreach_free_point(board_) \ + do { \ + int fmax__ = (board_)->flen; \ + for (int f__ = 0; f__ < fmax__; f__++) { \ + coord_t c = (board_)->f[f__]; +#define foreach_free_point_end \ + } \ + } while (0) + +#define foreach_in_group(board_, group_) \ + do { \ + struct board *board__ = board_; \ + coord_t c = group_base(group_); \ + coord_t c2 = c; c2 = groupnext_at(board__, c2); \ + do { +#define foreach_in_group_end \ + c = c2; c2 = groupnext_at(board__, c2); \ + } while (c != 0); \ + } while (0) + +/* NOT VALID inside of foreach_point() or another foreach_neighbor(), or rather + * on S_OFFBOARD coordinates. */ +#define foreach_neighbor(board_, coord_, loop_body) \ + do { \ + struct board *board__ = board_; \ + coord_t coord__ = coord_; \ + coord_t c; \ + c = coord__ - board_size(board__); do { loop_body } while (0); \ + c = coord__ - 1; do { loop_body } while (0); \ + c = coord__ + 1; do { loop_body } while (0); \ + c = coord__ + board_size(board__); do { loop_body } while (0); \ + } while (0) + +#define foreach_8neighbor(board_, coord_) \ + do { \ + int fn__i; \ + coord_t c = (coord_); \ + for (fn__i = 0; fn__i < 8; fn__i++) { \ + c += (board_)->nei8[fn__i]; +#define foreach_8neighbor_end \ + } \ + } while (0) + +#define foreach_diag_neighbor(board_, coord_) \ + do { \ + int fn__i; \ + coord_t c = (coord_); \ + for (fn__i = 0; fn__i < 4; fn__i++) { \ + c += (board_)->dnei[fn__i]; +#define foreach_diag_neighbor_end \ + } \ + } while (0) + + +static inline bool +board_is_eyelike(struct board *board, coord_t coord, enum stone eye_color) +{ + return (neighbor_count_at(board, coord, eye_color) + + neighbor_count_at(board, coord, S_OFFBOARD)) == 4; +} + +static inline bool +board_is_valid_play(struct board *board, enum stone color, coord_t coord) +{ + if (board_at(board, coord) != S_NONE) + return false; + if (!board_is_eyelike(board, coord, stone_other(color))) + return true; + /* Play within {true,false} eye-ish formation */ + if (board->ko.coord == coord && board->ko.color == color) + return false; +#ifdef BOARD_TRAITS + /* XXX: Disallows suicide. */ + return trait_at(board, coord, color).cap > 0; +#else + int groups_in_atari = 0; + foreach_neighbor(board, coord, { + group_t g = group_at(board, c); + groups_in_atari += (board_group_info(board, g).libs == 1); + }); + return !!groups_in_atari; +#endif +} + +static inline bool +board_is_valid_move(struct board *board, struct move *m) +{ + return board_is_valid_play(board, m->color, m->coord); +} + +static inline bool +board_playing_ko_threat(struct board *b) +{ + return !is_pass(b->ko.coord); +} + +static inline group_t +board_get_atari_neighbor(struct board *b, coord_t coord, enum stone group_color) +{ +#ifdef BOARD_TRAITS + if (!trait_at(b, coord, stone_other(group_color)).cap) return 0; +#endif + foreach_neighbor(b, coord, { + group_t g = group_at(b, c); + if (g && board_at(b, c) == group_color && board_group_info(b, g).libs == 1) + return g; + /* We return first match. */ + }); + return 0; +} + +static inline bool +board_safe_to_play(struct board *b, coord_t coord, enum stone color) +{ + /* number of free neighbors */ + int libs = immediate_liberty_count(b, coord); + if (libs > 1) + return true; + +#ifdef BOARD_TRAITS + /* number of capturable enemy groups */ + if (trait_at(b, coord, color).cap > 0) + return true; // XXX: We don't account for snapback. + /* number of non-capturable friendly groups */ + int noncap_ours = neighbor_count_at(b, coord, color) - trait_at(b, coord, stone_other(color)).cap; + if (noncap_ours < 1) + return false; +/*#else see below */ +#endif + + /* ok, but we need to check if they don't have just two libs. */ + coord_t onelib = -1; + foreach_neighbor(b, coord, { +#ifndef BOARD_TRAITS + if (board_at(b, c) == stone_other(color) && board_group_info(b, group_at(b, c)).libs == 1) + return true; // can capture; no snapback check +#endif + if (board_at(b, c) != color) continue; + group_t g = group_at(b, c); + if (board_group_info(b, g).libs == 1) continue; // in atari + if (board_group_info(b, g).libs == 2) { // two liberties + if (libs > 0) return true; // we already have one real liberty + /* we might be connecting two 2-lib groups, which is ok; + * so remember the other liberty and just make sure it's + * not the same one */ + if (onelib >= 0 && c != onelib) return true; + onelib = board_group_other_lib(b, g, c); + continue; + } + // many liberties + return true; + }); + // no good support group + return false; +} + +static inline int +group_stone_count(struct board *b, group_t group, int max) +{ + int n = 0; + foreach_in_group(b, group) { + n++; + if (n >= max) return max; + } foreach_in_group_end; + return n; +} + +static inline bool +board_coord_in_symmetry(struct board *b, coord_t c) +{ + if (coord_y(c, b) < b->symmetry.y1 || coord_y(c, b) > b->symmetry.y2) + return false; + if (coord_x(c, b) < b->symmetry.x1 || coord_x(c, b) > b->symmetry.x2) + return false; + if (b->symmetry.d) { + int x = coord_x(c, b); + if (b->symmetry.type == SYM_DIAG_DOWN) + x = board_size(b) - 1 - x; + if (x > coord_y(c, b)) + return false; + } + return true; + +} + +#endif diff --git a/jni/pachi/book.dat.bad b/jni/pachi/book.dat.bad new file mode 100644 index 0000000..16de24a --- /dev/null +++ b/jni/pachi/book.dat.bad @@ -0,0 +1 @@ +9 E5 E3 C4 G3 | E7 diff --git a/jni/pachi/book.dat.extra b/jni/pachi/book.dat.extra new file mode 100644 index 0000000..8921abc --- /dev/null +++ b/jni/pachi/book.dat.extra @@ -0,0 +1,30 @@ +9 E5 E7 G6 | E3 +9 E5 E7 G6 E3 | C6 +9 E5 E7 G6 E3 C6 | C7 +9 E5 E7 G6 E3 C6 C7 | D3 +9 E5 E7 G6 E3 C6 C7 D3 | D2 +9 E5 E7 G6 E3 C6 C7 D3 D2 | D4 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 | G4 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 | C5 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 C5 | G3 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 C5 G3 | F8 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 C5 G3 F8 | B6 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 C5 G3 F8 B6 | C2 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 C5 G3 F8 B6 C2 | E2 +9 E5 E7 G6 E3 C6 C7 D3 D2 D4 D6 C5 G3 F8 B6 C2 E2 | E8 +9 E5 E7 G6 E3 C4 | C3 +9 E5 E7 G6 E3 C4 C3 | D4 +9 E5 E7 G6 E3 C4 C3 D4 | D3 +9 E5 E7 G6 E3 C4 C3 D4 D3 | C7 +9 E5 E7 G6 E3 C4 C3 D4 D3 C7 | F6 +9 E5 E7 G6 E3 C4 C3 D4 D3 C7 F6 | F5 +9 E5 E7 G6 E3 C4 C3 D4 D3 C7 F6 F5 | G7 +9 E5 E7 G6 E3 C4 C3 D4 D3 C7 F6 F5 G7 | G3 +9 E5 E7 G5 | E3 +9 E5 E7 G5 E3 | C5 +9 E5 E7 G5 E3 C5 | C7 +9 E5 E7 G5 E3 C5 C7 | C3 +9 E5 E7 G5 E3 C5 C7 C3 | G3 +9 E5 E7 G5 E3 C5 C7 C3 G3 | G7 +9 E5 E7 G6 C6 | C4 +9 E5 G5 | E3 diff --git a/jni/pachi/chat.c b/jni/pachi/chat.c new file mode 100644 index 0000000..918f3ae --- /dev/null +++ b/jni/pachi/chat.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +#define DEBUG + +#include "debug.h" +#include "chat.h" +#include "random.h" + +#define MAX_CHAT_PATTERNS 500 + +static struct chat { + double minwin; + double maxwin; + char from[20]; + char regex[100]; + char reply[300]; // in printf format with one param (100*winrate) + + bool displayed; + bool match; +} *chat_table; + +static char default_reply[] = "I know all those words, but that sentence makes no sense to me"; +static char not_playing[] = "I'm winning big without playing"; + +/* Read the chat file, a sequence of lines of the form: + * minwin;maxwin;from;regex;reply + * Set minwin, maxwin to -1.0 2.0 for answers to chat other than winrate. + * Set from as one space for replies to anyone. + * Examples: + * -1.0;0.3; ;winrate;%.1f%% I'm losing + * -1.0;2.0;pasky;^when ;Today + */ +void chat_init(char *chat_file) { +} + +void chat_done() { +} + +/* Reply to a chat. When not playing, color is S_NONE and all remaining parameters are undefined. + * If some matching entries have not yet been displayed we pick randomly among them. Otherwise + * we pick randomly among all matching entries. */ +char +*generic_chat(struct board *b, bool opponent, char *from, char *cmd, enum stone color, coord_t move, + int playouts, int machines, int threads, double winrate, double extra_komi) { + return NULL; +} diff --git a/jni/pachi/chat.h b/jni/pachi/chat.h new file mode 100644 index 0000000..961cb64 --- /dev/null +++ b/jni/pachi/chat.h @@ -0,0 +1,17 @@ +#ifndef PACHI_CHAT_H +#define PACHI_CHAT_H + +#include + +#include "stone.h" +#include "move.h" + +struct board; + +void chat_init(char *chat_file); +void chat_done(); + +char *generic_chat(struct board *b, bool opponent, char *from, char *cmd, enum stone color, coord_t move, + int playouts, int machines, int threads, double winrate, double extra_komi); + +#endif diff --git a/jni/pachi/debug.h b/jni/pachi/debug.h new file mode 100644 index 0000000..13916ee --- /dev/null +++ b/jni/pachi/debug.h @@ -0,0 +1,28 @@ +#ifndef PACHI_DEBUG_H +#define PACHI_DEBUG_H + +#include + +#ifdef DEBUG +#define DEBUGL_(l, n) (unlikely((l) > (n))) +#define DEBUG_MODE (true) +#else +#define DEBUGL_(l, n) (false) +#define DEBUG_MODE (false) +#endif + +extern int debug_level; +extern bool debug_boardprint; + +#define DEBUGL(n) DEBUGL_(debug_level, n) + +/* The distributed engine can be _very_ verbose so use DEBUGV + * to keep only the first N verbose logs. */ +#ifndef MAX_VERBOSE_LOGS +# define MAX_VERBOSE_LOGS 100000 +#endif +extern long verbose_logs; +#define DEBUGV(verbose, n) (DEBUGL(n) && (!(verbose) || ++verbose_logs < MAX_VERBOSE_LOGS)) +#define DEBUGVV(n) DEBUGV(true, (n)) + +#endif diff --git a/jni/pachi/distributed/Makefile b/jni/pachi/distributed/Makefile new file mode 100644 index 0000000..448a24b --- /dev/null +++ b/jni/pachi/distributed/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=distributed.o protocol.o merge.o + +all: distributed.a +distributed.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/distributed/distributed.c b/jni/pachi/distributed/distributed.c new file mode 100644 index 0000000..ff7ad79 --- /dev/null +++ b/jni/pachi/distributed/distributed.c @@ -0,0 +1,526 @@ +/* This is a master for the "distributed" engine. It receives connections + * from slave machines, sends them gtp commands, then aggregates the + * results. It can also act as a proxy for the logs of all slave machines. + * The slave machines must run with engine "uct" (not "distributed"). + * The master sends pachi-genmoves gtp commands regularly to each slave, + * gets as replies a list of nodes, their number of playouts + * and their value. The master then picks the most popular move + * among the top level nodes. */ + +/* With time control, the master waits for all slaves, except + * when the allowed time is already passed. In this case the + * master picks among the available replies, or waits for just + * one reply if there is none yet. + * Without time control, the master waits until the desired + * number of games have been simulated. In this case the -t + * parameter for the master should be the sum of the parameters + * for all slaves. */ + +/* The master sends updated statistics for the best nodes in each + * genmoves command. They are incremental updates from all other + * slaves (so they exclude contributions from the target slave). + * The slaves reply with just their own stats. So both master and + * slave remember what was previously sent. A slave remembers in + * the tree ("pu" field), which is stable across moves. The slave + * also has a temporary hash table to map received coord paths + * to tree nodes; the hash table is cleared at each new move. + * The master remembers stats in a queue of received buffers that + * are merged together, plus one hash table per slave. The master + * queue and the hash tables are cleared at each new move. */ + +/* To allow the master to select the best move, slaves also send + * absolute playout counts for the best top level nodes (children + * of the root node), including contributions from other slaves. + * The master sums these counts and picks the best sum, which is + * equivalent to picking the best average. (The master cannot + * use the incremental stats sent in binary form because they + * are not maintained across moves, so playouts from previous + * moves would be lost.) */ + +/* The master-slave protocol has fault tolerance. If a slave is + * out of sync, the master sends it the appropriate command history. */ + +/* Pass me arguments like a=b,c=d,... + * Supported arguments: + * slave_port=SLAVE_PORT slaves connect to this port; this parameter is mandatory. + * max_slaves=MAX_SLAVES default 24 + * shared_nodes=SHARED_NODES default 10K + * stats_hbits=STATS_HBITS default 21. 2^stats_bits = hash table size + * slaves_quit=0|1 quit gtp command also sent to slaves, default false. + * proxy_port=PROXY_PORT slaves optionally send their logs to this port. + * Warning: with proxy_port, the master stderr mixes the logs of all + * machines but you can separate them again: + * slave logs: sed -n '/< .*:/s/.*< /< /p' logfile + * master logs: perl -0777 -pe 's/<[ <].*:.*\n//g' logfile + */ + +/* A configuration without proxy would have one master run on masterhost as: + * pachi -e distributed slave_port=1234 + * and N slaves running as: + * pachi -e uct -g masterhost:1234 slave + * With log proxy: + * pachi -e distributed slave_port=1234,proxy_port=1235 + * pachi -e uct -g masterhost:1234 -l masterhost:1235 slave + * If the master itself runs on a machine other than that running gogui, + * gogui-twogtp, kgsGtp or cgosGtp, it can redirect its gtp port: + * pachi -e distributed -g 10000 slave_port=1234,proxy_port=1235 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#include "engine.h" +#include "move.h" +#include "timeinfo.h" +#include "playout.h" +#include "stats.h" +#include "mq.h" +#include "debug.h" +#include "chat.h" +#include "distributed/distributed.h" +#include "distributed/merge.h" + +/* Internal engine state. */ +struct distributed { + char *slave_port; + char *proxy_port; + int max_slaves; + int shared_nodes; + int stats_hbits; + bool slaves_quit; + struct move my_last_move; + struct move_stats my_last_stats; + int slaves; + int threads; +}; + +/* Default number of simulations to perform per move. + * Note that this is in total over all slaves! */ +#define DIST_GAMES 80000 +static const struct time_info default_ti = { + .period = TT_MOVE, + .dim = TD_GAMES, + .len = { .games = DIST_GAMES }, +}; + +#define get_value(value, color) \ + ((color) == S_BLACK ? (value) : 1 - (value)) + + +/* Maximum time (seconds) to wait for answers to fast gtp commands + * (all commands except pachi-genmoves and final_status_list). */ +#define MAX_FAST_CMD_WAIT 0.5 + +/* Maximum time (seconds) to wait for answers to genmoves. */ +#define MAX_GENMOVES_WAIT 0.1 /* 100 ms */ + +/* Minimum time (seconds) to wait before we stop early. This should + * ensure that most slaves have replied at least once. */ +#define MIN_EARLY_STOP_WAIT 0.3 /* 300 ms */ + +/* Display a path as leafdata; + + /* Commands that should not be sent to slaves. + * time_left will be part of next pachi-genmoves, + * we reduce latency by not forwarding it here. */ + if ((!strcasecmp(cmd, "quit") && !dist->slaves_quit) + || !strcasecmp(cmd, "pachi-gentbook") + || !strcasecmp(cmd, "pachi-dumptbook") + || !strcasecmp(cmd, "kgs-chat") + || !strcasecmp(cmd, "time_left") + + /* and commands that will be sent to slaves later */ + || !strcasecmp(cmd, "genmove") + || !strcasecmp(cmd, "kgs-genmove_cleanup") + || !strcasecmp(cmd, "final_score") + || !strcasecmp(cmd, "final_status_list")) + return P_OK; + + protocol_lock(); + + // Create a new command to be sent by the slave threads. + new_cmd(b, cmd, args); + + /* Wait for replies here. If we don't wait, we run the + * risk of getting out of sync with most slaves and + * sending command history too frequently. But don't wait + * for all slaves otherwise we can lose on time because of + * a single slow slave when replaying a whole game. */ + int min_slaves = active_slaves > 1 ? 3 * active_slaves / 4 : 1; + get_replies(time_now() + MAX_FAST_CMD_WAIT, min_slaves); + + protocol_unlock(); + + // At the beginning wait even more for late slaves. + if (b->moves == 0) sleep(1); + return P_OK; +} + +/* The playouts sent by slaves for the children of the root node + * include contributions from other slaves. To avoid 32-bit overflow on + * large configurations with many slaves we must average the playouts. */ +struct large_stats { + long playouts; // # of playouts + floating_t value; // BLACK wins/playouts +}; + +static void +large_stats_add_result(struct large_stats *s, floating_t result, long playouts) +{ + s->playouts += playouts; + s->value += (result - s->value) * playouts / s->playouts; +} + +/* genmoves returns "=id played_own total_playouts threads keep_looking @size" + * then a list of lines "coord playouts value" with absolute counts for + * children of the root node, then a binary array of incr_stats structs. + * To simplify the code, we assume that master and slave have the same architecture + * (store values identically). + * Return the move with most playouts, and additional stats. + * keep_looking is set from a majority vote of the slaves seen so far for this + * move but should not be trusted if too few slaves have been seen. + * Keep this code in sync with uct/slave.c:report_stats(). + * slave_lock is held on entry and on return. */ +static coord_t +select_best_move(struct board *b, struct large_stats *stats, int *played, + int *total_playouts, int *total_threads, bool *keep_looking) +{ + assert(reply_count > 0); + + /* +2 for pass and resign */ + memset(stats-2, 0, (board_size2(b)+2) * sizeof(*stats)); + + coord_t best_move = pass; + long best_playouts = 0; + *played = 0; + *total_playouts = 0; + *total_threads = 0; + int keep = 0; + + for (int reply = 0; reply < reply_count; reply++) { + char *r = gtp_replies[reply]; + int id, o, p, t, k; + if (sscanf(r, "=%d %d %d %d %d", &id, &o, &p, &t, &k) != 5) continue; + *played += o; + *total_playouts += p; + *total_threads += t; + keep += k; + // Skip the rest of the firt line in particular @size + r = strchr(r, '\n'); + + char move[64]; + struct move_stats s; + while (r && sscanf(++r, "%63s %d " PRIfloating, move, &s.playouts, &s.value) == 3) { + coord_t c = str2scoord(move, board_size(b)); + assert (c >= resign && c < board_size2(b) && s.playouts >= 0); + + large_stats_add_result(&stats[c], s.value, (long)s.playouts); + + if (stats[c].playouts > best_playouts) { + best_playouts = stats[c].playouts; + best_move = c; + } + r = strchr(r, '\n'); + } + } + for (coord_t c = resign; c < board_size2(b); c++) + stats[c].playouts /= reply_count; + *keep_looking = keep > reply_count / 2; + return best_move; +} + +/* Set the args for the genmoves command. If binary_args is set, + * each slave thred will add the correct binary size when sending + * (see get_binary_arg()). args must have CMDS_SIZE bytes and + * upon return ends with a single \n. + * Keep this code in sync with uct/slave.c:uct_genmoves(). + * slave_lock is held on entry and on return but we don't + * rely on the lock here. */ +static void +genmoves_args(char *args, enum stone color, int played, + struct time_info *ti, bool binary_args) +{ + char *end = args + CMDS_SIZE; + char *s = args + snprintf(args, CMDS_SIZE, "%s %d", stone2str(color), played); + + if (ti->dim == TD_WALLTIME) { + s += snprintf(s, end - s, " %.3f %.3f %d %d", + ti->len.t.main_time, ti->len.t.byoyomi_time, + ti->len.t.byoyomi_periods, ti->len.t.byoyomi_stones); + } + s += snprintf(s, end - s, binary_args ? " @0\n" : "\n"); +} + +/* Time control is mostly done by the slaves, so we use default values here. */ +#define FUSEKI_END 20 +#define YOSE_START 40 +#define MAX_MAINTIME_RATIO 3.0 + +/* Regularly send genmoves command to the slaves, and select the best move. */ +static coord_t * +distributed_genmove(struct engine *e, struct board *b, struct time_info *ti, + enum stone color, bool pass_all_alive) +{ + struct distributed *dist = e->data; + double now = time_now(); + double first = now; + char buf[BSIZE]; // debug only + + char *cmd = pass_all_alive ? "pachi-genmoves_cleanup" : "pachi-genmoves"; + char args[CMDS_SIZE]; + + coord_t best; + int played, playouts, threads; + + if (ti->period == TT_NULL) *ti = default_ti; + struct time_stop stop; + time_stop_conditions(ti, b, FUSEKI_END, YOSE_START, MAX_MAINTIME_RATIO, &stop); + struct time_info saved_ti = *ti; + + /* Combined move stats from all slaves, only for children + * of the root node, plus 2 for pass and resign. */ + struct large_stats stats_array[board_size2(b) + 2], *stats; + stats = &stats_array[2]; + + protocol_lock(); + clear_receive_queue(); + + /* Send the first genmoves without stats. */ + genmoves_args(args, color, 0, ti, false); + new_cmd(b, cmd, args); + + /* Loop until most slaves want to quit or time elapsed. */ + int iterations; + for (iterations = 1; ; iterations++) { + double start = now; + /* Wait for just one slave to get stats as fresh as possible, + * or at most 100ms to check if we run out of time. */ + get_replies(now + MAX_GENMOVES_WAIT, 1); + now = time_now(); + if (ti->dim == TD_WALLTIME) + time_sub(ti, now - start, false); + + bool keep_looking; + best = select_best_move(b, stats, &played, &playouts, &threads, &keep_looking); + + if (ti->dim == TD_WALLTIME) { + if (now - ti->len.t.timer_start >= stop.worst.time) break; + if (!keep_looking && now - first >= MIN_EARLY_STOP_WAIT) break; + } else { + if (!keep_looking || played >= stop.worst.playouts) break; + } + if (DEBUGVV(2)) { + char *coord = coord2sstr(best, b); + snprintf(buf, sizeof(buf), + "temp winner is %s %s with score %1.4f (%d/%d games)" + " %d slaves %d threads\n", + stone2str(color), coord, get_value(stats[best].value, color), + (int)stats[best].playouts, playouts, reply_count, threads); + logline(NULL, "* ", buf); + } + /* Send the command with the same gtp id, to avoid discarding + * a reply to a previous genmoves at the same move. */ + genmoves_args(args, color, played, ti, true); + update_cmd(b, cmd, args, false); + } + int replies = reply_count; + + /* Do not subtract time spent twice (see gtp_parse). */ + *ti = saved_ti; + + dist->my_last_move.color = color; + dist->my_last_move.coord = best; + dist->my_last_stats.value = stats[best].value; + dist->my_last_stats.playouts = (int)stats[best].playouts; + dist->slaves = reply_count; + dist->threads = threads; + + /* Tell the slaves to commit to the selected move, overwriting + * the last "pachi-genmoves" in the command history. */ + clear_receive_queue(); + char coordbuf[4]; + char *coord = coord2bstr(coordbuf, best, b); + snprintf(args, sizeof(args), "%s %s\n", stone2str(color), coord); + update_cmd(b, "play", args, true); + protocol_unlock(); + + if (DEBUGL(1)) { + double time = now - first + 0.000001; /* avoid divide by zero */ + snprintf(buf, sizeof(buf), + "GLOBAL WINNER is %s %s with score %1.4f (%d/%d games)\n" + "genmove %d games in %0.2fs %d slaves %d threads (%d games/s," + " %d games/s/slave, %d games/s/thread, %.3f ms/iter)\n", + stone2str(color), coord, get_value(stats[best].value, color), + (int)stats[best].playouts, playouts, played, time, replies, threads, + (int)(played/time), (int)(played/time/replies), + (int)(played/time/threads), 1000*time/iterations); + logline(NULL, "* ", buf); + } + if (DEBUGL(3)) { + int total_hnodes = replies * (1 << dist->stats_hbits); + merge_print_stats(total_hnodes); + } + return coord_copy(best); +} + +static char * +distributed_chat(struct engine *e, struct board *b, bool opponent, char *from, char *cmd) +{ + struct distributed *dist = e->data; + double winrate = get_value(dist->my_last_stats.value, dist->my_last_move.color); + + return generic_chat(b, opponent, from, cmd, dist->my_last_move.color, dist->my_last_move.coord, + dist->my_last_stats.playouts, dist->slaves, dist->threads, winrate, 0.0); +} + +static int +scmp(const void *p1, const void *p2) +{ + return strcasecmp(*(char * const *)p1, *(char * const *)p2); +} + +static void +distributed_dead_group_list(struct engine *e, struct board *b, struct move_queue *mq) +{ + protocol_lock(); + + new_cmd(b, "final_status_list", "dead\n"); + get_replies(time_now() + MAX_FAST_CMD_WAIT, active_slaves); + + /* Find the most popular reply. */ + qsort(gtp_replies, reply_count, sizeof(char *), scmp); + int best_reply = 0; + int best_count = 1; + int count = 1; + for (int reply = 1; reply < reply_count; reply++) { + if (!strcmp(gtp_replies[reply], gtp_replies[reply-1])) { + count++; + } else { + count = 1; + } + if (count > best_count) { + best_count = count; + best_reply = reply; + } + } + + /* Pick the first move of each line as group. */ + char *dead = gtp_replies[best_reply]; + dead = strchr(dead, ' '); // skip "id " + while (dead && *++dead != '\n') { + mq_add(mq, str2scoord(dead, board_size(b)), 0); + dead = strchr(dead, '\n'); + } + protocol_unlock(); +} + +static struct distributed * +distributed_state_init(char *arg, struct board *b) +{ + struct distributed *dist = calloc2(1, sizeof(struct distributed)); + + dist->stats_hbits = DEFAULT_STATS_HBITS; + dist->max_slaves = DEFAULT_MAX_SLAVES; + dist->shared_nodes = DEFAULT_SHARED_NODES; + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "slave_port") && optval) { + dist->slave_port = strdup(optval); + } else if (!strcasecmp(optname, "proxy_port") && optval) { + dist->proxy_port = strdup(optval); + } else if (!strcasecmp(optname, "max_slaves") && optval) { + dist->max_slaves = atoi(optval); + } else if (!strcasecmp(optname, "shared_nodes") && optval) { + /* Share at most shared_nodes between master and slave at each genmoves. + * Must use the same value in master and slaves. */ + dist->shared_nodes = atoi(optval); + } else if (!strcasecmp(optname, "stats_hbits") && optval) { + /* Set hash table size to 2^stats_hbits for the shared stats. */ + dist->stats_hbits = atoi(optval); + } else if (!strcasecmp(optname, "slaves_quit")) { + dist->slaves_quit = !optval || atoi(optval); + } else { + fprintf(stderr, "distributed: Invalid engine argument %s or missing value\n", optname); + } + } + } + + gtp_replies = calloc2(dist->max_slaves, sizeof(char *)); + + if (!dist->slave_port) { + fprintf(stderr, "distributed: missing slave_port\n"); + exit(1); + } + + merge_init(&default_sstate, dist->shared_nodes, dist->stats_hbits, dist->max_slaves); + protocol_init(dist->slave_port, dist->proxy_port, dist->max_slaves); + + return dist; +} + +struct engine * +engine_distributed_init(char *arg, struct board *b) +{ + struct distributed *dist = distributed_state_init(arg, b); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "Distributed"; + e->comment = "If you believe you have won but I am still playing, " + "please help me understand by capturing all dead stones. " + "Anyone can send me 'winrate' in private chat to get my assessment of the position."; + e->notify = distributed_notify; + e->genmove = distributed_genmove; + e->dead_group_list = distributed_dead_group_list; + e->chat = distributed_chat; + e->data = dist; + // Keep the threads and the open socket connections: + e->keep_on_clear = true; + + return e; +} diff --git a/jni/pachi/distributed/distributed.h b/jni/pachi/distributed/distributed.h new file mode 100644 index 0000000..f90a8f4 --- /dev/null +++ b/jni/pachi/distributed/distributed.h @@ -0,0 +1,104 @@ +#ifndef PACHI_DISTRIBUTED_DISTRIBUTED_H +#define PACHI_DISTRIBUTED_DISTRIBUTED_H + +#include + +#include "engine.h" +#include "stats.h" + +/* A coord path encodes coordinates from root child to a given node: + * A1->B2->C3 is encoded as coord(A1)<<18 + coord(B2)<<9 + coord(C3) + * for 19x19. In this version the table is not a transposition table + * so A1->B2->C3 and C3->B2->A1 are different. + * The depth is limited to 7 for 19x19 (9 for 9x9) to fit in 64 bits. + * path_t is signed to include pass and resign. */ +typedef int64_t path_t; +#define PRIpath PRIx64 +#define PATH_T_MAX INT64_MAX + +#define hash_mask(bits) ((1<<(bits))-1) + +/* parent_path() must never be used if path might be pass or resign. */ +#define parent_path(path, board) ((path) >> board_bits2(board)) +#define leaf_coord(path, board) ((path) & hash_mask(board_bits2(board))) +#define append_child(path, c, board) (((path) << board_bits2(board)) | (c)) +#define max_parent_path(u, b) (((path_t)1) << (((u)->shared_levels - 1) * board_bits2(b))) + + +/* For debugging only */ +struct hash_counts { + long lookups; + long collisions; + long inserts; + long occupied; +}; + +/* Find a hash table entry given its coord path from root. + * Set found to false if the entry is empty. + * Abort if the table gets too full (should never happen). + * We use double hashing and coord_path = 0 for unused entries. */ +#define find_hash(hash, table, hash_bits, path, found, counts) \ + do { \ + if (DEBUG_MODE) counts.lookups++; \ + int mask = hash_mask(hash_bits); \ + int delta = (int)((path) >> (hash_bits)) | 1; \ + hash = ((int)(path) ^ delta ^ (delta >> (hash_bits))) & mask; \ + path_t cp = (table)[hash].coord_path; \ + found = (cp == path); \ + if (found | !cp) break; \ + int tries = 1 << ((hash_bits)-2); \ + do { \ + if (DEBUG_MODE) counts.collisions++; \ + hash = (hash + delta) & mask; \ + cp = (table)[hash].coord_path; \ + found = (cp == path); \ + if (found | !cp) break; \ + } while (--tries); \ + assert(tries); \ + } while (0) + + +/* Stats exchanged between master and slave. They are always + * incremental values to be added to what was last sent. */ +struct incr_stats { + path_t coord_path; + struct move_stats incr; +}; + +/* A slave machine updates at most 7 (19x19) or 9 (9x9) nodes for each + * update of the root node. If we have at most 20 threads at 1500 + * games/s each, a slave machine can do at most 30K games/s. */ + +/* At 30K games/s a slave can output 270K nodes/s or 4.2 MB/s. The master + * with a 100 MB/s network can thus support at most 24 slaves. */ +#define DEFAULT_MAX_SLAVES 24 + +/* In a 30s move at 270K nodes/s a slave can send and receive at most + * 8.1M nodes so at worst 23 bits are needed for the hash table in the + * slave and for the per-slave hash table in the master. However the + * same nodes are often sent so in practice 21 bits are sufficient. + * Larger hash tables are not desirable because it would take too much + * time to clear them at each move in the master. For the default + * shared_levels=1, 18 bits are enough. */ +#define DEFAULT_STATS_HBITS 18 + +/* If we select a cycle of at most 40ms, a slave machine can update at + * most 10K different nodes per cycle. In practice the updates are + * biased so we update much fewer nodes. As shorter cyle is preferable + * because the stats are more fresh. The cycle time does not affect + * the number of slaves and the hash table size. */ +#define DEFAULT_SHARED_NODES 10240 + + +/* Maximum game length. Power of 10 jut to ease debugging. */ +#define DIST_GAMELEN 1000 + +#define force_reply(id) ((id) + DIST_GAMELEN) +#define prevent_reply(id) ((id) % DIST_GAMELEN) +#define move_number(id) ((id) % DIST_GAMELEN) +#define reply_disabled(id) ((id) < DIST_GAMELEN) + +char *path2sstr(path_t path, struct board *b); +struct engine *engine_distributed_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/distributed/merge.c b/jni/pachi/distributed/merge.c new file mode 100644 index 0000000..b1cc1a6 --- /dev/null +++ b/jni/pachi/distributed/merge.c @@ -0,0 +1,347 @@ +/* The master keeps stats received from slaves in a queue of received + * buffers that are merged together with the functions implemented + * here. It also has one hash table per slave to maintain cumulative + * stats that have not yet been sent to the slave machine. The queue + * and the hash tables are cleared at each new move. */ + +#include +#include +#include + +#define DEBUG + +#include "debug.h" +#include "timeinfo.h" +#include "distributed/distributed.h" +#include "distributed/merge.h" + +/* We merge together debug stats for all hash tables. */ +static struct hash_counts h_counts; + +/* Display and reset hash statistics. For debugging only. */ +void +merge_print_stats(int total_hnodes) +{ + if (DEBUGL(3)) { + char buf[BSIZE]; + snprintf(buf, sizeof(buf), + "stats occupied %ld %.1f%% inserts %ld collisions %ld/%ld %.1f%%\n", + h_counts.occupied, h_counts.occupied * 100.0 / total_hnodes, + h_counts.inserts, h_counts.collisions, h_counts.lookups, + h_counts.collisions * 100.0 / (h_counts.lookups + 1)); + logline(NULL, "* ", buf); + } + if (DEBUG_MODE) h_counts.occupied = 0; +} + +/* We maintain counts per bucket to avoid sorting large arrays. + * All nodes with n updates since last send go to bucket n. + * We have at most max_merged_nodes = (max_slaves-1) * shared_nodes + * nodes to merge, 230K nodes for 24 slaves. If we put all nodes above + * 1K updates in the top bucket, we get at most 230 nodes in this + * bucket. So we can select exactly the best shared_nodes nodes if + * shared_nodes >= 230. In practice there is overlap between + * nodes sent by different slaves so shared_nodes can be lower. */ +#define MAX_BUCKETS 1024 + +/* Update the hash table for the given increment stats, + * and increment the bucket count. Return the hash index. + * The slave lock is not held on either entry or exit of this function */ +static inline int +stats_tally(struct incr_stats *s, struct slave_state *sstate, int *bucket_count) +{ + int h; + bool found; + struct incr_stats *stats_htable = sstate->stats_htable; + find_hash(h, stats_htable, sstate->stats_hbits, s->coord_path, found, h_counts); + if (found) { + assert(stats_htable[h].incr.playouts > 0); + stats_add_result(&stats_htable[h].incr, s->incr.value, s->incr.playouts); + } else { + stats_htable[h] = *s; + if (DEBUG_MODE) h_counts.inserts++, h_counts.occupied++; + } + + int incr = stats_htable[h].incr.playouts; + if (incr >= MAX_BUCKETS) incr = MAX_BUCKETS - 1; + bucket_count[incr]++; + return h; +} + +static struct incr_stats terminator = { .coord_path = INT64_MAX }; + +/* Initialize the next pointers (see merge_new_stats()). + * Exclude invalid buffers and my own buffers by setting their next pointer + * to a terminator value. Update min if there are too many nodes to merge, + * so that merge time remains reasonable and the merge buffer doesn't overflow. + * (We skip the oldest buffers if the slave thread is too much behind. It is + * more important to get frequent incomplete updates than late complete updates.) + * Return the total number of nodes to be merged. + * The slave lock is not held on either entry or exit of this function. */ +static int +filter_buffers(struct slave_state *sstate, struct incr_stats **next, + int *min, int max) +{ + int size = 0; + int max_size = sstate->max_merged_nodes * sizeof(struct incr_stats); + + for (int q = max; q >= *min; q--) { + if (!receive_queue[q] || receive_queue[q]->owner == sstate->thread_id) { + next[q] = &terminator; + } else if (size + receive_queue[q]->size > max_size) { + *min = q + 1; + assert(*min <= max); + break; + } else { + next[q] = (struct incr_stats *)receive_queue[q]->buf; + size += receive_queue[q]->size; + } + } + return size / sizeof(struct incr_stats); +} + +/* Return the minimum coord path of next[min..max]. + * This implementation is optimized for small values of max - min, + * which is the case if slaves are not too much behind. + * A heap (priority queue) could be used otherwise. + * The returned value might be come from a buffer that has + * been invalidated, the caller must check for this; in this + * case the returned value is < the correct value. */ +static inline path_t +min_coord(struct incr_stats **next, int min, int max) +{ + path_t min_c = next[min]->coord_path; + for (int q = min + 1; q <= max; q++) { + if (next[q]->coord_path < min_c) + min_c = next[q]->coord_path; + } + return min_c; +} + +/* Merge all valid incremental stats in receive_queue[min..max], + * update the hash table, set the bucket counts, and save the + * list of updated hash table entries. The input buffers and + * the output buffer are all sorted by increasing coord path. + * The input buffers end with a terminator value INT64_MAX. + * Return the number of updated hash table entries. */ + +/* The slave lock is not held on either entry or exit of this function, + * so receive_queue entries may be invalidated while we scan them. + * The receive queue might grow while we scan it but we ignore + * entries above max, they will be processed at the next call. + * This function does not modify the receive queue. */ +static int +merge_new_stats(struct slave_state *sstate, int min, int max, + int *bucket_count, int *nodes_read, int last_queue_age) +{ + *nodes_read = 0; + if (max < min) return 0; + + /* next[q] is the next value to be checked in receive_queue[q]->buf */ + struct incr_stats *next_[max - min + 1]; + struct incr_stats **next = next_ - min; + *nodes_read = filter_buffers(sstate, next, &min, max); + + /* prev_min_c is only used for debugging. */ + path_t prev_min_c = 0; + + /* Do N-way merge, processing one coord path per iteration. + * If the minimum coord is INT64_MAX, either all buffers are + * invalidated, or at least one is valid and we are at the + * end of all valid buffers. In both cases we're done. */ + int merge_count = 0; + path_t min_c; + while ((min_c = min_coord(next, min, max)) != INT64_MAX) { + + struct incr_stats sum = { .coord_path = min_c, + .incr = { .playouts = 0, .value = 0.0 }}; + for (int q = min; q <= max; q++) { + struct incr_stats s = *(next[q]); + + /* If s.coord_path != min_c, we must skip s.coord_path for now. + * If min_c is invalid, a future iteration will get a stable + * value since the call of min_coord(), so at some point we will + * get s.coord_path == min_c and we will not loop forever. */ + if (s.coord_path != min_c) continue; + + /* We check the buffer validity after s.coord has been checked + * to avoid a race condition, and also to avoid multiple useless + * checks for the same coord_path. */ + if (unlikely(!receive_queue[q])) { + next[q] = &terminator; + continue; + } + + /* Stop if we have a new move. If queue_age is incremented + * after this check, the merged output will be discarded. */ + if (unlikely(queue_age > last_queue_age)) return 0; + + /* s.coord_path is valid here, so min_c is valid too. + * (An invalid min_c would be < s.coord_path.) */ + assert(min_c > prev_min_c); + + assert(s.coord_path && s.incr.playouts); + stats_add_result(&sum.incr, s.incr.value, s.incr.playouts); + next[q]++; + } + /* All the buffers containing min_c may have been invalidated + * so sum may still be zero. But in this case the next[q] which + * contained min_c have been reset to &terminator so we will + * not loop forever. */ + if (!sum.incr.playouts) continue; + + assert(min_c > prev_min_c); + if (DEBUG_MODE) prev_min_c = min_c; + + /* At this point sum contains only valid increments, + * so we can add it to the hash table. */ + assert(merge_count < sstate->max_merged_nodes); + sstate->merged[merge_count++] = stats_tally(&sum, sstate, bucket_count); + } + return merge_count; +} + +/* Save in buf the best increments from other slaves merged previously. + * To avoid a costly scan of the entire hash table we only send nodes + * that were previously sent recently by other slaves. It is possible + * but very unlikely that the hash table contains some nodes with + * higher number of playouts. + * Return the number of nodes to be sent. + * The slave lock is not held on either entry or exit of this function. */ +static int +output_stats(struct incr_stats *buf, struct slave_state *sstate, + int *bucket_count, int merge_count) +{ + /* Find the minimum increment to send. The bucket with minimum + * increment may be sent only partially. */ + int out_count = 0; + int min_incr = MAX_BUCKETS; + int shared_nodes = sstate->max_buf_size / sizeof(*buf); + do { + out_count += bucket_count[--min_incr]; + } while (min_incr > 1 && out_count < shared_nodes); + + /* Send all all increments > min_incr plus whatever we can at min_incr. */ + int min_count = bucket_count[min_incr] - (out_count - shared_nodes); + out_count = 0; + int *merged = sstate->merged; + struct incr_stats *stats_htable = sstate->stats_htable; + while (merge_count--) { + int h = *merged++; + int delta = stats_htable[h].incr.playouts - min_incr; + if (delta < 0 || (delta == 0 && --min_count < 0)) continue; + + assert (out_count < shared_nodes); + buf[out_count++] = stats_htable[h]; + + /* Clear the hash table entry. (We could instead + * just clear the playouts but clearing the entry + * leads to fewer collisions later.) */ + stats_htable[h].coord_path = 0; + if (DEBUG_MODE) h_counts.occupied--; + } + /* The slave expects increments sorted by coord path + * but they are sorted already. */ + return out_count; +} + +/* Get all incremental stats received from other slaves since the + * last send. Store in buf the stats with largest playout increments. + * Return the byte size of the resulting buffer. The caller must + * check that the result is still valid. + * The slave lock is held on both entry and exit of this function. */ +static int +get_new_stats(struct incr_stats *buf, struct slave_state *sstate, int cmd_id) +{ + /* Process all valid buffers in receive_queue[min..max] */ + int min = sstate->last_processed + 1; + int max = queue_length - 1; + if (max < min && cmd_id == sstate->stats_id) return 0; + + sstate->last_processed = max; + int last_queue_age = queue_age; + + /* It takes time to clear the hash table and merge the stats + * so do this unlocked. */ + protocol_unlock(); + + double start = time_now(); + double clear_time = 0; + + /* Clear the hash table at a new move; the old paths in + * the hash table are now meaningless. */ + if (cmd_id != sstate->stats_id) { + memset(sstate->stats_htable, 0, + (1 << sstate->stats_hbits) * sizeof(sstate->stats_htable[0])); + sstate->stats_id = cmd_id; + clear_time = time_now() - start; + } + + /* Set the bucket counts and update the hash table stats. */ + int bucket_count[MAX_BUCKETS]; + memset(bucket_count, 0, sizeof(bucket_count)); + int nodes_read; + int merge_count = merge_new_stats(sstate, min, max, bucket_count, + &nodes_read, last_queue_age); + + int missed = 0; + if (DEBUG_MODE) + for (int q = min; q <= max; q++) missed += !receive_queue[q]; + + /* Put the best increments in the output buffer. */ + int output_nodes = output_stats(buf, sstate, bucket_count, merge_count); + + if (DEBUGVV(2)) { + char b[1024]; + snprintf(b, sizeof(b), "merged %d..%d missed %d %d/%d nodes," + " output %d/%d nodes in %.3fms (clear %.3fms)\n", + min, max, missed, merge_count, nodes_read, output_nodes, + sstate->max_buf_size / (int)sizeof(*buf), + (time_now() - start)*1000, clear_time*1000); + logline(&sstate->client, "= ", b); + } + + protocol_lock(); + + return output_nodes * sizeof(*buf); +} + +/* Allocate the buffers in the merge specific part of the slave sate, + * and reserve space for a terminator value (see merge_insert_hook). */ +static void +merge_state_alloc(struct slave_state *sstate) +{ + sstate->stats_htable = calloc2(1 << sstate->stats_hbits, sizeof(struct incr_stats)); + sstate->merged = malloc2(sstate->max_merged_nodes * sizeof(int)); + sstate->max_buf_size -= sizeof(struct incr_stats); +} + +/* Append a terminator value to make merge_new_stats() more + * efficient. merge_state_alloc() has reserved enough space. */ +static void +merge_insert_hook(struct incr_stats *buf, int size) +{ + int nodes = size / sizeof(*buf); + buf[nodes].coord_path = INT64_MAX; +} + +/* Initiliaze merge-related fields of the default slave state. */ +void +merge_init(struct slave_state *sstate, int shared_nodes, int stats_hbits, int max_slaves) +{ + /* See merge_state_alloc() for shared_nodes + 1 */ + sstate->max_buf_size = (shared_nodes + 1) * sizeof(struct incr_stats); + sstate->stats_hbits = stats_hbits; + + sstate->insert_hook = (buffer_hook)merge_insert_hook; + sstate->alloc_hook = merge_state_alloc; + sstate->args_hook = (getargs_hook)get_new_stats; + + /* At worst one late slave thread may have to merge up to + * shared_nodes * BUFFERS_PER_SLAVE * (max_slaves - 1) + * nodes but on average it should not have to merge more than + * dist->shared_nodes * (max_slaves - 1) + * Restricting the maximum number of merged nodes to the latter avoids + * spending excessive time on the merge. */ + sstate->max_merged_nodes = shared_nodes * (max_slaves - 1); +} diff --git a/jni/pachi/distributed/merge.h b/jni/pachi/distributed/merge.h new file mode 100644 index 0000000..e7634c7 --- /dev/null +++ b/jni/pachi/distributed/merge.h @@ -0,0 +1,9 @@ +#ifndef PACHI_DISTRIBUTED_MERGE_H +#define PACHI_DISTRIBUTED_MERGE_H + +#include "distributed/protocol.h" + +void merge_print_stats(int total_hnodes); +void merge_init(struct slave_state *sstate, int shared_nodes, int stats_hbits, int max_slaves); + +#endif diff --git a/jni/pachi/distributed/protocol.c b/jni/pachi/distributed/protocol.c new file mode 100644 index 0000000..c379c2a --- /dev/null +++ b/jni/pachi/distributed/protocol.c @@ -0,0 +1,681 @@ +/* The functions implementing the master-slave protocol of the + * distributed engine are grouped here. They are independent + * of the gtp protocol. See the comments at the top of distributed.c + * for a general introduction to the distributed engine. */ + +/* The receive queue is an array of pointers to binary buffers. + * These pointers are invalidated in one of two ways when a buffer + * is recycled: (1) the queue age is increased when the queue is + * emptied at a new move, (2) the pointer itself is set to NULL + * immmediately, and stays so until at least the next queue age + * increment. */ + +#include +#include +#include +#include +#include + +#define DEBUG + +#include "random.h" +#include "timeinfo.h" +#include "playout.h" +#include "network.h" +#include "debug.h" +#include "distributed/distributed.h" +#include "distributed/protocol.h" + +/* All gtp commands for current game separated by \n */ +static char gtp_cmds[CMDS_SIZE]; + +/* Latest gtp command sent to slaves. */ +static char *gtp_cmd = NULL; + +/* Slaves send gtp_cmd when cmd_count changes. */ +static int cmd_count = 0; + +/* Remember at most 10 gtp ids per move: kgs-rules, boardsize, clear_board, + * time_settings, komi, handicap, genmoves, play pass, play pass, final_status_list */ +#define MAX_CMDS_PER_MOVE 10 + +/* History of gtp commands sent for current game, indexed by move. */ +static struct cmd_history { + int gtp_id; + char *next_cmd; +} history[MAX_GAMELEN][MAX_CMDS_PER_MOVE]; + +/* Number of active slave machines working for this master. */ +int active_slaves = 0; + +/* Number of replies to last gtp command already received. */ +int reply_count = 0; + +/* All replies to latest gtp command are in gtp_replies[0..reply_count-1]. */ +char **gtp_replies; + + +struct buf_state **receive_queue; +int queue_length = 0; +int queue_age = 0; +static int queue_max_length; + +/* Mutex protecting all variables above. receive_queue may be + * read without the lock but is only written with lock held. */ +static pthread_mutex_t slave_lock = PTHREAD_MUTEX_INITIALIZER; + +/* Condition signaled when a new gtp command is available. */ +static pthread_cond_t cmd_cond = PTHREAD_COND_INITIALIZER; + +/* Condition signaled when reply_count increases. */ +static pthread_cond_t reply_cond = PTHREAD_COND_INITIALIZER; + +/* Mutex protecting stderr. Must not be held at same time as slave_lock. */ +static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER; + +/* Absolute time when this program was started. + * For debugging only. */ +static double start_time; + +/* Default slave state. */ +struct slave_state default_sstate; + + +/* Get exclusive access to the threads and commands state. */ +void +protocol_lock(void) +{ + pthread_mutex_lock(&slave_lock); +} + +/* Release exclusive access to the threads and commands state. */ +void +protocol_unlock(void) +{ + pthread_mutex_unlock(&slave_lock); +} + +/* Write the time, client address, prefix, and string s to stderr atomically. + * s should end with a \n */ +void +logline(struct in_addr *client, char *prefix, char *s) +{ + double now = time_now(); + + char addr[INET_ADDRSTRLEN]; + if (client) { +#ifdef _WIN32 + strcpy(addr, inet_ntoa(*client)); +#else + inet_ntop(AF_INET, client, addr, sizeof(addr)); +#endif + } else { + addr[0] = '\0'; + } + pthread_mutex_lock(&log_lock); + fprintf(stderr, "%s%15s %9.3f: %s", prefix, addr, now - start_time, s); + pthread_mutex_unlock(&log_lock); +} + +/* Thread opening a connection on the given socket and copying input + * from there to stderr. */ +static void * +proxy_thread(void *arg) +{ + int proxy_sock = (long)arg; + assert(proxy_sock >= 0); + for (;;) { + struct in_addr client; + int conn = open_server_connection(proxy_sock, &client); + FILE *f = fdopen(conn, "r"); + char buf[BSIZE]; + while (fgets(buf, BSIZE, f)) { + logline(&client, "< ", buf); + } + fclose(f); + } +} + +/* Get a reply to one gtp command. Return the gtp command id, + * or -1 if error. reply must have at least CMDS_SIZE bytes. + * The ascii reply ends with an empty line; if the first line + * contains "@size", a binary reply of size bytes follows the + * empty line. @size is not standard gtp, it is only used + * internally by Pachi for the genmoves command; it must be the + * last parameter on the line. + * *bin_size is the maximum size upon entry, actual size on return. + * slave_lock is not held on either entry or exit of this function. */ +static int +get_reply(FILE *f, struct in_addr client, char *reply, void *bin_reply, int *bin_size) +{ + double start = time_now(); + + int reply_id = -1; + *reply = '\0'; + if (!fgets(reply, CMDS_SIZE, f)) return -1; + + /* Check for binary reply. */ + char *s = strchr(reply, '@'); + int size = 0; + if (s) size = atoi(s+1); + assert(size <= *bin_size); + *bin_size = size; + + if (DEBUGV(s, 2)) + logline(&client, "<<", reply); + if ((*reply == '=' || *reply == '?') && isdigit(reply[1])) + reply_id = atoi(reply+1); + + /* Read the rest of the ascii reply */ + char *line = reply + strlen(reply); + while (fgets(line, reply + CMDS_SIZE - line, f) && *line != '\n') { + if (DEBUGL(3)) + logline(&client, "<<", line); + line += strlen(line); + } + if (*line != '\n') return -1; + + /* Read the binary reply if any. */ + int len; + while (size && (len = fread(bin_reply, 1, size, f)) > 0) { + bin_reply = (char *)bin_reply + len; + size -= len; + } + if (*bin_size && DEBUGVV(2)) { + char buf[1024]; + snprintf(buf, sizeof(buf), "read reply %d+%d bytes in %.4fms\n", + (int)strlen(reply), *bin_size, + (time_now() - start)*1000); + logline(&client, "= ", buf); + } + return size ? -1 : reply_id; +} + +/* Send the gtp command to_send and get a reply from the slave machine. + * Write the reply in buf which must have at least CMDS_SIZE bytes. + * If *bin_size > 0, send bin_buf after the gtp command. + * Return any binary reply in bin_buf and set its size in bin_size. + * bin_buf is private to the slave and need not be copied. + * Return the gtp command id, or -1 if error. + * slave_lock is held on both entry and exit of this function. */ +static int +send_command(char *to_send, void *bin_buf, int *bin_size, + FILE *f, struct slave_state *sstate, char *buf) +{ + assert(to_send && gtp_cmd && bin_buf && bin_size); + strncpy(buf, to_send, CMDS_SIZE); + bool resend = to_send != gtp_cmd; + + pthread_mutex_unlock(&slave_lock); + + if (DEBUGL(1) && resend) + logline(&sstate->client, "? ", + to_send == gtp_cmds ? "resend all\n" : "partial resend\n"); + + double start = time_now(); + fputs(buf, f); + + if (*bin_size) + fwrite(bin_buf, 1, *bin_size, f); + fflush(f); + + if (DEBUGV(strchr(buf, '@'), 2)) { + double ms = (time_now() - start) * 1000.0; + if (!DEBUGL(3)) { + char *s = strchr(buf, '\n'); + if (s) s[1] = '\0'; + } + logline(&sstate->client, ">>", buf); + if (*bin_size) { + char b[1024]; + snprintf(b, sizeof(b), + "sent cmd %d+%d bytes in %.4fms\n", + (int)strlen(buf), *bin_size, ms); + logline(&sstate->client, "= ", b); + } + } + + /* Reuse the buffers for the reply. */ + *bin_size = sstate->max_buf_size; + int reply_id = get_reply(f, sstate->client, buf, bin_buf, bin_size); + + pthread_mutex_lock(&slave_lock); + return reply_id; +} + +/* Return the command sent after that with the given gtp id, + * or gtp_cmds if the id wasn't used in this game. If a play command + * has overwritten a genmoves command, return the play command. + * slave_lock is held on both entry and exit of this function. */ +static char * +next_command(int cmd_id) +{ + if (cmd_id == -1) return gtp_cmds; + + int last_id = atoi(gtp_cmd); + int reply_move = move_number(cmd_id); + if (reply_move > move_number(last_id)) return gtp_cmds; + + int slot; + for (slot = 0; slot < MAX_CMDS_PER_MOVE; slot++) { + if (cmd_id == history[reply_move][slot].gtp_id) break; + } + if (slot == MAX_CMDS_PER_MOVE) return gtp_cmds; + + char *next = history[reply_move][slot].next_cmd; + assert(next); + return next; +} + +/* Allocate buffers for a slave thread. The state should have been + * initialized already as a copy of the default slave state. + * slave_lock is not held on either entry or exit of this function. */ +static void +slave_state_alloc(struct slave_state *sstate) +{ + for (int n = 0; n < BUFFERS_PER_SLAVE; n++) { + sstate->b[n].buf = malloc2(sstate->max_buf_size); + sstate->b[n].owner = sstate->thread_id; + } + if (sstate->alloc_hook) sstate->alloc_hook(sstate); +} + +/* Get a free binary buffer, first invalidating it in the receive + * queue if necessary. In practice all buffers should be used + * before they are invalidated, if BUFFERS_PER_SLAVE is large enough. + * slave_lock is held on both entry and exit of this function. */ +static void * +get_free_buf(struct slave_state *sstate) +{ + int newest = (sstate->newest_buf + 1) & (BUFFERS_PER_SLAVE - 1); + sstate->newest_buf = newest; + void *buf = sstate->b[newest].buf; + + if (DEBUGVV(7)) { + char b[1024]; + snprintf(b, sizeof(b), + "get free %d index %d buf=%p age %d qlength %d\n", newest, + sstate->b[newest].queue_index, buf, queue_age, queue_length); + logline(&sstate->client, "? ", b); + } + + int index = sstate->b[newest].queue_index; + if (index < 0) return buf; + + /* Invalidate the buffer if the calling thread still owns its previous + * entry in the receive queue. The entry may have been overwritten by + * another thread, but only after a new move which invalidates the + * entire receive queue. */ + if (receive_queue[index] && receive_queue[index]->owner == sstate->thread_id) { + receive_queue[index] = NULL; + } + sstate->b[newest].queue_index = -1; + return buf; +} + +/* Insert a buffer in the receive queue. It should be the most + * recent buffer allocated by the calling thread. + * slave_lock is held on both entry and exit of this function. */ +static void +insert_buf(struct slave_state *sstate, void *buf, int size) +{ + assert(queue_length < queue_max_length); + + int newest = sstate->newest_buf; + assert(buf == sstate->b[newest].buf); + + /* Update the buffer if necessary before making it + * available to other threads. */ + if (sstate->insert_hook) sstate->insert_hook(buf, size); + + if (DEBUGVV(7)) { + char b[1024]; + snprintf(b, sizeof(b), + "insert newest %d age %d rq[%d]->%p owner %d\n", + newest, queue_age, queue_length, buf, sstate->thread_id); + logline(&sstate->client, "? ", b); + } + receive_queue[queue_length] = &sstate->b[newest]; + receive_queue[queue_length]->size = size; + receive_queue[queue_length]->queue_index = queue_length; + queue_length++; +} + +/* Clear the receive queue. The buffer pointers do not have to be cleared + * here, this is done as each buffer is recycled. + * slave_lock is held on both entry and exit of this function. */ +void +clear_receive_queue(void) +{ + if (DEBUGL(3)) { + char buf[1024]; + snprintf(buf, sizeof(buf), "clear queue, old length %d age %d\n", + queue_length, queue_age); + logline(NULL, "? ", buf); + } + queue_length = 0; + queue_age++; +} + +/* Process the reply received from a slave machine. + * Copy the ascii part to reply_buf and insert the binary part + * (if any) in the receive queue. + * Return false if ok, true if the slave is out of sync. + * slave_lock is held on both entry and exit of this function. */ +static bool +process_reply(int reply_id, char *reply, char *reply_buf, + void *bin_reply, int bin_size, int *last_reply_id, + int *reply_slot, struct slave_state *sstate) +{ + /* Resend everything if slave returned an error. */ + if (*reply != '=') { + *last_reply_id = -1; + return true; + } + /* Make sure we are still in sync. cmd_count may have + * changed but the reply is valid as long as cmd_id didn't + * change (this only occurs for consecutive genmoves). */ + int cmd_id = atoi(gtp_cmd); + if (reply_id != cmd_id) { + *last_reply_id = reply_id; + return true; + } + + strncpy(reply_buf, reply, CMDS_SIZE); + if (reply_id != *last_reply_id) + *reply_slot = reply_count++; + gtp_replies[*reply_slot] = reply_buf; + + if (bin_size) insert_buf(sstate, bin_reply, bin_size); + + pthread_cond_signal(&reply_cond); + *last_reply_id = reply_id; + return false; +} + +/* Get the binary arg for the given command, and update the command + * if necessary. For now, only genmoves has a binary argument, and + * we return the best stats increments from all other slaves. + * Set *bin_size to 0 if the command doesn't take binary arguments, + * but still return a buffer, to be used for the reply. + * Return NULL if the binary arg is obsolete by the time we have + * finished computing it, because a new command is available. + * This version only gets the buffer for the reply, to be completed + * in future commits. + * slave_lock is held on both entry and exit of this function. */ +void * +get_binary_arg(struct slave_state *sstate, char *cmd, int cmd_size, int *bin_size) +{ + int cmd_id = atoi(gtp_cmd); + void *buf = get_free_buf(sstate); + + *bin_size = 0; + char *s = strchr(cmd, '@'); + if (!s || !sstate->args_hook) return buf; + + int size = sstate->args_hook(buf, sstate, cmd_id); + + /* Check that the command is still valid. */ + if (atoi(gtp_cmd) != cmd_id) return NULL; + + /* Set the correct binary size for this slave. + * cmd may have been overwritten with new parameters. */ + *bin_size = size; + s = strchr(cmd, '@'); + assert(s); + snprintf(s, cmd + cmd_size - s, "@%d\n", size); + return buf; +} + +/* Main loop of a slave thread. + * Send the current command to the slave machine and wait for a reply. + * Resend command history if the slave machine is out of sync. + * Returns when the connection with the slave machine is cut. + * slave_lock is held on both entry and exit of this function. */ +static void +slave_loop(FILE *f, char *reply_buf, struct slave_state *sstate, bool resend) +{ + char *to_send; + int last_cmd_count = 0; + int last_reply_id = -1; + int reply_slot = -1; + for (;;) { + if (resend) { + /* Resend complete or partial history */ + to_send = next_command(last_reply_id); + } else { + /* Wait for a new command. */ + while (last_cmd_count == cmd_count) + pthread_cond_wait(&cmd_cond, &slave_lock); + to_send = gtp_cmd; + } + + /* Command available, send it to slave machine. + * If slave was out of sync, send the history. + * But first get binary arguments if necessary. */ + int bin_size = 0; + void *bin_buf = get_binary_arg(sstate, gtp_cmd, + gtp_cmds + CMDS_SIZE - gtp_cmd, + &bin_size); + /* Check that the command is still valid. */ + resend = true; + if (!bin_buf) continue; + + /* Send the command and get the reply, which always ends with \n\n + * The slave machine sends "=id reply" or "?id reply" + * with id == cmd_id if it is in sync. */ + last_cmd_count = cmd_count; + char buf[CMDS_SIZE]; + int reply_id = send_command(to_send, bin_buf, &bin_size, f, + sstate, buf); + if (reply_id == -1) return; + + resend = process_reply(reply_id, buf, reply_buf, bin_buf, bin_size, + &last_reply_id, &reply_slot, sstate); + } +} + +/* Minimimal check of slave identity. Close the file if error. */ +static bool +is_pachi_slave(FILE *f, struct in_addr *client) +{ + char buf[1024]; + fputs("name\n", f); + if (!fgets(buf, sizeof(buf), f) + || strncasecmp(buf, "= Pachi", 7) + || !fgets(buf, sizeof(buf), f) + || strcmp(buf, "\n")) { + logline(client, "? ", "bad slave\n"); + fclose(f); + sleep(1); // avoid busy loop if error + return false; + } + return true; +} + +/* Thread sending gtp commands to one slave machine, and + * reading replies. If a slave machine dies, this thread waits + * for a connection from another slave. + * The large buffers are allocated only once we get a first + * connection, to avoid wasting memory if max_slaves is too large. + * We do not invalidate the received buffers if a slave disconnects; + * they are still useful for other slaves. */ +static void * +slave_thread(void *arg) +{ + struct slave_state sstate = default_sstate; + sstate.thread_id = (long)arg; + + assert(sstate.slave_sock >= 0); + char reply_buf[CMDS_SIZE]; + bool resend = false; + + for (;;) { + /* Wait for a connection from any slave. */ + struct in_addr client; + int conn = open_server_connection(sstate.slave_sock, &client); + + FILE *f = fdopen(conn, "r+"); + if (DEBUGL(2)) { + snprintf(reply_buf, sizeof(reply_buf), + "new slave, id %d\n", sstate.thread_id); + logline(&client, "= ", reply_buf); + } + if (!is_pachi_slave(f, &client)) continue; + + if (!resend) slave_state_alloc(&sstate); + sstate.client = client; + + pthread_mutex_lock(&slave_lock); + active_slaves++; + slave_loop(f, reply_buf, &sstate, resend); + + assert(active_slaves > 0); + active_slaves--; + // Unblock main thread if it was waiting for this slave. + pthread_cond_signal(&reply_cond); + pthread_mutex_unlock(&slave_lock); + + resend = true; + if (DEBUGL(2)) + logline(&client, "= ", "lost slave\n"); + fclose(f); + } +} + +/* Create a new gtp command for all slaves. The slave lock is held + * upon entry and upon return, so the command will actually be + * sent when the lock is released. The last command is overwritten + * if gtp_cmd points to a non-empty string. cmd is a single word; + * args has all arguments and is empty or has a trailing \n */ +void +update_cmd(struct board *b, char *cmd, char *args, bool new_id) +{ + assert(gtp_cmd); + /* To make sure the slaves are in sync, we ignore the original id + * and use the board number plus some random bits as gtp id. */ + static int gtp_id = -1; + int moves = is_reset(cmd) ? 0 : b->moves; + if (new_id) { + int prev_id = gtp_id; + do { + /* fast_random() is 16-bit only so the multiplication can't overflow. */ + gtp_id = force_reply(moves + fast_random(65535) * DIST_GAMELEN); + } while (gtp_id == prev_id); + reply_count = 0; + } + snprintf(gtp_cmd, gtp_cmds + CMDS_SIZE - gtp_cmd, "%d %s %s", + gtp_id, cmd, *args ? args : "\n"); + cmd_count++; + + /* Remember history for out-of-sync slaves. */ + static int slot = 0; + static struct cmd_history *last = NULL; + if (new_id) { + if (last) last->next_cmd = gtp_cmd; + slot = (slot + 1) % MAX_CMDS_PER_MOVE; + last = &history[moves][slot]; + last->gtp_id = gtp_id; + last->next_cmd = NULL; + } + // Notify the slave threads about the new command. + pthread_cond_broadcast(&cmd_cond); +} + +/* Update the command history, then create a new gtp command + * for all slaves. The slave lock is held upon entry and + * upon return, so the command will actually be sent when the + * lock is released. cmd is a single word; args has all + * arguments and is empty or has a trailing \n */ +void +new_cmd(struct board *b, char *cmd, char *args) +{ + // Clear the history when a new game starts: + if (!gtp_cmd || is_gamestart(cmd)) { + gtp_cmd = gtp_cmds; + memset(history, 0, sizeof(history)); + } else { + /* Preserve command history for new slaves. + * To indicate that the slave should only reply to + * the last command we force the id of previous + * commands to be just the move number. */ + int id = prevent_reply(atoi(gtp_cmd)); + int len = strspn(gtp_cmd, "0123456789"); + char buf[32]; + snprintf(buf, sizeof(buf), "%0*d", len, id); + memcpy(gtp_cmd, buf, len); + + gtp_cmd += strlen(gtp_cmd); + } + + // Let the slave threads send the new gtp command: + update_cmd(b, cmd, args, true); +} + +/* Wait for at least one new reply. Return when at least + * min_replies slaves have already replied, or when the + * given absolute time is passed. + * The replies are returned in gtp_replies[0..reply_count-1] + * slave_lock is held on entry and on return. */ +void +get_replies(double time_limit, int min_replies) +{ + for (;;) { + if (reply_count > 0) { + struct timespec ts; + double sec; + ts.tv_nsec = (int)(modf(time_limit, &sec)*1000000000.0); + ts.tv_sec = (int)sec; + pthread_cond_timedwait(&reply_cond, &slave_lock, &ts); + } else { + pthread_cond_wait(&reply_cond, &slave_lock); + } + if (reply_count == 0) continue; + if (reply_count >= min_replies || reply_count >= active_slaves) return; + if (time_now() >= time_limit) break; + } + if (DEBUGL(1)) { + char buf[1024]; + snprintf(buf, sizeof(buf), + "get_replies timeout %.3f >= %.3f, replies %d < min %d, active %d\n", + time_now() - start_time, time_limit - start_time, + reply_count, min_replies, active_slaves); + logline(NULL, "? ", buf); + } + assert(reply_count > 0); +} + +/* In a 5mn move with at least 5ms per genmoves we get at most + * 300*200=60000 genmoves per slave. */ +#define MAX_GENMOVES_PER_SLAVE 60000 + +/* Allocate the receive queue, and create the slave and proxy threads. + * max_buf_size and the merge-related fields of default_sstate must + * already be initialized. */ +void +protocol_init(char *slave_port, char *proxy_port, int max_slaves) +{ + start_time = time_now(); + + queue_max_length = max_slaves * MAX_GENMOVES_PER_SLAVE; + receive_queue = calloc2(queue_max_length, sizeof(*receive_queue)); + + default_sstate.slave_sock = port_listen(slave_port, max_slaves); + default_sstate.last_processed = -1; + + for (int n = 0; n < BUFFERS_PER_SLAVE; n++) { + default_sstate.b[n].queue_index = -1; + } + + pthread_t thread; + for (int id = 0; id < max_slaves; id++) { + pthread_create(&thread, NULL, slave_thread, (void *)(long)id); + } + + if (proxy_port) { + int proxy_sock = port_listen(proxy_port, max_slaves); + for (int id = 0; id < max_slaves; id++) { + pthread_create(&thread, NULL, proxy_thread, (void *)(long)proxy_sock); + } + } +} diff --git a/jni/pachi/distributed/protocol.h b/jni/pachi/distributed/protocol.h new file mode 100644 index 0000000..d0188d1 --- /dev/null +++ b/jni/pachi/distributed/protocol.h @@ -0,0 +1,98 @@ +#ifndef PACHI_DISTRIBUTED_PROTOCOL_H +#define PACHI_DISTRIBUTED_PROTOCOL_H + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +#include "board.h" + + +/* Each slave thread maintains a ring of 256 buffers holding + * incremental stats received from the slave. The oldest + * buffer is recycled to hold stats sent to the slave and + * received the next reply. */ +#define BUFFERS_PER_SLAVE_BITS 8 +#define BUFFERS_PER_SLAVE (1 << BUFFERS_PER_SLAVE_BITS) + +struct slave_state; +typedef void (*buffer_hook)(void *buf, int size); +typedef void (*state_alloc_hook)(struct slave_state *sstate); +typedef int (*getargs_hook)(void *buf, struct slave_state *sstate, int cmd_id); + +struct buf_state { + void *buf; + /* All buffers have the same physical size. size is the + * number of valid bytes. It is set only when the buffer + * is actually in the receive queueue. */ + int size; + int queue_index; + int owner; +}; + +struct slave_state { + int max_buf_size; + int thread_id; + struct in_addr client; // for debugging only + state_alloc_hook alloc_hook; + buffer_hook insert_hook; + getargs_hook args_hook; + + /* Index in received_queue of most recent processed + * buffer, -1 if none processed yet. */ + int last_processed; + + /* --- PRIVATE DATA for protocol.c --- */ + + struct buf_state b[BUFFERS_PER_SLAVE]; + int newest_buf; + int slave_sock; + + /* --- PRIVATE DATA for merge.c --- */ + + /* Hash table of incremental stats. */ + struct incr_stats *stats_htable; + int stats_hbits; + int stats_id; + + /* Hash indices updated by stats merge. */ + int *merged; + int max_merged_nodes; +}; +extern struct slave_state default_sstate; + +void protocol_lock(void); +void protocol_unlock(void); + +void logline(struct in_addr *client, char *prefix, char *s); + +void clear_receive_queue(void); +void update_cmd(struct board *b, char *cmd, char *args, bool new_id); +void new_cmd(struct board *b, char *cmd, char *args); +void get_replies(double time_limit, int min_replies); +void protocol_init(char *slave_port, char *proxy_port, int max_slaves); + +extern int reply_count; +extern char **gtp_replies; +extern int active_slaves; + +/* All binary buffers received from all slaves in current move are in + * receive_queue[0..queue_length-1] */ +extern struct buf_state **receive_queue; +extern int queue_length; +/* Queue age is incremented each time the queue is emptied. */ +extern int queue_age; + +/* Max size of all gtp commands for one game. + * 60 chars for the first line of genmoves plus 100 lines + * of 30 chars each for the stats at last move. */ +#define CMDS_SIZE (60*MAX_GAMELEN + 30*100) + +/* Max size for one line of reply or slave log. */ +#define BSIZE 4096 + +#endif diff --git a/jni/pachi/engine.h b/jni/pachi/engine.h new file mode 100644 index 0000000..ab712bb --- /dev/null +++ b/jni/pachi/engine.h @@ -0,0 +1,53 @@ +#ifndef PACHI_ENGINE_H +#define PACHI_ENGINE_H + +#include "board.h" +#include "move.h" +#include "gtp.h" + +struct move_queue; + +typedef enum parse_code (*engine_notify)(struct engine *e, struct board *b, int id, char *cmd, char *args, char **reply); +typedef char *(*engine_notify_play)(struct engine *e, struct board *b, struct move *m, char *enginearg); +typedef char *(*engine_undo)(struct engine *e, struct board *b); +typedef char *(*engine_result)(struct engine *e, struct board *b); +typedef char *(*engine_chat)(struct engine *e, struct board *b, bool in_game, char *from, char *cmd); +/* Generate a move. If pass_all_alive is true, shall be generated only + * if all stones on the board can be considered alive, without regard to "dead" + * considered stones. */ +typedef coord_t *(*engine_genmove)(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive); +typedef char *(*engine_genmoves)(struct engine *e, struct board *b, struct time_info *ti, enum stone color, + char *args, bool pass_all_alive, void **stats_buf, int *stats_size); +/* Evaluate feasibility of player @color playing at all free moves. Will + * simulate each move from b->f[i] for time @ti, then set + * 1-max(opponent_win_likelihood) in vals[i]. */ +typedef void (*engine_evaluate)(struct engine *e, struct board *b, struct time_info *ti, floating_t *vals, enum stone color); +/* One dead group per queued move (coord_t is (ab)used as group_t). */ +typedef void (*engine_dead_group_list)(struct engine *e, struct board *b, struct move_queue *mq); +/* e->data and e will be free()d by caller afterwards. */ +typedef void (*engine_done)(struct engine *e); + +/* This is engine data structure. A new engine instance is spawned + * for each new game during the program lifetime. */ +struct engine { + char *name; + char *comment; + + /* If set, do not reset the engine state on clear_board. */ + bool keep_on_clear; + + engine_notify notify; + board_cprint printhook; + engine_notify_play notify_play; + engine_chat chat; + engine_undo undo; + engine_result result; + engine_genmove genmove; + engine_genmoves genmoves; + engine_evaluate evaluate; + engine_dead_group_list dead_group_list; + engine_done done; + void *data; +}; + +#endif diff --git a/jni/pachi/fbook.c b/jni/pachi/fbook.c new file mode 100644 index 0000000..4041ff4 --- /dev/null +++ b/jni/pachi/fbook.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#define DEBUG + +#include "board.h" +#include "debug.h" +#include "fbook.h" +#include "random.h" + + +static coord_t +coord_transform(struct board *b, coord_t coord, int i) +{ +#define HASH_VMIRROR 1 +#define HASH_HMIRROR 2 +#define HASH_XYFLIP 4 + if (i & HASH_VMIRROR) { + coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b)); + } + if (i & HASH_HMIRROR) { + coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b)); + } + if (i & HASH_XYFLIP) { + coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b)); + } + return coord; +} + +/* Check if we can make a move along the fbook right away. + * Otherwise return pass. */ +coord_t +fbook_check(struct board *board) +{ + if (!board->fbook) return pass; + + hash_t hi = board->hash; + coord_t cf = pass; + while (!is_pass(board->fbook->moves[hi & fbook_hash_mask])) { + if (board->fbook->hashes[hi & fbook_hash_mask] == board->hash) { + cf = board->fbook->moves[hi & fbook_hash_mask]; + break; + } + hi++; + } + if (!is_pass(cf)) { + if (DEBUGL(1)) + fprintf(stderr, "fbook match %"PRIhash":%"PRIhash"\n", board->hash, board->hash & fbook_hash_mask); + } else { + /* No match, also prevent further fbook usage + * until the next clear_board. */ + if (DEBUGL(4)) + fprintf(stderr, "fbook out %"PRIhash":%"PRIhash"\n", board->hash, board->hash & fbook_hash_mask); + fbook_done(board->fbook); + board->fbook = NULL; + } + return cf; +} + +static struct fbook *fbcache; + +struct fbook * +fbook_init(char *filename, struct board *b) +{ + if (fbcache && fbcache->bsize == board_size(b) + && fbcache->handicap == b->handicap) + return fbcache; + + FILE *f = fopen(filename, "r"); + if (!f) { + perror(filename); + return NULL; + } + + struct fbook *fbook = calloc(1, sizeof(*fbook)); + fbook->bsize = board_size(b); + fbook->handicap = b->handicap; + /* We do not set handicap=1 in case of too low komi on purpose; + * we want to go with the no-handicap fbook for now. */ + for (int i = 0; i < 1<moves[i] = pass; + + if (DEBUGL(1)) + fprintf(stderr, "Loading opening fbook %s...\n", filename); + + /* Scratch board where we lay out the sequence; + * one for each transposition. */ + struct board *bs[8]; + for (int i = 0; i < 8; i++) { + bs[i] = board_init(NULL); + board_resize(bs[i], fbook->bsize - 2); + } + + char linebuf[1024]; + while (fgets(linebuf, sizeof(linebuf), f)) { + char *line = linebuf; + linebuf[strlen(linebuf) - 1] = 0; // chop + + /* Format of line is: + * BSIZE COORD COORD COORD... | COORD + * BSIZE/HANDI COORD COORD COORD... | COORD */ + int bsize = strtol(line, &line, 10); + if (bsize != fbook->bsize - 2) + continue; + int handi = 0; + if (*line == '/') { + line++; + handi = strtol(line, &line, 10); + } + if (handi != fbook->handicap) + continue; + while (isspace(*line)) line++; + + for (int i = 0; i < 8; i++) { + board_clear(bs[i]); + bs[i]->last_move.color = S_WHITE; + } + + while (*line != '|') { + coord_t *c = str2coord(line, fbook->bsize); + + for (int i = 0; i < 8; i++) { + coord_t coord = coord_transform(b, *c, i); + struct move m = { .coord = coord, .color = stone_other(bs[i]->last_move.color) }; + int ret = board_play(bs[i], &m); + assert(ret >= 0); + } + + coord_done(c); + while (!isspace(*line)) line++; + while (isspace(*line)) line++; + } + + line++; + while (isspace(*line)) line++; + + /* In case of multiple candidates, pick one with + * exponentially decreasing likelihood. */ + while (strchr(line, ' ') && fast_random(2)) { + line = strchr(line, ' '); + while (isspace(*line)) line++; + // fprintf(stderr, "<%s> skip to %s\n", linebuf, line); + } + + coord_t *c = str2coord(line, fbook->bsize); + for (int i = 0; i < 8; i++) { + coord_t coord = coord_transform(b, *c, i); +#if 0 + char conflict = is_pass(fbook->moves[bs[i]->hash & fbook_hash_mask]) ? '+' : 'C'; + if (conflict == 'C') + for (int j = 0; j < i; j++) + if (bs[i]->hash == bs[j]->hash) + conflict = '+'; + if (conflict == 'C') { + hash_t hi = bs[i]->hash; + while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash) + hi++; + if (fbook->hashes[hi & fbook_hash_mask] == bs[i]->hash) + hi = 'c'; + } + fprintf(stderr, "%c %"PRIhash":%"PRIhash" (<%d> %s)\n", conflict, + bs[i]->hash & fbook_hash_mask, bs[i]->hash, i, linebuf); +#endif + hash_t hi = bs[i]->hash; + while (!is_pass(fbook->moves[hi & fbook_hash_mask]) && fbook->hashes[hi & fbook_hash_mask] != bs[i]->hash) + hi++; + fbook->moves[hi & fbook_hash_mask] = coord; + fbook->hashes[hi & fbook_hash_mask] = bs[i]->hash; + fbook->movecnt++; + } + coord_done(c); + } + + for (int i = 0; i < 8; i++) { + board_done(bs[i]); + } + + fclose(f); + + if (!fbook->movecnt) { + /* Empty book is not worth the hassle. */ + fbook_done(fbook); + return NULL; + } + + struct fbook *fbold = fbcache; + fbcache = fbook; + if (fbold) + fbook_done(fbold); + + return fbook; +} + +void fbook_done(struct fbook *fbook) +{ + if (fbook != fbcache) + free(fbook); +} diff --git a/jni/pachi/fbook.h b/jni/pachi/fbook.h new file mode 100644 index 0000000..9e3db4d --- /dev/null +++ b/jni/pachi/fbook.h @@ -0,0 +1,28 @@ +#ifndef PACHI_FBOOK_H +#define PACHI_FBOOK_H + +#include "move.h" + +struct board; + +/* Opening book (fbook as in "forcing book" since the move is just + * played unconditionally if found, or possibly "fuseki book"). */ + +struct fbook { + int bsize; + int handicap; + + int movecnt; + +#define fbook_hash_bits 20 // 12M w/ 32-bit coord_t +#define fbook_hash_mask ((1 << fbook_hash_bits) - 1) + /* pass == no move in this position */ + coord_t moves[1< + +typedef uint_fast32_t fixp_t; + +/* We should accomodate at least 0..131072 (17bits) in the whole number + * portion; assuming at least 32bit integer, that leaves us with 15-bit + * fractional part. Thankfully, we need only unsigned values. */ +#define FIXP_BITS 15 + +#define FIXP_SCALE (1< +#include +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "fbook.h" +#include "gtp.h" +#include "mq.h" +#include "uct/uct.h" +#include "version.h" +#include "timeinfo.h" + +#define NO_REPLY (-2) + +/* Sleep 5 seconds after a game ends to give time to kill the program. */ +#define GAME_OVER_SLEEP 5 + +void +gtp_prefix(char prefix, int id) +{ + if (id == NO_REPLY) return; + if (id >= 0) + printf("%c%d ", prefix, id); + else + printf("%c ", prefix); +} + +void +gtp_flush(void) +{ + putchar('\n'); + fflush(stdout); +} + +void +gtp_output(char prefix, int id, va_list params) +{ + if (id == NO_REPLY) return; + gtp_prefix(prefix, id); + char *s; + while ((s = va_arg(params, char *))) { + fputs(s, stdout); + } + putchar('\n'); + gtp_flush(); +} + +void +gtp_reply(int id, ...) +{ + va_list params; + va_start(params, id); + gtp_output('=', id, params); + va_end(params); +} + +void +gtp_error(int id, ...) +{ + va_list params; + va_start(params, id); + gtp_output('?', id, params); + va_end(params); +} + +/* List of known gtp commands. The internal command pachi-genmoves is not exported, + * it should only be used between master and slaves of the distributed engine. */ +static char *known_commands = + "protocol_version\n" + "echo\n" + "name\n" + "version\n" + "list_commands\n" + "known_command\n" + "quit\n" + "boardsize\n" + "clear_board\n" + "kgs-game_over\n" + "komi\n" + "kgs-rules\n" + "play\n" + "genmove\n" + "kgs-genmove_cleanup\n" + "set_free_handicap\n" + "place_free_handicap\n" + "fixed_handicap\n" + "final_score\n" + "final_status_list\n" + "undo\n" + "pachi-evaluate\n" + "pachi-result\n" + "pachi-gentbook\n" + "pachi-dumptbook\n" + "kgs-chat\n" + "time_left\n" + "time_settings\n" + "kgs-time_settings"; + + +/* Return true if cmd is a valid gtp command. */ +bool +gtp_is_valid(const char *cmd) +{ + if (!cmd || !*cmd) return false; + const char *s = strcasestr(known_commands, cmd); + if (!s) return false; + if (s != known_commands && s[-1] != '\n') return false; + + int len = strlen(cmd); + return s[len] == '\0' || s[len] == '\n'; +} + +/* XXX: THIS IS TOTALLY INSECURE!!!! + * Even basic input checking is missing. */ + +enum parse_code +gtp_parse(struct board *board, struct engine *engine, struct time_info *ti, char *buf) +{ +#define next_tok(to_) \ + to_ = next; \ + next = next + strcspn(next, " \t\r\n"); \ + if (*next) { \ + *next = 0; next++; \ + next += strspn(next, " \t\r\n"); \ + } + + if (strchr(buf, '#')) + *strchr(buf, '#') = 0; + + char *cmd, *next = buf; + next_tok(cmd); + + int id = -1; + if (isdigit(*cmd)) { + id = atoi(cmd); + next_tok(cmd); + } + + if (!*cmd) + return P_OK; + + if (!strcasecmp(cmd, "protocol_version")) { + gtp_reply(id, "2", NULL); + return P_OK; + + } else if (!strcasecmp(cmd, "name")) { + /* KGS hack */ + gtp_reply(id, "Pachi ", engine->name, NULL); + return P_OK; + + } else if (!strcasecmp(cmd, "echo")) { + gtp_reply(id, next, NULL); + return P_OK; + + } else if (!strcasecmp(cmd, "version")) { + gtp_reply(id, PACHI_VERSION, ": ", engine->comment, " Have a nice game!", NULL); + return P_OK; + + } else if (!strcasecmp(cmd, "list_commands")) { + gtp_reply(id, known_commands, NULL); + return P_OK; + + } else if (!strcasecmp(cmd, "known_command")) { + char *arg; + next_tok(arg); + if (gtp_is_valid(arg)) { + gtp_reply(id, "true", NULL); + } else { + gtp_reply(id, "false", NULL); + } + return P_OK; + } + + if (engine->notify && gtp_is_valid(cmd)) { + char *reply; + enum parse_code c = engine->notify(engine, board, id, cmd, next, &reply); + if (c == P_NOREPLY) { + id = NO_REPLY; + } else if (c == P_DONE_OK) { + gtp_reply(id, reply, NULL); + return P_OK; + } else if (c == P_DONE_ERROR) { + gtp_error(id, reply, NULL); + /* This is an internal error for the engine, but + * it is still OK from main's point of view. */ + return P_OK; + } else if (c != P_OK) { + return c; + } + } + + if (!strcasecmp(cmd, "quit")) { + gtp_reply(id, NULL); + exit(0); + + } else if (!strcasecmp(cmd, "boardsize")) { + char *arg; + next_tok(arg); + int size = atoi(arg); + if (size < 1 || size > BOARD_MAX_SIZE) { + gtp_error(id, "illegal board size", NULL); + return P_OK; + } + board_resize(board, size); + board_clear(board); + gtp_reply(id, NULL); + return P_ENGINE_RESET; + + } else if (!strcasecmp(cmd, "clear_board")) { + board_clear(board); + if (DEBUGL(3) && debug_boardprint) + board_print(board, stderr); + gtp_reply(id, NULL); + return P_ENGINE_RESET; + + } else if (!strcasecmp(cmd, "kgs-game_over")) { + /* The game may not be really over, just adjourned. + * Do not clear the board to avoid illegal moves + * if the game is resumed immediately after. KGS + * may start directly with genmove on resumption. */ + if (DEBUGL(1)) { + fprintf(stderr, "game is over\n"); + fflush(stderr); + } + /* Sleep before replying, so that kgs doesn't + * start another game immediately. */ + sleep(GAME_OVER_SLEEP); + gtp_reply(id, NULL); + + } else if (!strcasecmp(cmd, "komi")) { + char *arg; + next_tok(arg); + sscanf(arg, PRIfloating, &board->komi); + + if (DEBUGL(3) && debug_boardprint) + board_print(board, stderr); + gtp_reply(id, NULL); + + } else if (!strcasecmp(cmd, "kgs-rules")) { + char *arg; + next_tok(arg); + if (!board_set_rules(board, arg)) { + gtp_error(id, "unknown rules", NULL); + return P_OK; + } + gtp_reply(id, NULL); + + } else if (!strcasecmp(cmd, "play")) { + struct move m; + + char *arg; + next_tok(arg); + m.color = str2stone(arg); + next_tok(arg); + coord_t *c = str2coord(arg, board_size(board)); + m.coord = *c; coord_done(c); + next_tok(arg); + char *enginearg = arg; + char *reply = NULL; + + if (DEBUGL(5)) + fprintf(stderr, "got move %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board)); + + // This is where kgs starts the timer, not at genmove! + time_start_timer(&ti[stone_other(m.color)]); + + if (engine->notify_play) + reply = engine->notify_play(engine, board, &m, enginearg); + if (board_play(board, &m) < 0) { + if (DEBUGL(0)) { + fprintf(stderr, "! ILLEGAL MOVE %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board)); + board_print(board, stderr); + } + gtp_error(id, "illegal move", NULL); + } else { + if (DEBUGL(4) && debug_boardprint) + board_print_custom(board, stderr, engine->printhook); + gtp_reply(id, reply, NULL); + } + + } else if (!strcasecmp(cmd, "genmove") || !strcasecmp(cmd, "kgs-genmove_cleanup")) { + char *arg; + next_tok(arg); + enum stone color = str2stone(arg); + coord_t *c = NULL; + if (DEBUGL(2) && debug_boardprint) + board_print_custom(board, stderr, engine->printhook); + + if (!ti[color].len.t.timer_start) { + /* First game move. */ + time_start_timer(&ti[color]); + } + + coord_t cf = pass; + if (board->fbook) + cf = fbook_check(board); + if (!is_pass(cf)) { + c = coord_copy(cf); + } else { + c = engine->genmove(engine, board, &ti[color], color, !strcasecmp(cmd, "kgs-genmove_cleanup")); + } + struct move m = { *c, color }; + if (board_play(board, &m) < 0) { + fprintf(stderr, "Attempted to generate an illegal move: [%s, %s]\n", coord2sstr(m.coord, board), stone2str(m.color)); + abort(); + } + char *str = coord2str(*c, board); + if (DEBUGL(4)) + fprintf(stderr, "playing move %s\n", str); + if (DEBUGL(1) && debug_boardprint) { + board_print_custom(board, stderr, engine->printhook); + } + gtp_reply(id, str, NULL); + free(str); coord_done(c); + + /* Account for spent time. If our GTP peer keeps our clock, this will + * be overriden by next time_left GTP command properly. */ + /* (XXX: Except if we pass to byoyomi and the peer doesn't, but that + * should be absolutely rare situation and we will just spend a little + * less time than we could on next few moves.) */ + if (ti[color].period != TT_NULL && ti[color].dim == TD_WALLTIME) + time_sub(&ti[color], time_now() - ti[color].len.t.timer_start, true); + + } else if (!strcasecmp(cmd, "pachi-genmoves") || !strcasecmp(cmd, "pachi-genmoves_cleanup")) { + char *arg; + next_tok(arg); + enum stone color = str2stone(arg); + void *stats; + int stats_size; + + char *reply = engine->genmoves(engine, board, &ti[color], color, next, + !strcasecmp(cmd, "pachi-genmoves_cleanup"), + &stats, &stats_size); + if (!reply) { + gtp_error(id, "genmoves error", NULL); + return P_OK; + } + if (DEBUGL(3)) + fprintf(stderr, "proposing moves %s\n", reply); + if (DEBUGL(4) && debug_boardprint) + board_print_custom(board, stderr, engine->printhook); + gtp_reply(id, reply, NULL); + if (stats_size > 0) { + double start = time_now(); + fwrite(stats, 1, stats_size, stdout); + fflush(stdout); + if (DEBUGVV(2)) + fprintf(stderr, "sent reply %d bytes in %.4fms\n", + stats_size, (time_now() - start)*1000); + } + + } else if (!strcasecmp(cmd, "set_free_handicap")) { + struct move m; + m.color = S_BLACK; + + char *arg; + next_tok(arg); + do { + coord_t *c = str2coord(arg, board_size(board)); + m.coord = *c; coord_done(c); + if (DEBUGL(4)) + fprintf(stderr, "setting handicap %d,%d\n", coord_x(m.coord, board), coord_y(m.coord, board)); + + if (board_play(board, &m) < 0) { + if (DEBUGL(0)) + fprintf(stderr, "! ILLEGAL MOVE %d,%d,%d\n", m.color, coord_x(m.coord, board), coord_y(m.coord, board)); + gtp_error(id, "illegal move", NULL); + } + board->handicap++; + next_tok(arg); + } while (*arg); + if (DEBUGL(1) && debug_boardprint) + board_print(board, stderr); + gtp_reply(id, NULL); + + /* TODO: Engine should choose free handicap; however, it tends to take + * overly long to think it all out, and unless it's clever its + * handicap stones won't be of much help. ;-) */ + } else if (!strcasecmp(cmd, "place_free_handicap") + || !strcasecmp(cmd, "fixed_handicap")) { + char *arg; + next_tok(arg); + int stones = atoi(arg); + + gtp_prefix('=', id); + board_handicap(board, stones, id == NO_REPLY ? NULL : stdout); + if (DEBUGL(1) && debug_boardprint) + board_print(board, stderr); + if (id == NO_REPLY) return P_OK; + putchar('\n'); + gtp_flush(); + + } else if (!strcasecmp(cmd, "final_score")) { + struct move_queue q = { .moves = 0 }; + if (engine->dead_group_list) + engine->dead_group_list(engine, board, &q); + floating_t score = board_official_score(board, &q); + char str[64]; + if (DEBUGL(1)) + fprintf(stderr, "counted score %.1f\n", score); + if (score == 0) { + gtp_reply(id, "0", NULL); + } else if (score > 0) { + snprintf(str, 64, "W+%.1f", score); + gtp_reply(id, str, NULL); + } else { + snprintf(str, 64, "B+%.1f", -score); + gtp_reply(id, str, NULL); + } + + /* XXX: This is a huge hack. */ + } else if (!strcasecmp(cmd, "final_status_list")) { + if (id == NO_REPLY) return P_OK; + char *arg; + next_tok(arg); + struct move_queue q = { .moves = 0 }; + if (engine->dead_group_list) + engine->dead_group_list(engine, board, &q); + /* else we return empty list - i.e. engine not supporting + * this assumes all stones alive at the game end. */ + if (!strcasecmp(arg, "dead")) { + gtp_prefix('=', id); + for (unsigned int i = 0; i < q.moves; i++) { + foreach_in_group(board, q.move[i]) { + printf("%s ", coord2sstr(c, board)); + } foreach_in_group_end; + putchar('\n'); + } + if (!q.moves) + putchar('\n'); + gtp_flush(); + } else if (!strcasecmp(arg, "seki") || !strcasecmp(arg, "alive")) { + gtp_prefix('=', id); + bool printed_group = false; + foreach_point(board) { // foreach_group, effectively + group_t g = group_at(board, c); + if (!g || g != c) continue; + + for (unsigned int i = 0; i < q.moves; i++) { + if (q.move[i] == g) + goto next_group; + } + foreach_in_group(board, g) { + printf("%s ", coord2sstr(c, board)); + } foreach_in_group_end; + putchar('\n'); + printed_group = true; +next_group:; + } foreach_point_end; + if (!printed_group) + putchar('\n'); + gtp_flush(); + } else { + gtp_error(id, "illegal status specifier", NULL); + } + + } else if (!strcasecmp(cmd, "undo")) { + if (board_undo(board) < 0) { + if (DEBUGL(1)) { + fprintf(stderr, "undo on non-pass move %s\n", coord2sstr(board->last_move.coord, board)); + board_print(board, stderr); + } + gtp_error(id, "cannot undo", NULL); + return P_OK; + } + char *reply = NULL; + if (engine->undo) + reply = engine->undo(engine, board); + if (DEBUGL(3) && debug_boardprint) + board_print(board, stderr); + gtp_reply(id, reply, NULL); + + /* Custom commands for handling the tree opening tbook */ + } else if (!strcasecmp(cmd, "pachi-gentbook")) { + /* Board must be initialized properly, as if for genmove; + * makes sense only as 'uct_gentbook b'. */ + char *arg; + next_tok(arg); + enum stone color = str2stone(arg); + if (uct_gentbook(engine, board, &ti[color], color)) + gtp_reply(id, NULL); + else + gtp_error(id, "error generating tbook", NULL); + + } else if (!strcasecmp(cmd, "pachi-dumptbook")) { + char *arg; + next_tok(arg); + enum stone color = str2stone(arg); + uct_dumptbook(engine, board, color); + gtp_reply(id, NULL); + + } else if (!strcasecmp(cmd, "pachi-evaluate")) { + char *arg; + next_tok(arg); + enum stone color = str2stone(arg); + + if (!engine->evaluate) { + gtp_error(id, "pachi-evaluate not supported by engine", NULL); + } else { + gtp_prefix('=', id); + floating_t vals[board->flen]; + engine->evaluate(engine, board, &ti[color], vals, color); + for (int i = 0; i < board->flen; i++) { + if (!board_coord_in_symmetry(board, board->f[i]) + || isnan(vals[i]) || vals[i] < 0.001) + continue; + printf("%s %.3f\n", coord2sstr(board->f[i], board), (double) vals[i]); + } + gtp_flush(); + } + + } else if (!strcasecmp(cmd, "pachi-result")) { + /* More detailed result of the last genmove. */ + /* For UCT, the output format is: = color move playouts winrate dynkomi */ + char *reply = NULL; + if (engine->result) + reply = engine->result(engine, board); + if (reply) + gtp_reply(id, reply, NULL); + else + gtp_error(id, "unknown pachi-result command", NULL); + + } else if (!strcasecmp(cmd, "kgs-chat")) { + char *loc; + next_tok(loc); + bool opponent = !strcasecmp(loc, "game"); + char *from; + next_tok(from); + char *msg = next; + msg += strspn(msg, " \n\t"); + char *end = strchr(msg, '\n'); + if (end) *end = '\0'; + char *reply = NULL; + if (engine->chat) { + reply = engine->chat(engine, board, opponent, from, msg); + } + if (reply) + gtp_reply(id, reply, NULL); + else + gtp_error(id, "unknown kgs-chat command", NULL); + + } else if (!strcasecmp(cmd, "time_left")) { + char *arg; + next_tok(arg); + enum stone color = str2stone(arg); + next_tok(arg); + int time = atoi(arg); + next_tok(arg); + int stones = atoi(arg); + if (!ti[color].ignore_gtp) { + time_left(&ti[color], time, stones); + } else { + if (DEBUGL(2)) fprintf(stderr, "ignored time info\n"); + } + + gtp_reply(id, NULL); + + } else if (!strcasecmp(cmd, "time_settings") || !strcasecmp(cmd, "kgs-time_settings")) { + char *time_system; + char *arg; + if (!strcasecmp(cmd, "kgs-time_settings")) { + next_tok(time_system); + } else { + time_system = "canadian"; + } + + int main_time = 0, byoyomi_time = 0, byoyomi_stones = 0, byoyomi_periods = 0; + if (!strcasecmp(time_system, "none")) { + main_time = -1; + } else if (!strcasecmp(time_system, "absolute")) { + next_tok(arg); + main_time = atoi(arg); + } else if (!strcasecmp(time_system, "byoyomi")) { + next_tok(arg); + main_time = atoi(arg); + next_tok(arg); + byoyomi_time = atoi(arg); + next_tok(arg); + byoyomi_periods = atoi(arg); + } else if (!strcasecmp(time_system, "canadian")) { + next_tok(arg); + main_time = atoi(arg); + next_tok(arg); + byoyomi_time = atoi(arg); + next_tok(arg); + byoyomi_stones = atoi(arg); + } + + if (DEBUGL(1)) + fprintf(stderr, "time_settings %d %d/%d*%d\n", + main_time, byoyomi_time, byoyomi_stones, byoyomi_periods); + if (!ti[S_BLACK].ignore_gtp) { + time_settings(&ti[S_BLACK], main_time, byoyomi_time, byoyomi_stones, byoyomi_periods); + ti[S_WHITE] = ti[S_BLACK]; + } else { + if (DEBUGL(1)) fprintf(stderr, "ignored time info\n"); + } + + gtp_reply(id, NULL); + + } else { + gtp_error(id, "unknown command", NULL); + return P_UNKNOWN_COMMAND; + } + return P_OK; + +#undef next_tok +} diff --git a/jni/pachi/gtp.h b/jni/pachi/gtp.h new file mode 100644 index 0000000..8f8aec3 --- /dev/null +++ b/jni/pachi/gtp.h @@ -0,0 +1,25 @@ +#ifndef PACHI_GTP_H +#define PACHI_GTP_H + +struct board; +struct engine; +struct time_info; + +enum parse_code { + P_OK, + P_NOREPLY, + P_DONE_OK, + P_DONE_ERROR, + P_ENGINE_RESET, + P_UNKNOWN_COMMAND, +}; + +enum parse_code gtp_parse(struct board *b, struct engine *e, struct time_info *ti, char *buf); +void gtp_reply(int id, ...); +bool gtp_is_valid(const char *cmd); + +#define is_gamestart(cmd) (!strcasecmp((cmd), "boardsize")) +#define is_reset(cmd) (is_gamestart(cmd) || !strcasecmp((cmd), "clear_board") || !strcasecmp((cmd), "kgs-rules")) +#define is_repeated(cmd) (strstr((cmd), "pachi-genmoves")) + +#endif diff --git a/jni/pachi/joseki/Makefile b/jni/pachi/joseki/Makefile new file mode 100644 index 0000000..b1a8a1c --- /dev/null +++ b/jni/pachi/joseki/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=joseki.o base.o + +all: joseki.a +joseki.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/joseki/README b/jni/pachi/joseki/README new file mode 100644 index 0000000..6ef72f0 --- /dev/null +++ b/jni/pachi/joseki/README @@ -0,0 +1,32 @@ +This is a joseki pattern scanner. At the beginning, you should +have a SGF file with various joseki as variations; they are assumed +to be laid out in the upper right corner; only variations leading +to a comment containing the word "GOOD" are considered. All variations +should be tagged as real moves, not move placement. + +This pattern scanner works with the Kogo joseki dictionary. You need +to just preprocess the file with this script: + + perl -ple 's/\w+\[\]//g; s/PL\[\d\]//g;' + +Then, use the ./sgfvar2gtp.pl script to convert the SGF to a GTP stream, +one game per good variation. + +Then, feed that GTP stream to ./pachi -e joseki to get a pattern datafile +on the output - the file contains list of quadrant positions (identified +by their zobrist hashes) and the associated w-to-play and b-to-play +joseki followups. + +Save the pattern datafile to joseki19.pdict; if Pachi will find this file +in its working directory, it will use the quadrant positions in fuseki +for playouts and UCT priors. + + +In summary, the recipe for getting Pachi-compatible joseki dictionary +from the Kogo is: + + wget http://waterfire.us/071012_KJD.zip + unzip 071012_KJD.zip + cat Kogo\'s\ Joseki\ Dictionary.sgf | perl -ple 's/\w+\[\]//g; s/PL\[\d\]//g;' >kogo.sgf + perl joseki/sgfvar2gtp.pl kogo.sgf >kogo.gtp + cat kogo.gtp | ./pachi -e joseki -d 0 | grep -v ^= | grep -v '^$' >joseki19.pdict diff --git a/jni/pachi/joseki/base.c b/jni/pachi/joseki/base.c new file mode 100644 index 0000000..2cc6ef8 --- /dev/null +++ b/jni/pachi/joseki/base.c @@ -0,0 +1,78 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "move.h" +#include "joseki/base.h" + + +struct joseki_dict * +joseki_init(int bsize) +{ + struct joseki_dict *jd = calloc(1, sizeof(*jd)); + jd->bsize = bsize; + jd->patterns = calloc(1 << joseki_hash_bits, sizeof(jd->patterns[0])); + return jd; +} + +struct joseki_dict * +joseki_load(int bsize) +{ + char fname[1024]; + snprintf(fname, 1024, "joseki%d.pdict", bsize - 2); + FILE *f = fopen(fname, "r"); + if (!f) { + if (DEBUGL(3)) + perror(fname); + return NULL; + } + struct joseki_dict *jd = joseki_init(bsize); + + char linebuf[1024]; + while (fgets(linebuf, 1024, f)) { + char *line = linebuf; + + while (isspace(*line)) line++; + if (*line == '#') + continue; + hash_t h = strtoull(line, &line, 16); + while (isspace(*line)) line++; + enum stone color = *line++ == 'b' ? S_BLACK : S_WHITE; + while (isspace(*line)) line++; + + /* Get count. */ + char *cs = strrchr(line, ' '); assert(cs); + *cs++ = 0; + int count = atoi(cs); + + coord_t **ccp = &jd->patterns[h].moves[color - 1]; + assert(!*ccp); + *ccp = calloc2(count + 1, sizeof(coord_t)); + coord_t *cc = *ccp; + while (*line) { + assert(cc - *ccp < count); + coord_t *c = str2coord(line, bsize); + *cc++ = *c; + coord_done(c); + line += strcspn(line, " "); + line += strspn(line, " "); + } + *cc = pass; + } + + fclose(f); + if (DEBUGL(2)) + fprintf(stderr, "Joseki dictionary for board size %d loaded.\n", bsize - 2); + return jd; +} + +void +joseki_done(struct joseki_dict *jd) +{ + if (!jd) return; + free(jd->patterns); + free(jd); +} diff --git a/jni/pachi/joseki/base.h b/jni/pachi/joseki/base.h new file mode 100644 index 0000000..4062af7 --- /dev/null +++ b/jni/pachi/joseki/base.h @@ -0,0 +1,25 @@ +#ifndef PACHI_JOSEKI_BASE_H +#define PACHI_JOSEKI_BASE_H + +#include "board.h" + +/* Single joseki situation - moves for S_BLACK-1, S_WHITE-1. */ +struct joseki_pattern { + /* moves[] is a pass-terminated list or NULL */ + coord_t *moves[2]; +}; + +/* The joseki dictionary for given board size. */ +struct joseki_dict { + int bsize; + +#define joseki_hash_bits 20 // 8M w/ 32-bit pointers +#define joseki_hash_mask ((1 << joseki_hash_bits) - 1) + struct joseki_pattern *patterns; +}; + +struct joseki_dict *joseki_init(int bsize); +struct joseki_dict *joseki_load(int bsize); +void joseki_done(struct joseki_dict *); + +#endif diff --git a/jni/pachi/joseki/joseki.c b/jni/pachi/joseki/joseki.c new file mode 100644 index 0000000..d5bf25b --- /dev/null +++ b/jni/pachi/joseki/joseki.c @@ -0,0 +1,212 @@ +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "move.h" +#include "joseki/joseki.h" +#include "joseki/base.h" + + +/* Internal engine state. */ +struct joseki_engine { + int debug_level; + bool discard; + + int size; + struct joseki_dict *jdict; + + struct board *b[16]; // boards with reversed color, mirrored and rotated +}; + +/* We will record the joseki positions into incrementally-built + * jdict->patterns[]. */ + + +static char * +joseki_play(struct engine *e, struct board *b, struct move *m, char *enginearg) +{ + struct joseki_engine *j = e->data; + + if (!b->moves) { + /* New game, reset state. */ + j->size = board_size(b); + if (j->jdict) + assert(j->size == j->jdict->bsize); + else + j->jdict = joseki_init(j->size); + j->discard = false; + for (int i = 0; i < 16; i++) { + board_resize(j->b[i], j->size - 2); + board_clear(j->b[i]); + } + } + + //printf("%s %d\n", coord2sstr(m->coord, b), coord_quadrant(m->coord, b)); + + assert(!is_resign(m->coord)); + if (is_pass(m->coord)) + return NULL; + /* Ignore moves in different quadrants. */ + if (coord_quadrant(m->coord, b) > 0) + return NULL; + + if (coord_x(m->coord, b) == board_size(b) / 2 || coord_y(m->coord, b) == board_size(b) / 2) { + /* This is troublesome, since it cannot mirror properly: + * it won't be hashed in some quadrants. Better just discard + * the rest of the sequence for now. (TODO: Make quadrants + * overlap.) */ + j->discard = true; + } + if (j->discard) + return NULL; + + //printf("%"PRIhash" %"PRIhash"\n", j->b[0]->qhash[0], b->qhash[0]); + assert(j->b[0]->qhash[0] == b->qhash[0]); + + /* Record next move in all rotations and update the hash. */ + for (int i = 0; i < 16; i++) { +#define HASH_VMIRROR 1 +#define HASH_HMIRROR 2 +#define HASH_XYFLIP 4 +#define HASH_OCOLOR 8 + int quadrant = 0; + coord_t coord = m->coord; + if (i & HASH_VMIRROR) { + coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b)); + quadrant += 2; + } + if (i & HASH_HMIRROR) { + coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b)); + quadrant++; + } + if (i & HASH_XYFLIP) { + coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b)); + if (quadrant == 1) + quadrant = 2; + else if (quadrant == 2) + quadrant = 1; + } + enum stone color = m->color; + if (i & HASH_OCOLOR) + color = stone_other(color); + + coord_t **ccp = &j->jdict->patterns[j->b[i]->qhash[quadrant] & joseki_hash_mask].moves[color - 1]; + + int count = 1; + if (*ccp) { + for (coord_t *cc = *ccp; !is_pass(*cc); cc++) { + count++; + if (*cc == coord) { + //printf("%d,%d (%"PRIhash", %d) !+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b)); + goto already_have; + } + } + } + + //printf("%d,%d (%"PRIhash", %d) =+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b)); + *ccp = realloc(*ccp, (count + 1) * sizeof(coord_t)); + (*ccp)[count - 1] = coord; + (*ccp)[count] = pass; + +already_have: { + struct move m2 = { .coord = coord, .color = color }; + board_play(j->b[i], &m2); + } + } + + return NULL; +} + +static coord_t * +joseki_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + fprintf(stderr, "genmove command not available in joseki scan!\n"); + exit(EXIT_FAILURE); +} + +void +engine_joseki_done(struct engine *e) +{ + struct joseki_engine *j = e->data; + struct board *b = board_init(NULL); + board_resize(b, j->size - 2); + board_clear(b); + + for (hash_t i = 0; i < 1 << joseki_hash_bits; i++) { + for (int s = 0; s < 2; s++) { + static const char cs[] = "bw"; + if (!j->jdict->patterns[i].moves[s]) + continue; + printf("%" PRIhash " %c", i, cs[s]); + coord_t *cc = j->jdict->patterns[i].moves[s]; + int count = 0; + while (!is_pass(*cc)) { + printf(" %s", coord2sstr(*cc, b)); + cc++, count++; + } + printf(" %d\n", count); + } + } + + board_done(b); + + joseki_done(j->jdict); +} + + +struct joseki_engine * +joseki_state_init(char *arg) +{ + struct joseki_engine *j = calloc2(1, sizeof(struct joseki_engine)); + + for (int i = 0; i < 16; i++) + j->b[i] = board_init(NULL); + + j->debug_level = 1; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "debug")) { + if (optval) + j->debug_level = atoi(optval); + else + j->debug_level++; + + } else { + fprintf(stderr, "joseki: Invalid engine argument %s or missing value\n", optname); + exit(EXIT_FAILURE); + } + } + } + + return j; +} + +struct engine * +engine_joseki_init(char *arg, struct board *b) +{ + struct joseki_engine *j = joseki_state_init(arg); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "Joseki"; + e->comment = "You cannot play Pachi with this engine, it is intended for special development use - scanning of joseki sequences fed to it within the GTP stream."; + e->genmove = joseki_genmove; + e->notify_play = joseki_play; + e->done = engine_joseki_done; + e->data = j; + // clear_board does not concern us, we like to work over many games + e->keep_on_clear = true; + + return e; +} diff --git a/jni/pachi/joseki/joseki.h b/jni/pachi/joseki/joseki.h new file mode 100644 index 0000000..74730ad --- /dev/null +++ b/jni/pachi/joseki/joseki.h @@ -0,0 +1,8 @@ +#ifndef PACHI_JOSEKI_JOSEKI_H +#define PACHI_JOSEKI_JOSEKI_H + +#include "engine.h" + +struct engine *engine_joseki_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/joseki/sgfvar2gtp.pl b/jni/pachi/joseki/sgfvar2gtp.pl new file mode 100644 index 0000000..5b05b6b --- /dev/null +++ b/jni/pachi/joseki/sgfvar2gtp.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl +# Convert SGF file to a sequence of GTP games, one game per each variation +# that ends with /GOOD/ comment. + +use warnings; +use strict; + +sub printgame +{ + my ($sgf) = @_; + my $pt = $sgf->getAddress(); + + my @moves; + do { + my ($b, $w) = ($sgf->property('B'), $sgf->property('W')); + if ($b) { push @moves, ['b', $_] foreach @$b; } + if ($w) { push @moves, ['w', $_] foreach @$w; } + } while ($sgf->prev()); + + print "boardsize 19\nclear_board\n"; + for my $move (reverse @moves) { + my ($sx, $sy) = @{$move->[1]}; + my @abcd = split(//, "abcdefghjklmnopqrstuvwxyz"); + my $x = $sy + 1; my $y = $abcd[18 - $sx]; + if ("$y$x" eq "z20") { + $y = "pass"; $x = ""; + } + print "play ".$move->[0]." $y$x\n"; + } + + $sgf->goto($pt); +} + +sub recurse +{ + my ($sgf) = @_; + my $c = $sgf->property('C'); + if ($c and $c->[0] =~ /GOOD/) { + printgame($sgf); + } + for (0 .. $sgf->branches()-1) { + $sgf->gotoBranch($_); + recurse($sgf); + $sgf->prev(); + } +} + +use Games::SGF::Go; +my $sgf = new Games::SGF::Go; + +$sgf->readFile($ARGV[0]); + +$sgf->gotoRoot(); +recurse($sgf); diff --git a/jni/pachi/joseki19.pdict b/jni/pachi/joseki19.pdict new file mode 100644 index 0000000..2d2ee62 --- /dev/null +++ b/jni/pachi/joseki19.pdict @@ -0,0 +1,31551 @@ +# This joseki database has been autogenerated from 071012_KJD.zip: +# The Kogo Joseki Dictionary, http://waterfire.us/joseki.htm +# Distribution of this data along Pachi under the terms of GPLv2 +# has been approved by Gary Odom on 2010-12-04. +# +0 b C3 C17 R3 R17 C4 C16 R4 R16 D3 Q3 D17 Q17 D4 D16 Q4 Q16 E3 E17 P3 P17 C5 R5 C15 R15 E4 E16 P4 P16 D5 Q5 D15 Q15 32 +0 w C3 C17 R3 R17 C4 C16 R4 R16 D3 Q3 D17 Q17 D4 D16 Q4 Q16 E3 E17 P3 P17 C5 R5 C15 R15 E4 E16 P4 P16 D5 Q5 D15 Q15 32 +11 w L17 1 +41 w N4 1 +50 b O16 1 +76 b S4 1 +a1 b D4 1 +a6 b R3 1 +b6 w C8 1 +f1 w D16 1 +130 b P17 1 +137 w P14 1 +151 w F2 1 +1d6 w L17 1 +216 w E2 B2 2 +221 b S3 1 +2e1 w O17 P16 2 +301 b O4 N3 2 +311 w D19 1 +330 w D8 1 +341 b Q15 R17 R13 O16 M16 5 +341 w P16 Q17 2 +367 b C17 D14 D16 E16 F15 E15 E13 D13 F17 G17 G16 11 +367 w C16 1 +3a1 b P6 Q18 2 +3c7 w C16 1 +3d0 w E18 1 +3e6 w D2 1 +3f0 b F4 1 +410 b D16 P7 N4 3 +420 w R7 1 +477 b C5 1 +477 w B16 1 +481 b D3 1 +4c6 b B2 1 +4c7 w R4 1 +510 b N16 Q15 2 +541 w D16 1 +581 b J16 1 +587 b G2 1 +5a0 w E18 1 +5b1 b Q11 1 +5c6 b D18 1 +5d0 w O4 1 +5f0 w R14 1 +616 b P15 1 +686 w N5 1 +6c1 b Q7 1 +6c6 b L6 1 +726 b O17 P16 2 +7b1 w G14 1 +7d1 b F18 1 +7e1 w C5 1 +7f0 w B5 1 +7f1 w P9 1 +7f7 b P2 1 +827 w Q2 1 +866 w E2 1 +8b0 w O12 1 +8f1 b N4 1 +8f6 b Q14 1 +926 w G6 1 +927 b D15 1 +930 b C5 1 +930 w Q17 1 +931 w P7 1 +937 b O5 1 +997 b D5 C4 B3 3 +997 w E4 C3 G3 D6 D8 5 +9c1 b J3 E6 2 +9e0 w C14 1 +9f1 b D11 1 +a07 w D3 1 +a46 w R1 1 +a56 w O5 N4 P6 3 +a57 w Q6 1 +a70 w R16 1 +aa0 w C5 C14 2 +ab1 b O4 1 +b16 b P2 1 +b31 w C5 1 +b46 b F18 1 +b47 w B14 1 +b80 b E3 1 +b96 b E4 1 +b96 w J3 1 +ba7 b L15 1 +bc0 b P2 1 +bd0 w G3 1 +be1 w B16 1 +c11 b E6 1 +c26 b N18 1 +c26 w D15 1 +c40 w Q18 1 +c56 b N4 1 +c66 w E17 1 +cb0 b C12 1 +ce1 w R8 1 +cf0 w E5 1 +cf6 w R6 N3 2 +d07 w Q6 C17 2 +d26 b D7 1 +d26 w F18 1 +d36 w C16 1 +d87 b S14 1 +db7 w Q16 1 +e01 w D14 E15 2 +e16 w G6 1 +e27 w E4 1 +eb6 b L2 1 +eb7 w S15 1 +ed7 w Q16 1 +f30 b E4 1 +f41 b Q3 1 +f86 b G18 1 +fa1 b G5 1 +fd1 w B15 1 +ff6 b G16 1 +1001 w Q4 Q6 2 +1030 b S6 1 +10c6 w O17 Q16 2 +10c7 w F3 1 +10f6 b D4 1 +1131 b C13 1 +1150 b R16 1 +1197 b B16 1 +11a7 w C7 1 +11b1 b P17 1 +11e7 b H3 1 +1216 w S16 1 +1221 b E4 1 +1237 b D14 E15 2 +1237 w C14 1 +1256 b M17 1 +12a0 w Q2 1 +12b7 b Q17 1 +1337 w C5 1 +1351 b C8 1 +1376 w Q3 1 +1387 w P17 1 +13b0 w B15 D15 2 +13d0 w S3 1 +1437 b O17 1 +1437 w D5 1 +1446 b S5 1 +1447 w D6 1 +1456 b H18 1 +1460 b J16 1 +1481 w O16 1 +14a0 b A15 1 +14e7 b D3 1 +14f0 w E17 1 +1510 w C7 1 +1516 w B3 1 +1537 w Q8 1 +1540 w O16 1 +1541 w Q14 1 +1560 w S4 1 +1566 b E18 1 +1570 b H14 1 +1587 w N6 1 +1591 b P2 1 +15b0 b E16 1 +15f6 w R17 F18 2 +1620 b P15 1 +1640 w E14 1 +1647 w D14 1 +1651 b N16 1 +1677 w N6 1 +1696 b R14 1 +16a6 b L18 1 +16a7 w C19 1 +16c1 w E8 1 +16d1 b R4 1 +1710 b E15 1 +1727 b P16 1 +17a0 w O5 1 +17a1 w O12 1 +17d7 b Q14 R17 2 +17e7 b E15 C15 2 +1800 w O16 O18 2 +1811 w L16 1 +1817 b C15 O15 2 +1837 w P17 1 +1860 w Q5 1 +1880 b C13 1 +1886 w R14 1 +18c0 b C3 1 +18e1 w C3 1 +18e6 w N3 1 +1900 w O15 1 +1930 w D17 1 +1931 b F4 1 +1936 b D14 1 +1937 b B4 1 +1946 b R2 1 +1957 b D3 1 +1970 b R5 1 +1977 w D13 1 +1987 b C18 1 +1990 w O17 1 +19d0 w C17 1 +1a51 w Q13 Q16 2 +1a87 b Q16 1 +1a91 w C14 1 +1aa1 w N16 1 +1ac0 w P6 1 +1af0 b P14 1 +1b06 w C15 1 +1b51 w G2 1 +1b57 w Q2 1 +1b61 b Q3 R3 2 +1b70 b P4 1 +1b87 b B5 1 +1bc1 b E13 1 +1c37 b P5 1 +1c91 b F17 1 +1cb0 w C18 1 +1ce0 w C16 1 +1ce7 w P12 1 +1d66 w Q1 1 +1d81 w J17 1 +1df0 w O2 1 +1df7 w R16 1 +1e01 b B15 1 +1e37 b F2 1 +1e41 b G4 C5 2 +1e56 w R12 1 +1ea1 b Q3 1 +1ea7 w H4 1 +1ee7 w B7 1 +1f11 w E11 1 +1f27 b F5 1 +1f46 b O16 E16 2 +1f87 b S3 1 +1fd6 b P2 1 +1fd7 w Q17 1 +1ff0 b S5 1 +2006 w P2 1 +2026 w R17 1 +2031 b D6 1 +20b0 b D18 E17 2 +20b0 w Q8 1 +2100 w E18 1 +2116 b F15 1 +2126 b O16 1 +2156 b R16 1 +2171 w B17 1 +2181 w N16 1 +21d6 w C14 1 +21f0 w D18 1 +2201 w F18 1 +2220 b P14 1 +22f7 b L16 1 +2300 b D4 1 +2301 b D6 1 +2320 b H15 E18 2 +2346 b M2 1 +2361 b Q6 1 +2370 w H15 1 +2371 b J4 1 +23b7 b B7 1 +23c0 w S2 1 +23c7 b N14 1 +23e7 b Q15 P15 N16 R17 4 +23e7 w P16 1 +2416 w R8 1 +2431 b Q18 1 +2470 b C14 1 +2480 w S16 1 +2496 b D5 1 +24a1 b H16 1 +24a7 b F4 G3 2 +24c7 b A18 1 +24d0 w Q15 1 +24d6 w B6 1 +24f7 b D13 1 +2510 b F14 1 +2531 b F16 1 +2580 w N16 R16 2 +25c1 w S7 1 +25d0 b D7 1 +2620 w C17 1 +2637 w F3 1 +2650 w Q13 Q16 P14 3 +2697 b E3 1 +26b6 w Q5 1 +2731 b F5 1 +2740 b D7 D3 2 +27a0 b D16 1 +27c7 b G6 1 +27f1 b E4 1 +2811 w F7 1 +2840 b D13 1 +2877 w O14 1 +2891 b P2 1 +28e1 b B17 1 +2906 b C15 1 +2907 b P5 1 +2917 w E8 1 +2947 b E5 F3 2 +2991 b J18 1 +29c7 b L17 1 +29f0 w R17 1 +2a21 w D17 1 +2ab7 w S4 1 +2b16 w J18 1 +2b20 w Q6 1 +2b27 b P11 1 +2b30 b O4 1 +2b40 b D5 1 +2b90 w G4 1 +2b91 w J18 1 +2ba0 w N14 1 +2ba6 b P3 1 +2bb0 w M3 1 +2bc0 w R15 O14 2 +2bc6 b R13 P15 2 +2bc6 w C5 E6 O3 3 +2bd0 b Q15 1 +2bd0 w N17 1 +2be6 b N4 1 +2c10 b N7 1 +2c26 w C4 1 +2c40 w S2 1 +2c60 b S3 1 +2c67 w O4 1 +2c81 w J3 1 +2c87 b F15 1 +2c90 b O15 1 +2ca1 w D14 1 +2cc1 b P15 1 +2cd0 b P17 1 +2cf6 w C18 1 +2d70 b B15 1 +2d86 b O2 1 +2d90 w Q5 1 +2db6 b P5 1 +2de7 w D16 1 +2e40 b Q2 1 +2e86 b Q14 1 +2ee1 b C5 R8 2 +2ef6 b R8 1 +2f36 b A16 1 +2f37 w B15 1 +2f41 b O16 1 +2f57 w G5 1 +2fe7 b N3 1 +3051 b D5 1 +3067 b C15 1 +3070 b H3 1 +3081 w P16 1 +3090 b M3 1 +30a0 w J4 1 +30b6 w B2 1 +30c6 w C12 1 +30e7 b M3 1 +3101 b G17 1 +3116 w B8 1 +3170 b D15 1 +3186 w H2 1 +31a0 w H4 1 +31f0 b G2 1 +3216 b N16 1 +3261 b Q13 1 +3277 w B4 1 +32a0 b Q17 1 +32a0 w P18 1 +32b1 b C2 1 +32f7 w C4 1 +3371 w C18 1 +33b1 b S17 1 +33c1 b R5 1 +3427 w O16 1 +3471 w G4 1 +3480 w F17 1 +34c7 w D18 1 +3501 b A16 1 +3507 b F16 B5 2 +3576 w O3 1 +35b0 b E4 1 +35d0 b F4 1 +35e1 b F13 1 +3606 b R4 1 +3617 w C18 1 +3647 w C8 1 +3657 w C7 1 +36b1 b S14 1 +36f1 b R5 S6 S5 3 +3720 w S16 Q14 2 +3751 w H3 1 +3770 b C14 1 +3781 b T19 1 +3796 w S15 S18 2 +37e1 w G6 1 +37f6 b C3 1 +3811 b N15 O16 2 +3821 w M17 1 +3846 w F14 1 +3847 b D17 1 +3850 w F4 1 +3856 w G4 1 +3881 b D14 1 +3891 w R14 P16 S3 3 +38e0 b P4 1 +38f6 b R7 P5 2 +3906 b B7 1 +3921 b T3 1 +3930 w O16 1 +3931 w C15 1 +3936 w C16 1 +3940 w P16 1 +3950 w Q14 1 +3996 b C9 1 +3997 b E15 1 +39e1 w P6 L4 2 +39f0 w P2 1 +3a16 w B8 1 +3a36 b A3 1 +3a40 b Q7 1 +3a51 b C3 B3 2 +3a56 b C3 B18 2 +3a56 w R5 1 +3a76 b S16 1 +3a87 w D8 1 +3a90 b E4 1 +3aa0 b E5 1 +3ac7 w B14 1 +3ae0 w L17 1 +3b01 w C17 1 +3b10 b F4 1 +3b11 w Q2 1 +3b17 b N17 1 +3b47 w C12 1 +3b51 b P4 Q5 2 +3b66 w S9 1 +3b77 b B6 1 +3bc1 w E5 E3 2 +3c17 b R5 1 +3c30 b R18 1 +3c37 w D17 1 +3ca7 w O3 1 +3cf0 w D14 1 +3d70 b E16 1 +3d77 w P15 1 +3d91 b H15 Q15 2 +3d97 w P5 1 +3da0 b S15 1 +3dd7 b O4 1 +3df0 w R3 1 +3e01 b S16 1 +3e21 b Q17 1 +3e51 b P18 N17 2 +3e77 w R18 1 +3ea0 w E16 1 +3ed6 w T17 1 +3ee0 b O17 1 +3ef6 b O2 1 +3f31 w D16 1 +3f50 b P3 1 +3f61 w C3 1 +3f76 b R4 S5 2 +3f90 w F17 1 +3fd0 w N4 1 +3fe7 w Q14 1 +3ff0 b C13 1 +4011 w S5 1 +4017 b R2 1 +4047 b R4 1 +4050 b E3 1 +4061 b D3 1 +4067 w P17 1 +4070 b J3 1 +4087 w P13 1 +4091 w R17 1 +40b1 w N18 1 +40c0 w Q8 1 +40e6 w D6 1 +4141 w C3 1 +4147 b D18 1 +4157 w B16 C17 S6 3 +41b6 w F6 1 +41e0 b F2 1 +41e7 w S4 1 +41f0 w Q16 1 +4201 b R15 1 +4220 w O6 1 +4236 b P15 1 +4246 b N3 1 +4250 b C9 1 +4280 b R4 Q3 2 +4281 w Q14 1 +4286 b C3 1 +4296 w L14 1 +42b0 b S4 Q4 2 +42b1 w P16 1 +4300 b Q3 Q4 2 +4301 w S17 1 +4341 w G3 1 +4366 w N4 1 +4381 w R12 1 +4396 b P7 O7 2 +4396 w C17 1 +43b0 w E16 1 +43b1 b Q6 1 +43b7 w D6 D7 2 +43c0 b D9 1 +43c7 w S4 1 +43d7 b O18 1 +4410 b C14 1 +4420 w C15 1 +4440 b D7 1 +4500 b O3 1 +4501 w N17 C17 2 +4507 w E17 1 +4526 b P6 1 +4566 b R15 1 +4580 w C7 1 +4591 w B11 1 +45a7 w S15 1 +45b0 b S15 1 +45b1 w E4 1 +45d0 w E2 1 +45f1 w C8 1 +4617 w D3 1 +4687 b C4 1 +46a6 w R5 1 +46f0 w P18 1 +4707 w S15 O18 2 +4727 b R6 E6 C3 3 +4751 w E8 1 +4756 b N16 1 +4790 w N2 1 +4796 b F3 1 +4826 w C14 1 +4830 w B15 1 +4837 b M16 1 +4846 w D13 1 +48a0 w D16 1 +48a7 w B15 1 +48c7 w Q16 1 +48e0 b N4 1 +4901 w L15 R16 2 +4996 w R17 1 +49a6 w P4 1 +49c7 w P15 1 +4a31 w O3 1 +4a36 w E4 1 +4a47 w C14 1 +4a67 b C8 1 +4a80 b E17 1 +4a91 w C5 1 +4a97 w S16 1 +4ab7 b D7 1 +4ae6 w Q5 1 +4b16 b O5 1 +4b46 w N18 1 +4b77 b G17 1 +4b81 w Q14 1 +4b86 b E7 1 +4b91 w B6 1 +4ba1 b Q3 1 +4ba6 w C2 1 +4bd6 w D18 1 +4bd7 w E2 1 +4be0 w L16 1 +4c11 w C3 D3 C9 C8 B5 C4 6 +4c16 b D2 1 +4c17 w H15 1 +4c27 b Q13 1 +4c41 b C17 D16 E16 F15 F16 G16 6 +4c41 w C16 1 +4c71 w N4 O2 2 +4c87 w C4 1 +4ca0 w B15 1 +4cd7 b C15 1 +4d00 b E18 1 +4d56 b R14 1 +4d60 w P14 1 +4d66 w O6 1 +4df0 w P17 1 +4df1 w D2 1 +4df7 w P3 1 +4e00 b N18 1 +4e10 w P3 1 +4e36 b E4 1 +4e47 w C3 1 +4e71 b C4 F6 2 +4e76 b R15 1 +4e77 b N16 1 +4e80 b H16 1 +4e90 b R2 1 +4eb1 w E5 1 +4eb7 w S15 1 +4f00 w L16 1 +4f20 b P17 1 +4f26 w F13 1 +4f31 b B3 1 +4f51 b C7 1 +4f86 w O5 1 +4fd7 w E6 1 +4fe6 w E17 1 +5057 w F5 1 +50b0 b B2 1 +50f6 w Q14 1 +5100 b N5 1 +5141 b E4 1 +5171 w E3 1 +5177 b C16 1 +5187 b S6 1 +51a1 b C18 1 +51d7 w F3 1 +5207 b D9 1 +5211 w Q4 1 +5216 b S1 1 +5220 b M13 O12 2 +5227 w J16 1 +5270 b S5 E3 2 +52a6 w Q7 R7 R3 3 +52d7 w R2 1 +5311 b Q5 1 +5340 w P18 1 +5350 b S3 1 +53f0 w H17 1 +53f1 b O4 1 +5420 b O4 1 +5487 b R17 R16 L17 M17 P18 Q17 6 +5487 w Q13 1 +5490 b J3 1 +54b0 b E17 1 +54c6 w C8 1 +54c7 w C3 E6 F3 F4 4 +54d1 b S14 1 +54d1 w D17 1 +54e7 b C5 1 +5511 w P2 1 +5540 b P12 1 +5550 w B15 1 +5560 b O12 1 +5581 b C15 1 +5591 b Q13 1 +55b6 w P4 1 +55c1 b D9 1 +5607 w L14 1 +5616 w D17 1 +5626 b B17 1 +5630 w P2 1 +5651 w Q1 1 +5657 w Q2 1 +5681 b F2 1 +56b6 b R15 1 +56d0 b D3 C4 S2 3 +56e0 b C11 1 +56e1 w R7 1 +56f1 b Q2 1 +5710 w Q7 P4 2 +5731 w F13 1 +5747 w P5 1 +57a0 b D18 1 +57b0 b Q11 1 +57c0 b P7 1 +57d7 b Q14 1 +57f6 w R3 1 +5810 b R2 1 +5816 b M17 1 +5820 b O17 P14 2 +5841 w M16 1 +5847 b D7 1 +58f7 b D6 1 +5931 w C6 1 +5950 w C13 1 +5960 b N15 1 +5967 w Q18 1 +5986 b R3 1 +59a7 w C3 B5 D7 3 +59d0 b D4 1 +59d0 w R2 1 +59e1 b L16 1 +5a06 w B9 1 +5a50 b R13 1 +5a71 b B3 1 +5a76 b R6 1 +5a77 w O18 1 +5a90 w D5 1 +5ae7 w C6 1 +5af6 w Q4 1 +5b17 w Q3 1 +5b20 w P3 1 +5b37 b B11 1 +5b60 w M16 1 +5c20 b N15 1 +5c20 w R7 1 +5c27 w O15 1 +5c40 w F4 1 +5d01 w M17 1 +5d27 b F3 1 +5da0 w R11 1 +5dd7 b C15 1 +5de1 b R2 1 +5de6 b C2 1 +5e26 w G3 1 +5e27 w F14 1 +5e46 b Q6 1 +5e67 w O16 1 +5e96 b B15 1 +5e97 w S18 1 +5ea1 w F15 D14 2 +5ea7 w B14 1 +5f01 w R4 1 +5f40 w Q13 1 +5f41 w Q11 1 +5f46 b N15 1 +5f60 w C15 1 +5f86 w R16 1 +5fa7 b E6 1 +5fb1 w S6 1 +5fb6 w N15 1 +6000 b N17 R14 2 +6000 w J4 1 +6020 b O5 P4 2 +6036 b Q17 1 +6040 w B5 1 +6047 w D7 E3 2 +6056 w N4 1 +6066 b P17 1 +6077 w B4 1 +60a0 w H16 1 +60c1 w S17 1 +60c7 w D5 1 +60f0 w E4 1 +6121 w R15 1 +6136 w Q11 1 +6156 w R15 Q16 2 +6167 b C2 C13 2 +6187 w O3 1 +61a1 b C15 1 +61a6 w A17 1 +61c7 b R8 1 +6217 w Q17 R17 2 +6227 b S15 1 +6257 b C8 B5 B4 3 +6286 b S17 1 +6291 w B2 1 +62a6 w D2 1 +62a7 w H16 1 +6326 b G2 1 +6327 b P17 1 +6371 w B9 1 +6386 b R18 1 +63b7 b P11 Q17 2 +6446 w E18 B18 2 +6486 b E4 1 +6486 w E4 1 +64a6 w O16 1 +64d6 b B14 1 +64f6 b N4 1 +6507 b E16 1 +6517 b Q13 1 +6566 w C1 1 +6570 w R4 1 +6577 w F12 1 +65a1 w Q4 1 +65e1 b M18 1 +6657 w D4 1 +6691 w D5 D6 2 +66b1 w G18 1 +66b7 w N3 1 +66c1 w D2 1 +6707 w P15 1 +6711 b C18 1 +6736 b O13 1 +6766 w E4 1 +67b1 b B3 1 +67b7 w B5 1 +67c7 w R3 Q7 2 +67d7 b G18 1 +6800 w C7 1 +6831 b S5 1 +6841 w B14 1 +6857 b Q6 P7 2 +6887 b C8 1 +68a7 b E2 1 +68c7 w H15 1 +6941 w D2 1 +6951 w P16 D8 2 +6970 w Q15 1 +6987 b M2 1 +6987 w G2 F17 2 +69a6 w D12 1 +69d6 b O17 1 +69e6 w M18 1 +69f7 b E3 1 +6a87 w N2 B17 2 +6a91 w B16 1 +6a96 b R3 1 +6ab6 w D13 1 +6ae1 b N7 1 +6b00 w P17 1 +6b37 w D3 1 +6b40 w E5 1 +6b71 b S15 1 +6bd1 w S4 1 +6bd7 w C4 1 +6c30 b C5 1 +6c87 b D3 1 +6cb0 w D3 1 +6cc0 b B14 1 +6cd0 w Q8 1 +6cd6 w E15 1 +6d00 b D11 1 +6d26 b Q13 1 +6d31 w E8 1 +6d71 w R14 1 +6db0 b E7 1 +6db6 b F13 1 +6dc1 w B4 B14 2 +6e07 w B3 1 +6e20 w N17 1 +6e21 b J16 1 +6e31 w R6 1 +6e51 w C8 1 +6e60 w O3 1 +6e96 b D6 1 +6ea1 w F5 1 +6ed6 w G3 1 +6ed7 b G14 1 +6ef1 w C14 B14 2 +6f17 w R6 1 +6f21 w C17 1 +6f67 w G4 1 +6f70 b G15 1 +6f77 b F14 1 +6f97 b S6 1 +7000 b G14 O4 2 +70d1 b C15 1 +70d1 w C4 1 +7106 b Q5 1 +7117 b N3 1 +7157 w O4 1 +7186 b E17 1 +71c0 b P5 1 +71d7 w E17 1 +71f1 w C17 1 +7206 b O4 1 +7216 b A16 1 +7237 b R5 1 +7260 w C18 1 +7277 w P4 1 +7280 b Q8 1 +72a6 w D17 D15 2 +72b6 b O7 1 +72b6 w Q15 O16 2 +7327 w R5 1 +7396 w C7 1 +73b7 b P18 1 +73f0 b O2 1 +7416 b D14 1 +7466 b J17 1 +7470 b E4 1 +74b0 w C18 1 +74b6 b E5 D5 2 +74e0 b R7 1 +74f0 w Q15 1 +7506 b E14 1 +7537 w P6 1 +7580 w B3 1 +7590 w S17 S16 2 +75f7 w E3 B7 2 +7600 b R6 1 +7626 w S15 1 +7630 w F12 1 +76a6 b C15 C17 2 +76f1 w C16 1 +7736 w L3 1 +7737 w B4 1 +7740 b H4 1 +7756 b C3 1 +7761 w P19 1 +7781 w L16 1 +7787 w E18 1 +77b0 b P17 1 +77e7 b B6 1 +7817 b M5 1 +7817 w P5 1 +7831 b O17 1 +7846 b Q15 O16 2 +78f7 w P16 1 +7927 w N4 1 +7941 b Q2 1 +79a7 w C2 1 +79b7 b M17 1 +79c6 b R16 1 +79d0 b D5 1 +79e6 b C5 B5 2 +79e7 w C3 G4 2 +7a17 b O4 1 +7a17 w Q6 1 +7a27 w B15 1 +7a37 w E3 1 +7a51 w P14 R17 2 +7a57 w S12 1 +7ab0 b F8 1 +7ad6 b S11 1 +7b10 w F6 1 +7b21 b G4 1 +7b46 w Q17 1 +7b76 b Q3 1 +7bc1 w O7 1 +7bc6 w C16 1 +7c01 b D3 1 +7c01 w P3 1 +7c07 b C11 1 +7c26 w D16 1 +7c40 b C15 C18 2 +7c50 w S6 1 +7c91 b C7 1 +7c97 b F8 Q16 2 +7ca7 b S4 1 +7cb6 b R15 1 +7cb7 b Q6 1 +7ce0 w P7 1 +7ce1 w R3 Q3 R6 3 +7ce6 b O16 1 +7d01 w C15 1 +7d46 w R17 1 +7d57 b F3 1 +7d60 w O5 1 +7d97 w P5 1 +7de7 w R7 1 +7e20 w F3 Q13 2 +7e31 w B16 1 +7e36 b B16 1 +7e41 w O5 1 +7e66 b P5 1 +7e86 b P4 1 +7eb1 w D3 1 +7eb7 w B5 1 +7ec0 w C19 1 +7f06 b Q18 1 +7f10 w P16 1 +7f51 b P6 1 +7f77 w R15 1 +7f81 b S16 1 +7f91 w N17 1 +7fb1 w P1 1 +7fc7 w C15 1 +7fd6 b M3 1 +7ff0 b R17 1 +8060 b Q13 1 +80d1 b Q3 1 +80e1 w O6 1 +80f0 w C16 1 +80f6 b P4 Q5 2 +8120 b C7 1 +8120 w C13 E15 D13 3 +8141 b C16 1 +8146 w Q8 1 +8157 w S16 1 +8166 w D16 1 +8177 b P17 1 +8180 b R7 1 +8180 w P2 1 +81e0 w G16 1 +81e6 w S15 1 +8220 b Q3 1 +8221 b Q18 1 +8287 w Q14 1 +82f6 w M16 1 +82f7 b O5 1 +8311 b N5 1 +8321 w P6 1 +8327 b E14 F14 E17 F17 4 +8337 b P3 1 +8346 b C2 1 +8366 b P4 1 +83d0 b P4 1 +83f7 b B4 C2 2 +8411 w S15 1 +8416 b R2 1 +8427 b F15 1 +8461 b C16 1 +8491 w D17 1 +84a1 b F16 1 +84f1 w B14 1 +8506 w H17 1 +8520 w F5 1 +8540 b Q15 1 +8551 b Q14 P13 2 +8557 b T2 1 +8586 w R13 1 +8587 w R4 1 +85a6 w C15 1 +85c1 b M3 1 +85d1 w S3 1 +8611 b F4 1 +8626 w D5 1 +8671 w P12 1 +8690 w Q1 1 +86b0 b S5 1 +86d0 b N4 1 +86d1 w S13 1 +86d6 w D6 D7 2 +86e1 w G2 1 +86e7 b B4 1 +8701 w Q16 1 +8707 w D17 1 +8731 b D7 1 +87c7 b C4 1 +8800 b L4 1 +8851 b C5 1 +88b6 b F3 1 +88b7 w D12 1 +88d0 w E4 1 +8951 b Q4 1 +8961 b C3 1 +8977 b P18 1 +8991 b G17 1 +89f0 b D7 1 +89f7 w O15 1 +8a00 w H16 1 +8a30 b C3 1 +8a40 w R5 1 +8a61 w D17 1 +8a70 b D16 F18 2 +8ab1 b O4 1 +8ac1 w S18 1 +8af0 w B5 1 +8b01 b N2 1 +8b17 w C3 1 +8b40 w F13 C16 2 +8b41 w O15 1 +8b57 b C17 1 +8b60 w O8 1 +8b61 b C3 1 +8b97 w R2 1 +8ba0 b E4 1 +8bb6 w R16 1 +8bc0 w R12 1 +8c17 w R13 1 +8c20 b N16 R16 2 +8c46 w R2 1 +8c70 w D14 1 +8c76 b R13 1 +8cb1 b L4 1 +8cb7 w R18 1 +8cd6 b B17 1 +8d11 b P3 1 +8d41 w O6 1 +8d46 b C7 1 +8d51 w R4 1 +8da6 b R12 1 +8de0 b Q5 1 +8e17 w C5 1 +8e30 w G7 1 +8e31 b Q14 1 +8e57 w C18 1 +8e70 w P2 1 +8e96 b C2 1 +8ec6 b Q14 1 +8ed6 w C2 1 +8ef7 w Q17 1 +8f06 w B3 1 +8f50 w N17 P15 N16 3 +8f51 w B5 1 +8f57 b C3 1 +8f76 b C17 D16 2 +8fe1 b P15 1 +9030 b D3 1 +9071 w D3 1 +9096 b D14 D13 2 +90b7 w B15 1 +90f0 b P18 1 +9126 w H4 1 +9197 b S15 1 +91f1 b C8 1 +9227 b P3 1 +9230 b F6 1 +9231 w E16 F16 2 +9237 b N17 1 +9250 w O4 1 +9251 w P5 1 +92a7 w B14 1 +92c1 b Q12 1 +9310 w F7 1 +9320 w D13 1 +9321 b R16 1 +9327 w D17 1 +9336 b R14 Q15 2 +9340 w B3 1 +9351 w C8 1 +9357 b S16 1 +93b7 b C14 1 +93c6 b G16 1 +93d6 b O4 N4 2 +93e7 w C16 1 +93f1 w M3 1 +9407 w E17 1 +9431 b D18 1 +9436 b Q6 1 +9456 b E15 1 +9460 w R14 1 +9516 b C3 1 +9521 b Q16 1 +9570 b E17 1 +95a7 w R2 1 +95d7 b R11 1 +95f6 w R5 1 +9610 b Q12 1 +9647 b Q14 1 +9666 w Q15 1 +9670 w M5 1 +9677 b S4 1 +96a1 b N5 1 +96c0 b O18 1 +96f0 b P16 1 +96f6 w C17 1 +9707 w R7 1 +9721 w N17 1 +9740 w S5 1 +9776 w D14 1 +97a6 b E17 1 +97b0 b D5 1 +97c0 w G16 C16 2 +97d0 b E4 1 +9856 w S4 1 +9891 b F4 1 +98b0 b Q8 Q2 P3 3 +98b6 b P6 1 +98c6 w D6 1 +98c7 b R16 1 +9916 w Q18 1 +9936 w Q6 Q7 2 +9941 b M4 1 +9941 w P4 1 +9957 w E3 F2 E2 3 +9970 b P8 O8 2 +9986 b Q13 1 +99b1 b H15 1 +99b7 w B14 1 +9a30 w R15 1 +9a51 b E6 C3 2 +9a70 b B7 1 +9a76 w R8 1 +9a96 b P18 1 +9aa0 b C16 D17 2 +9ab1 b D2 1 +9b10 w N15 1 +9b17 b J17 1 +9b20 w P7 1 +9b27 w B7 1 +9b36 b G3 1 +9bb0 b R8 1 +9bb1 b P8 1 +9bb6 w B16 1 +9c20 b R4 1 +9c40 w L4 1 +9c77 b G17 1 +9c86 w Q1 1 +9ca1 w P16 1 +9cc7 w M6 1 +9cf1 b E6 1 +9cf7 b C13 1 +9d16 w O3 1 +9d31 b O6 N7 2 +9d36 w G4 1 +9d37 b C4 1 +9d51 w H3 1 +9d70 b Q6 1 +9da1 w P17 1 +9e80 w G7 1 +9e96 b P4 1 +9ee6 w C18 1 +9f27 b S4 1 +9f37 b E4 1 +9f56 w F4 1 +9fb1 b D12 1 +9fc6 w P14 Q13 O15 3 +9fd7 b O7 1 +9fd7 w G17 1 +9ff1 b R2 1 +a006 b S6 1 +a007 w F5 1 +a037 b B18 1 +a040 w P5 1 +a077 w R16 1 +a086 b R3 1 +a090 w F14 1 +a097 b R5 1 +a0c7 b Q3 1 +a107 b S16 1 +a156 w C3 1 +a170 b D1 1 +a171 b R16 1 +a181 w N3 1 +a1a0 w F14 1 +a1c6 w O17 1 +a1f1 b O15 1 +a1f1 w F2 1 +a201 b Q12 1 +a207 w O16 N15 2 +a221 w P4 Q5 2 +a226 b P18 1 +a267 b O15 1 +a280 b C15 1 +a287 b O5 1 +a296 w D3 1 +a2c7 w G4 H3 J3 3 +a2f0 b S4 R5 2 +a360 b H3 1 +a380 b Q18 1 +a3a1 w P17 D14 2 +a3a7 w F4 G4 2 +a3c1 b R17 1 +a420 b E5 1 +a440 b B6 1 +a446 b E6 1 +a460 b O5 1 +a461 b R12 1 +a491 b S6 1 +a4b0 w D1 1 +a4b1 b C14 1 +a4b6 w O4 1 +a4d0 w P2 1 +a4e1 w Q3 1 +a511 w S18 1 +a547 w B5 1 +a5a6 b C14 1 +a5b1 b F6 1 +a5c1 b F16 G16 2 +a5c1 w E5 1 +a5c7 b S7 1 +a631 b C5 1 +a636 b N3 1 +a651 w N5 1 +a656 b F15 1 +a657 b Q7 R3 2 +a677 b E5 1 +a690 b C14 1 +a6a7 w C4 1 +a706 w D17 1 +a716 b F17 1 +a730 w P4 1 +a770 b E3 1 +a777 b D4 1 +a786 w S17 1 +a7a6 b S17 1 +a7b7 b C3 1 +a7c0 w N3 1 +a7e1 w C4 1 +a7f0 b R13 1 +a7f7 w R4 1 +a807 w G15 1 +a816 w R17 1 +a837 w R17 1 +a8e0 w E6 1 +a8f6 w C14 1 +a911 b N16 1 +a917 b Q17 S16 2 +a9e1 b P3 1 +aa10 b F15 1 +aa40 w C14 1 +aa50 w E6 R7 2 +aa61 w R6 1 +aa66 b M3 1 +aa71 w B7 E8 2 +aa76 w R2 1 +aa91 b S2 1 +aac0 w F15 1 +aaf0 b C18 1 +ab07 b F18 1 +ab30 w L3 1 +ab77 w C2 1 +aba6 b F2 1 +abb6 b R18 1 +abc0 b C7 B5 2 +abd0 b C6 1 +abd0 w D6 C6 C7 F4 H3 H4 J4 7 +abe7 b F17 E16 2 +ac80 w H3 1 +acb0 w E6 1 +acb7 w B3 1 +acc1 w R17 1 +acd0 b O5 1 +ad00 w C16 1 +ad16 w O3 1 +ad21 b Q6 1 +ad26 b S4 1 +ade0 b N15 1 +ae10 b C13 1 +ae17 b S16 1 +ae30 w O17 1 +ae46 b S17 1 +ae57 b C3 1 +ae67 w F16 1 +aed0 w E16 G17 2 +aef1 b D17 1 +aef6 b C2 1 +aef7 b C13 1 +af06 w L2 1 +af07 w Q13 1 +af16 b D5 1 +af77 w R18 1 +af81 w Q3 1 +af86 b O17 Q16 2 +af90 w B7 1 +afa7 w S4 1 +afb6 b E4 C18 2 +afe0 b B15 1 +afe1 w G4 1 +b000 b M14 1 +b016 w Q17 1 +b036 b Q2 1 +b047 w R2 1 +b061 w D7 1 +b081 w C17 1 +b091 b D18 1 +b0a0 w P13 1 +b0b1 w P2 1 +b0c7 b C9 1 +b0e1 w P14 1 +b130 b C18 1 +b131 w G6 1 +b146 w M4 1 +b160 b Q5 1 +b186 b O4 1 +b1a1 w Q6 1 +b1c0 w E16 1 +b1c1 b E5 1 +b200 b R6 1 +b206 b E2 E17 2 +b291 w R18 1 +b296 b R3 Q4 2 +b2a0 b C14 1 +b2b0 w R17 1 +b310 b R12 1 +b317 w H5 1 +b331 b Q15 P14 2 +b346 w E14 1 +b376 w G3 E3 2 +b387 b Q11 1 +b3a1 w E6 1 +b401 b H16 1 +b406 w E13 1 +b416 b R9 1 +b427 w P17 1 +b431 w C12 1 +b450 b E2 D3 2 +b451 b E18 1 +b470 w P16 P13 2 +b486 w S17 1 +b4b1 w C2 1 +b4f1 b S3 1 +b517 w J4 1 +b560 w F13 1 +b561 b Q18 1 +b571 b P16 1 +b581 b G16 Q18 2 +b596 w F4 1 +b5b6 b O18 1 +b610 b R17 1 +b616 b P3 S4 2 +b640 w Q5 1 +b661 b R3 1 +b6e1 b D12 1 +b6f0 w Q16 1 +b716 b R17 1 +b761 w E17 1 +b770 w C13 E15 2 +b791 w D15 1 +b7d1 w D15 1 +b7e0 b D5 1 +b7e0 w D16 1 +b7f1 b C18 1 +b7f6 w E17 1 +b817 b H13 1 +b867 b D16 1 +b891 b P5 1 +b896 b Q6 Q7 2 +b8a7 b C5 1 +b8c0 b C5 1 +b8c1 w C3 1 +b8d0 b E5 1 +b8e1 w C2 1 +b8e6 w B6 1 +b8f0 b C3 1 +b8f1 w O2 1 +b936 w O16 1 +b940 b Q6 1 +b951 w F13 1 +b966 w E6 1 +b970 w P17 1 +b981 w N15 1 +b997 b C17 1 +b9b7 w C3 1 +ba07 b D1 1 +ba36 w F3 1 +ba51 b F4 1 +ba61 w S3 1 +ba87 b Q4 1 +bb30 b H16 1 +bb46 b E5 D4 2 +bb90 b M14 1 +bbb6 w B17 1 +bbb7 b F4 1 +bbe0 b P4 1 +bbf0 b A14 1 +bbf7 w N4 R5 O6 P5 4 +bc00 b R5 1 +bc26 w D2 1 +bc27 w S3 1 +bc31 b C16 1 +bc47 w D16 1 +bc51 w C3 1 +bc67 b O18 1 +bc70 w G16 D15 2 +bcc6 b R14 1 +bcc7 w Q14 1 +bce1 w F17 1 +bce7 b S2 1 +bce7 w E16 D15 2 +bcf1 b P9 1 +bd00 w R11 1 +bd20 b C3 1 +bd37 b S4 1 +bd60 b E16 1 +bd66 w G15 1 +bd90 b D6 1 +bda6 b C17 1 +bdb0 w C17 1 +bdf7 b O2 1 +be21 b E16 1 +be41 w A5 1 +be46 b B15 1 +be47 b L16 1 +be47 w R3 1 +be76 b Q14 1 +be77 w P7 1 +be80 b P5 1 +be81 w B17 G4 2 +be90 w P18 1 +beb1 b S2 1 +bed1 b F15 1 +bee6 b Q15 1 +bf00 w N6 1 +bf27 b S13 1 +bf56 w S2 1 +bfa0 w L17 E4 2 +bfb7 b G4 1 +bfd6 w Q4 1 +bfe0 b R15 1 +bff6 b T2 1 +c010 b F4 1 +c010 w F5 1 +c020 w Q15 1 +c067 w G2 1 +c080 b R1 1 +c086 w E5 1 +c0c7 w C17 1 +c0f0 w B15 1 +c0f6 w P5 1 +c101 w S13 1 +c116 w Q17 1 +c141 w B6 1 +c176 b E14 1 +c180 b D14 E14 2 +c180 w A14 1 +c196 b S12 1 +c1c0 b E3 1 +c221 w G17 1 +c250 b O14 1 +c280 w N2 1 +c290 w P16 1 +c291 w M5 1 +c2a6 b P13 1 +c2b6 b D13 1 +c2d1 w S2 1 +c2f1 w Q14 1 +c346 w H4 F3 2 +c356 b B16 1 +c356 w P5 1 +c366 b Q17 1 +c370 w Q14 1 +c381 w F12 1 +c3f6 b S3 1 +c447 b R2 1 +c456 w Q7 1 +c461 b O18 1 +c481 b B17 1 +c4a0 b E6 1 +c4f1 w Q5 1 +c506 b G18 1 +c517 w C13 1 +c520 w R12 1 +c560 w P3 1 +c576 w R3 Q4 R11 3 +c5b6 b Q17 1 +c5d6 b C18 1 +c5e0 w Q5 1 +c600 b Q1 1 +c600 w F2 1 +c651 w Q2 1 +c667 b N6 1 +c671 w S17 1 +c691 b P15 1 +c6c7 w S4 1 +c6f6 b E7 1 +c707 b S16 1 +c711 b C3 1 +c736 b M2 1 +c780 b R16 1 +c790 w M17 1 +c7d7 b Q16 1 +c7d7 w S3 1 +c7f1 w Q4 1 +c821 b N15 1 +c881 w C3 C4 F3 3 +c8d1 b E15 1 +c8e6 b Q2 1 +c900 w S15 1 +c907 b R16 1 +c917 b S6 1 +c927 b D13 1 +c980 w Q15 1 +c990 b Q5 1 +c996 b C18 O4 2 +c9a6 b D3 1 +c9b7 w S4 1 +ca21 w Q11 1 +ca27 b F5 1 +caa1 b M4 1 +cab6 b E17 1 +cad7 b C18 1 +cb30 w F8 1 +cb40 w Q11 1 +cb60 w H4 1 +cb90 w H16 1 +cbb6 b Q5 1 +cc07 b G2 1 +cc20 w E15 1 +cc26 w Q14 1 +cc47 w R6 1 +cc76 b E5 1 +ccb1 b E2 1 +ccd1 b R13 1 +ccd7 w R4 1 +ccf0 b C14 1 +cd40 b E14 1 +cd90 w E18 1 +cdb1 b D4 1 +cdd6 w Q12 1 +cde6 w B15 B18 2 +cdf7 w O15 O16 2 +ce01 b O17 1 +ce07 b R17 1 +ce40 b N5 1 +ce81 b L16 1 +ce87 w O8 1 +ce90 b G4 1 +ce90 w O17 1 +ceb1 w B4 1 +ced6 w D11 1 +cef1 w B16 1 +cef6 w O17 N16 2 +cf21 w R1 1 +cf47 w D8 H4 2 +cf50 b G16 1 +cf56 w R18 1 +cf57 b P4 P6 2 +cfb1 b P15 1 +cfc1 w B7 1 +cfd7 w S15 1 +cfe1 w O13 1 +d037 w Q7 1 +d0a6 b E2 1 +d0c0 w Q8 1 +d121 b R7 1 +d126 w R12 1 +d127 b P4 Q5 2 +d177 b C11 C13 2 +d1a0 b D5 M17 2 +d1a0 w E4 1 +d1d1 w S15 1 +d1e7 b S3 1 +d1f1 w D16 1 +d206 w P2 1 +d216 w R4 1 +d271 b C7 1 +d281 b F4 1 +d296 b P4 1 +d2a0 w P13 N16 2 +d2c1 b D3 C3 2 +d2f0 w D11 1 +d390 w Q17 1 +d3c1 w P3 1 +d3f1 b R3 1 +d417 w B4 1 +d420 w R13 1 +d451 w D18 B17 2 +d461 w F8 1 +d480 b E4 G3 2 +d4d6 b O18 1 +d4f1 b E3 1 +d501 b N15 1 +d516 b S15 S18 2 +d530 b P2 1 +d557 w Q1 1 +d560 w D17 C16 2 +d596 w B11 1 +d5a0 w M15 1 +d5a1 w D3 1 +d5f0 w E17 1 +d5f6 w H2 1 +d620 b R3 N4 2 +d640 b Q8 1 +d641 b P6 1 +d646 w M18 1 +d666 b C8 1 +d707 b P16 1 +d717 b C6 D5 2 +d726 b P14 O17 2 +d786 w C17 D16 2 +d7b0 b C3 1 +d7b1 w S6 1 +d7c7 b B2 1 +d7d6 w F17 F16 2 +d7f0 b N13 1 +d7f7 w E6 1 +d801 b S3 1 +d820 w D8 1 +d821 b D17 1 +d850 w B6 1 +d857 w M2 1 +d877 b P7 1 +d887 w R17 1 +d8b1 w D3 1 +d8c1 b Q16 C8 2 +d8e0 w S15 1 +d957 b N17 1 +d960 b L4 1 +d961 w R3 1 +d9e7 b Q2 1 +da01 b F4 1 +da06 w P14 O17 2 +da61 b P3 1 +da91 b D18 1 +dac0 w J3 1 +dac1 b S17 1 +dae0 b P6 1 +db00 b C2 1 +db01 w B13 E12 2 +db11 w B7 1 +db90 w P8 1 +db97 b R7 1 +dbd0 w M4 1 +dbe7 w S6 1 +dc16 b B3 1 +dc16 w P5 1 +dc20 b P16 1 +dc76 b S5 1 +dc81 w F2 1 +dcb7 b Q16 1 +dcc1 w S15 1 +dcf1 w Q14 1 +dd10 w F4 1 +dd56 b H3 1 +dd61 b Q3 1 +dd86 w D13 1 +dd90 w H4 1 +dd97 w Q3 1 +ddc0 b B4 1 +ddc1 b E18 1 +dde0 w S13 1 +ddf6 w C5 1 +de20 w S18 1 +de26 b R5 1 +de26 w R5 1 +de30 b F15 1 +de70 b C6 D4 C4 3 +de81 b Q6 1 +de86 b S3 1 +de86 w H5 1 +dea0 b Q12 1 +dee7 b C14 1 +df17 b C16 1 +df20 b Q4 1 +df26 w S11 1 +df36 b C12 1 +df51 w H4 1 +df81 b N18 1 +df90 b S6 1 +df97 b C17 1 +dfa0 b L4 1 +dfb0 b N4 1 +dfb7 b S17 1 +dfd6 w R9 1 +e007 w D4 1 +e027 b C14 1 +e067 w S17 1 +e0f7 w F16 1 +e127 w D18 1 +e141 b F6 1 +e156 w O14 B5 2 +e186 b O14 1 +e187 b E4 1 +e1a0 w G4 C4 2 +e1b0 b H15 1 +e1d0 b E18 D17 2 +e1d1 b S17 1 +e227 w Q5 1 +e237 w C16 E16 2 +e257 w N2 1 +e281 b O16 1 +e2b0 b F3 1 +e2d0 b C16 1 +e2d7 b B3 1 +e2e6 b O15 1 +e2f0 b G5 1 +e307 w S17 1 +e340 b O16 1 +e360 b R2 1 +e381 b D6 1 +e396 b T18 1 +e400 b P4 1 +e426 b D5 1 +e460 w T4 1 +e470 w F3 1 +e477 w A17 1 +e490 b P4 1 +e4b0 b F9 1 +e4c1 b R5 1 +e4e1 b R13 S12 2 +e4f0 b Q14 R14 R13 O16 M17 M16 L16 7 +e4f0 w R14 1 +e4f7 w P18 1 +e516 w F18 1 +e517 b C17 D17 C11 C12 B15 C16 6 +e527 b P7 1 +e540 w N5 1 +e551 b D14 1 +e556 b F17 1 +e581 b D15 1 +e590 w O5 1 +e591 w S18 1 +e596 w N12 1 +e5c6 w B4 Q7 2 +e606 w R5 1 +e610 w C11 1 +e617 b R8 1 +e6a7 w R5 1 +e6f0 w P7 1 +e726 b D4 1 +e737 w B16 C17 C16 H17 4 +e757 w S15 1 +e771 w D5 1 +e791 w E16 D15 2 +e7f0 w G14 1 +e7f1 b F16 1 +e837 w C2 1 +e851 w D5 1 +e861 b D4 1 +e861 w C5 C6 D5 E4 4 +e871 b C17 1 +e886 b C9 1 +e8d7 w D12 1 +e8e6 b O6 1 +e911 b J14 1 +e940 b H15 1 +e967 w Q16 1 +e976 b D12 1 +e987 b R17 1 +e991 b C15 1 +e9a0 b E16 1 +e9b6 w D5 1 +e9f7 b D12 1 +ea01 w R14 1 +ea27 w F15 F16 2 +ea30 b Q17 Q16 N17 P15 R17 5 +ea30 w O18 1 +ea60 b P13 1 +ea70 b E5 1 +ea86 w C4 1 +ead7 b G16 1 +eb20 w B3 1 +eb26 b M17 1 +eb30 w F5 1 +eb81 w S15 1 +eb86 b C3 1 +eb90 b P3 1 +eba0 w R16 Q16 O18 3 +ebf1 b S17 1 +ec16 w A2 1 +ec47 w O4 1 +ec77 b R14 1 +ec81 w B14 1 +ecf6 b C13 1 +ed37 w B16 1 +ed40 b D15 1 +ed60 w O15 1 +ed96 w C3 1 +edf7 b E17 1 +ee07 b B13 E12 2 +ee17 w Q3 1 +ee67 b C14 1 +ee86 w B15 E2 2 +eeb0 w Q3 1 +eeb7 b O5 1 +eec0 w D18 1 +eec7 w F17 1 +eed6 w F16 1 +eee0 b D15 1 +ef11 w N3 1 +ef71 w P5 1 +ef80 b G14 1 +efa1 b Q16 1 +efc6 b C17 1 +efd7 b Q17 1 +eff1 b Q5 1 +f001 b S14 1 +f006 w P3 1 +f016 b R18 1 +f017 b R2 1 +f076 w F16 C16 2 +f0c0 b E4 1 +f0c6 w M17 1 +f0f6 b O2 1 +f117 b P18 1 +f121 w R17 1 +f141 w D16 F16 R12 3 +f197 b P17 1 +f197 w C6 1 +f1b7 b R6 1 +f1c0 w D5 1 +f1c1 b E4 C3 G3 D6 D8 5 +f1c1 w D5 C4 2 +f1c6 b D13 1 +f1f0 w Q7 1 +f200 b O16 1 +f236 w P2 1 +f240 w G15 1 +f250 w P15 1 +f2d1 b E12 1 +f2e7 w R17 1 +f2f0 b T5 1 +f2f7 b S16 1 +f307 w R4 1 +f381 w D4 1 +f3a6 b P3 1 +f3c0 b L3 1 +f3c7 w Q16 1 +f3e1 w H7 1 +f3e6 w C4 1 +f3f1 w L2 1 +f407 w B6 1 +f450 b D12 1 +f467 b O19 1 +f467 w E5 1 +f481 b E15 1 +f4b6 w F18 1 +f4c1 w G16 1 +f4e1 w B1 1 +f511 b R18 1 +f537 w D15 1 +f571 b E16 1 +f576 w P16 1 +f586 b O3 1 +f5b0 b P2 1 +f5c1 w P7 1 +f5d1 b N17 1 +f601 w M3 1 +f650 w C3 1 +f656 b D17 1 +f671 b H16 1 +f687 w D16 1 +f697 b F17 1 +f6c0 b C4 1 +f6c7 w F15 1 +f6e7 w Q3 1 +f706 w S18 1 +f726 w E16 1 +f731 b O15 1 +f741 w F2 1 +f767 b Q13 1 +f767 w R5 1 +f786 w G4 1 +f797 w P14 Q14 2 +f7f6 b S15 1 +f831 w F18 1 +f846 b O7 F4 2 +f887 w M5 R13 2 +f8a0 b C11 1 +f8a1 b N17 1 +f8d0 b F3 1 +f921 b R2 R9 2 +f940 b O17 1 +f946 b P5 1 +f960 b R3 1 +f961 b H3 1 +f9a0 w C3 E4 G3 3 +f9a6 w O16 1 +f9d0 w M16 1 +f9d1 b C3 E2 G4 3 +fa27 b C5 1 +fa37 b P4 1 +fa40 w P17 1 +fa41 b Q5 1 +fa46 b G14 1 +fa70 b E12 1 +fa87 b G4 C3 2 +fac6 w E4 1 +fad7 b Q18 1 +faf0 b O15 1 +fb66 w R16 1 +fb80 w D13 1 +fb86 b M1 1 +fb97 b C3 1 +fc06 w R9 1 +fc21 b D4 1 +fc50 w Q12 1 +fc51 b C3 1 +fc56 w C2 1 +fc71 b S17 1 +fca1 w B5 1 +fcb0 b G15 D13 2 +fd36 b R5 1 +fda7 b Q17 1 +fdb6 w B5 1 +fdc0 b D17 1 +fde6 b E5 D4 2 +fe01 w B4 1 +fe07 b S3 1 +fe36 w S5 1 +fe46 w F13 1 +fe57 w B5 1 +fea7 b R16 1 +feb7 b D5 1 +fec6 b Q17 1 +fed1 b Q6 P7 2 +ff20 b D2 1 +ff57 b C3 D8 2 +ff87 w B6 1 +fff1 b C15 1 +10010 b B15 D15 2 +10017 b O3 1 +10050 w O12 1 +10077 b E5 1 +10097 b C6 B6 2 +10097 w C16 1 +100b6 b D6 1 +100d6 w D15 1 +100e7 b E15 1 +10116 b D6 1 +10171 b B17 1 +10191 w P17 1 +101a1 w D16 1 +101b7 w R2 1 +101f7 w O2 1 +10247 b D15 1 +10247 w R6 1 +10261 b D4 1 +10276 b R8 1 +102c7 w B15 1 +102e6 b R5 1 +10310 b H5 1 +10321 w Q15 1 +10330 b P2 1 +10337 w A19 1 +10356 b C6 1 +10356 w C8 1 +10367 b P9 Q3 2 +10387 w F4 1 +103b7 w R2 1 +103c6 w P17 1 +10401 b O16 1 +10420 b F16 F17 G17 D14 C12 D12 D11 7 +10420 w F17 1 +10427 w O12 1 +10436 b Q2 1 +10441 b B17 1 +10446 w S2 1 +10447 b C4 1 +10470 w Q14 1 +10477 w O4 1 +10486 w Q18 1 +10497 w F16 D5 2 +10507 w E3 1 +10546 b S16 1 +10596 b M16 1 +105b0 w S3 1 +105c0 b P17 1 +105f1 w N4 1 +105f6 b C9 1 +10606 b A18 1 +10610 b C7 1 +10676 b E2 1 +10677 b L18 1 +10686 b M4 1 +106b0 b N17 1 +106e6 b B18 1 +10720 w H7 1 +10751 b G13 1 +10757 w G17 1 +10771 b P8 1 +107a1 b C13 1 +107a7 w F4 1 +107c0 b C15 1 +10826 b F18 1 +10847 w O6 1 +10851 w C4 1 +10861 b R3 1 +10876 w C6 1 +10887 b C16 1 +108c6 b D6 1 +10921 b S7 1 +10957 w C13 1 +10991 b O16 1 +109b1 w F18 1 +109b6 w S13 1 +109d0 b L7 1 +10a31 b C3 1 +10a70 b E6 1 +10ae0 b C8 1 +10ae6 b Q5 1 +10ae7 w B3 1 +10b21 b B5 1 +10b27 w N5 1 +10b41 w E18 1 +10b56 b Q3 1 +10b71 w G2 1 +10b81 w Q13 1 +10bd7 w Q12 1 +10bf7 w B2 1 +10c40 w Q11 1 +10c51 b O13 1 +10c51 w F5 1 +10cb6 b P17 1 +10cb7 b G17 1 +10cc1 b M16 1 +10d26 b N16 1 +10d46 w S4 1 +10d97 b R18 1 +10da0 w F14 1 +10da7 w C4 1 +10db1 b E6 1 +10db1 w C17 D17 C14 3 +10dd7 w R2 1 +10de0 w O5 1 +10de1 w R6 1 +10e06 w N16 1 +10e20 b R4 1 +10e86 w N16 1 +10ea0 w C16 1 +10eb0 b M3 1 +10ed6 b Q9 O2 2 +10f07 w B6 1 +10ff0 b Q15 1 +11016 w D9 1 +11026 w P3 1 +11030 w C15 1 +11041 w Q11 1 +11070 b R16 1 +11071 b G3 1 +11076 w Q3 1 +11086 b O15 1 +110a6 b D13 1 +110b0 w Q9 1 +110b1 w B15 1 +11107 b G17 1 +11116 b D8 1 +11120 b L17 1 +11141 w G18 1 +11147 w B3 1 +11161 w B18 1 +111a6 b D5 1 +111c1 b O16 1 +111d6 b C12 1 +111e1 b D5 1 +111e7 b C16 1 +11201 b M15 1 +11201 w B14 1 +11226 w C11 1 +11251 w O17 1 +11257 b Q12 1 +11261 w S4 1 +11266 b R15 1 +11277 b R7 1 +11280 w P6 1 +112f1 b S19 1 +11377 w Q17 1 +11390 b P4 1 +11391 b B18 1 +113d7 w N2 1 +11411 b S3 1 +11437 w S17 1 +11440 b D18 1 +11461 w S14 1 +11470 w H5 1 +11471 b N4 1 +11477 b N2 1 +11486 b R18 1 +11491 w Q3 P4 R3 3 +114f1 b M6 1 +11526 w M14 1 +11546 b Q14 1 +11580 w C16 1 +115d6 b N14 1 +115d7 b H6 1 +11621 w J4 1 +11640 w O16 1 +11647 w O18 1 +11677 b Q3 1 +11687 b G18 1 +116b0 b L3 1 +116c7 w R13 1 +116d0 b F13 1 +116f6 w S4 1 +11721 b Q18 1 +11731 b Q12 1 +11747 w B4 C3 2 +11760 b H17 1 +117b7 w E3 1 +117c6 w F14 1 +117f6 w E16 D15 2 +11837 w B16 1 +11857 b Q18 1 +11857 w Q6 1 +118b1 b D18 1 +118b7 w F15 C17 2 +11901 b R13 1 +11906 b L17 1 +11921 b S15 1 +11931 b Q7 R8 R9 3 +11947 b Q13 1 +11961 b E2 1 +11981 b Q8 1 +11990 b M4 1 +11991 w G15 1 +119c0 b D2 1 +119e1 w C5 1 +11a01 w T5 1 +11a10 w D13 1 +11a16 w D3 1 +11a21 b D15 1 +11a21 w G6 1 +11a30 b Q14 1 +11a50 w N5 1 +11b21 w C5 1 +11b40 b J17 1 +11b86 b Q15 1 +11b97 b R3 1 +11bb6 w R17 1 +11bd0 b Q12 1 +11bf1 w E17 1 +11bf6 w S18 1 +11c06 w O14 1 +11c11 w O3 P4 2 +11c20 w E15 1 +11c80 b C15 1 +11c91 b E4 1 +11ca1 b S2 1 +11cd6 b P5 Q4 2 +11cd7 w C15 1 +11ce1 w O3 P12 2 +11ce6 b G4 1 +11d77 b Q9 1 +11dd6 w Q15 1 +11dd7 w E16 1 +11de0 b R18 1 +11de1 b D16 1 +11e01 w D5 1 +11e17 w Q6 1 +11e61 b F5 F4 2 +11ea1 b S16 1 +11eb6 b F13 1 +11ed0 w C3 1 +11ee1 w E3 1 +11ef7 w D9 1 +11f27 b C6 1 +11f30 w C13 B15 2 +11f66 w Q3 1 +11f81 w Q4 Q6 2 +11fc7 b E6 1 +11fd6 b Q3 1 +11fd6 w R8 1 +11ff0 b N5 1 +11ff7 b E18 1 +12010 b H3 1 +12051 b D17 1 +120d1 b Q14 1 +120e0 b B12 1 +120e1 b R3 P6 O3 O4 4 +12106 w S17 1 +12111 w C16 D15 C17 3 +12116 w G14 D14 2 +12130 w D2 1 +12136 w F15 E15 2 +12141 w R6 O3 Q6 O4 Q7 N4 R7 N3 R9 L3 10 +12161 b E14 1 +12177 b D9 1 +12190 b C11 1 +121a0 b S2 1 +121f0 b B5 1 +12251 b P6 1 +12260 w Q2 1 +12267 b B13 1 +12291 b Q2 1 +122a0 b C3 E3 2 +122d7 b C16 1 +122e6 w O17 1 +12397 w D14 1 +123b7 w P11 P13 2 +123d0 w O13 1 +12406 w C18 1 +12426 w Q13 R13 R17 3 +12436 w R2 1 +12491 b Q4 1 +124c0 w F6 1 +124d0 b P5 1 +124d6 w S17 1 +124f6 b D18 1 +12507 b D3 1 +12550 b E18 1 +12556 w G3 1 +12580 w D7 1 +125a0 w S16 Q16 2 +125e1 b S17 1 +12671 w H3 1 +126a7 b G17 1 +126c0 b D7 1 +126d7 w M3 1 +126f7 w P7 H18 2 +12706 w N4 1 +12717 b Q11 1 +12737 b G2 1 +12750 w S5 R3 O4 O3 4 +12786 w D14 D15 2 +12797 b C16 1 +127c6 b S6 1 +127c7 b D13 1 +127e1 w R9 R7 2 +127f0 b O7 1 +12800 b H4 1 +12801 b C7 B8 2 +12811 w R6 S6 2 +12836 w C18 1 +12856 w R15 R17 2 +12870 b Q4 1 +12871 b R15 1 +12886 w Q5 1 +128a7 b D16 1 +128b7 w B4 1 +128f6 w F16 1 +12921 w E12 1 +12937 b L4 1 +12941 w P16 1 +12961 w N17 1 +12987 w F16 1 +12997 w B13 1 +12a00 w D16 1 +12a07 w F5 1 +12a10 b Q17 1 +12a10 w F17 1 +12a17 w D16 1 +12a81 b C18 C11 2 +12a86 b N4 1 +12a90 w R6 1 +12a96 b E8 1 +12ab0 w Q3 1 +12ab6 b N2 1 +12b26 b O16 1 +12b27 b O18 C9 2 +12b40 b R16 Q17 2 +12b71 w Q7 1 +12b77 w N3 1 +12b80 w C4 1 +12b81 b C17 1 +12b87 b Q13 O14 2 +12c10 b E7 1 +12c60 b S2 1 +12c81 w P16 1 +12c86 w C17 1 +12c91 b Q5 P4 2 +12ca1 w Q2 1 +12cd6 b P15 1 +12ce7 w R18 1 +12d26 w D4 1 +12d61 b R15 1 +12d66 w C6 1 +12d76 b Q13 1 +12d80 w S5 1 +12d87 w M15 1 +12da7 w B6 1 +12df7 w E5 1 +12ec0 w Q13 Q17 2 +12ed7 b B6 1 +12ee7 b C3 F4 D4 D5 E6 E5 G5 G4 C6 C7 D7 11 +12ee7 w D3 1 +12f00 b O15 1 +12f16 b C17 1 +12f17 b P15 1 +12f70 w M3 1 +12f90 w B4 1 +12fb6 b S6 1 +12fb7 w B4 1 +12fc1 b P15 1 +12fc6 b B6 1 +12fe0 w R2 1 +12ff6 w B5 1 +13021 w Q4 1 +13086 b R4 Q5 Q9 3 +130c6 b R16 1 +130e1 b Q2 1 +130e6 w N3 1 +13126 w Q7 1 +13136 w B4 1 +13161 b O7 1 +13176 b R15 1 +13180 b C3 1 +131b7 w D17 1 +131c1 w F4 C3 2 +131e1 b R6 1 +131e1 w Q6 P5 2 +131f7 w J5 1 +13216 b D17 E18 2 +13226 w R14 1 +13246 b D4 1 +13296 w O15 1 +132f0 b F13 1 +13331 w Q2 1 +13396 b D2 1 +133a0 w B3 1 +133e7 b S5 1 +13427 w O16 1 +13487 b R17 1 +134e0 w S14 1 +13511 b J18 1 +13530 w Q3 R4 2 +13531 w O2 1 +135c6 w O18 1 +135f7 b Q5 1 +13676 b O2 1 +136c6 w H4 1 +136e1 w O4 1 +13710 b N13 1 +13737 b Q2 1 +13756 w Q4 1 +13760 b R15 1 +13781 b C16 1 +137a1 w B13 1 +137a7 b H15 1 +137e0 w Q7 1 +13807 b B14 1 +13827 b E14 1 +13841 w O14 1 +138b7 b P3 1 +138f7 w R5 1 +13916 w P2 1 +13930 w Q5 1 +13936 b Q14 1 +13957 w D3 1 +13971 b Q3 1 +13980 w D14 E16 2 +139d1 w B13 1 +139e1 w R14 1 +139f1 w N15 1 +13a16 b B4 1 +13a40 b D18 1 +13a41 w C4 D5 C3 3 +13a50 w E13 1 +13a57 b F4 1 +13a66 w T16 1 +13ad6 w R13 1 +13b00 w F19 1 +13b07 w P17 1 +13b10 b E3 B3 2 +13b20 w J3 1 +13b37 b T15 1 +13b50 w C6 1 +13b61 w L16 1 +13ba1 w J4 1 +13bd1 w F16 1 +13bd6 b C1 1 +13c20 w F3 1 +13c30 w O5 R5 2 +13c37 b O7 1 +13c40 w R19 1 +13c70 b G17 1 +13c70 w Q9 1 +13c77 b D6 E5 2 +13c87 w D13 1 +13cd7 b Q18 1 +13cd7 w S3 1 +13d10 w S5 1 +13d70 w E13 1 +13d80 b L14 1 +13d86 b P14 1 +13da1 w R8 1 +13db6 w O16 1 +13de7 w D2 C3 D3 C8 4 +13e20 b R7 1 +13e27 w B16 1 +13e56 w E5 1 +13ea6 w C14 1 +13f06 b F16 1 +13f40 b A3 1 +13f46 b E12 1 +13f57 w T4 1 +13f76 b Q2 1 +13f90 b E7 1 +13fc6 w C15 1 +13fe0 w G4 1 +13ff1 b O2 1 +14006 w Q4 1 +14046 b C8 1 +14077 w D16 1 +140b0 w P9 1 +140c7 b Q6 1 +14107 b C15 1 +14167 w P2 1 +14191 b F4 1 +14191 w H3 1 +14220 b F5 1 +14287 w P2 1 +14296 w R16 1 +142b6 w N16 1 +142d7 w N2 1 +142f1 b C7 1 +142f6 w G4 1 +142f7 b D6 1 +142f7 w Q2 1 +14320 b S18 1 +14326 b E7 R8 2 +14336 b R17 1 +14337 w Q18 R17 2 +14347 b P14 O14 P17 O17 4 +14356 w G17 1 +14357 w S15 1 +14377 w O4 1 +143a0 w H15 1 +14400 w S15 1 +14401 w G17 1 +14437 b Q17 1 +14446 b O4 1 +14451 b F15 1 +14471 w S4 1 +14476 b G15 G14 2 +14481 b D3 1 +144c6 w D8 1 +144f0 b R6 1 +14517 w F4 1 +14527 b F16 1 +14527 w Q15 1 +14551 b Q2 1 +14581 w O5 1 +145c1 b B18 1 +145d7 b F14 1 +145f0 b C4 1 +14620 w P7 1 +14661 b B17 H17 2 +14680 w G3 1 +14691 w E3 1 +146a7 w E5 1 +146b6 b O13 1 +14700 w O2 1 +14716 b Q17 1 +14771 w C17 1 +147a7 b D11 1 +147c0 w C4 1 +147c7 w B3 1 +14826 b C17 1 +14831 w O4 1 +14887 w N18 1 +14891 w F15 F14 C15 C14 4 +148d6 b R12 1 +148f0 b E15 1 +148f0 w E16 1 +148f6 b G2 1 +14917 b H3 1 +14957 b D5 1 +14961 w C18 1 +14967 b B18 1 +14970 b B7 1 +14996 b G15 1 +149d6 w P7 1 +14a37 b Q5 Q6 2 +14a67 w N17 1 +14b36 b R4 1 +14b61 w B18 1 +14b97 b R7 1 +14bb6 b Q18 1 +14bd6 w O2 1 +14c00 w P16 1 +14c07 b F5 D6 2 +14c21 w Q7 R3 2 +14c27 b C4 1 +14c51 w Q13 1 +14c60 b P6 1 +14c70 w C8 1 +14c96 w T4 1 +14cb7 b O13 1 +14ce1 b S4 R3 R4 M3 4 +14ce6 b R3 1 +14ce6 w J3 1 +14cf1 b S9 1 +14d17 b B17 1 +14d51 b O15 R17 2 +14d56 b N3 1 +14d56 w F16 G16 2 +14d71 w R7 1 +14d97 b Q14 1 +14da1 w C14 1 +14dc7 b E17 F17 E16 D15 4 +14dc7 w D16 1 +14de6 b E15 1 +14e71 w Q4 1 +14f11 b F2 1 +14f16 b E16 1 +14f20 w D12 1 +14f60 w C7 1 +14f66 b F3 D4 2 +15007 w P13 1 +15016 w C7 1 +15080 b P4 1 +15091 b C15 1 +15097 w E18 1 +150c0 b B14 1 +150c7 w F4 1 +15140 w R3 1 +15141 b D14 R1 2 +15147 w C2 1 +15151 b N3 1 +15156 w F16 1 +15186 w C3 1 +151d1 w O18 1 +151e0 w P15 1 +15237 w E16 1 +15260 w F16 1 +15267 w M14 1 +15270 b Q16 1 +15296 b C11 1 +152b0 w N4 1 +152b7 w L14 1 +152d7 b H5 1 +152f0 w Q15 1 +15301 b R5 N2 2 +15317 b E16 O17 O18 3 +15326 b E4 1 +15347 b R3 1 +15361 b Q15 1 +15371 b G2 1 +15377 b O15 1 +15381 w R5 S5 Q4 3 +15397 w G15 1 +153b6 w D2 1 +153c0 w D5 1 +153e1 b S15 1 +15406 w F4 G4 2 +15411 b B6 1 +15417 w P4 1 +15451 b B9 1 +15456 b D11 1 +15476 w B15 1 +15477 b F15 D14 2 +154a0 b O7 1 +154a6 w R6 1 +154a7 w S4 1 +154d1 w F3 1 +154d7 b F16 1 +154f7 w P12 1 +15516 w B2 1 +15527 w M14 1 +15540 w A5 1 +15551 w D7 1 +15567 b S14 1 +155c6 b R5 1 +155c7 b N16 1 +155d6 w O16 N16 2 +155e7 b C4 1 +155f6 w M19 1 +155f7 w B17 1 +15617 w D4 1 +15621 w B17 1 +15630 w D7 1 +15660 b R7 1 +15671 b Q12 1 +15696 w R12 1 +156a1 w E5 1 +156a7 b E18 1 +156d7 b D2 1 +156e0 b O17 1 +15741 w O16 1 +15767 b N18 1 +15776 w R7 R5 2 +15777 b R5 D2 2 +15796 w N6 1 +157a0 w D13 1 +157b0 w N15 1 +157b1 b P17 1 +157d1 b P6 1 +157d6 b Q14 Q15 2 +157e1 w Q7 1 +15801 w P14 1 +15807 w C7 1 +15810 w F16 1 +15816 b C8 1 +15847 b O18 1 +15856 b D7 1 +15890 w O4 1 +158c7 b F5 C3 O3 3 +158f1 w P1 1 +15916 b R17 1 +15976 w B4 1 +15981 w C18 1 +15986 w S3 1 +159d7 w Q16 1 +15a00 w P16 1 +15a16 w D6 D5 2 +15a31 w G4 1 +15a81 b F15 1 +15ac0 w G3 1 +15ac7 w E14 1 +15ad1 b O2 1 +15ad6 b F4 1 +15af1 w B15 1 +15b06 b F18 1 +15b17 w R2 1 +15b21 w C8 1 +15b50 b D6 C3 2 +15b50 w D7 1 +15b86 b D2 1 +15ba6 w F18 1 +15ba7 b S14 1 +15bc6 w P2 1 +15bd1 w D7 1 +15bd6 b D15 1 +15c80 b E4 E3 2 +15cf0 b G17 1 +15d11 b R3 1 +15d60 b C17 1 +15d71 b D5 1 +15d91 b G2 1 +15db6 b S2 1 +15de6 w R5 1 +15e01 w S4 1 +15e36 w E15 1 +15e47 w Q17 1 +15e51 w Q11 1 +15e67 b S13 1 +15e87 w Q2 1 +15e90 w D8 1 +15e91 w D16 1 +15ed1 b C5 1 +15ee1 w R15 1 +15ee6 w S14 1 +15ee7 b B4 1 +15ef7 b O14 1 +15f16 b F14 1 +15f50 b B18 1 +15f77 b J4 1 +15f87 w Q7 1 +15fc1 w N16 1 +16030 w E17 1 +16050 b R16 1 +16060 w Q15 1 +160c1 b O12 1 +160c6 b S8 1 +160c6 w M4 1 +160d0 b P16 P17 2 +160d6 b B4 1 +16146 w H3 1 +16151 w O3 1 +16167 b R3 1 +16190 b R7 1 +16197 w S1 1 +161c0 w C5 1 +161f6 b D13 1 +16236 b B4 1 +16236 w C14 1 +16241 w C16 Q3 2 +16247 b G16 F16 2 +16247 w F16 1 +16280 b D14 1 +16286 b F4 1 +162a1 b E17 1 +162a6 b B3 1 +162d0 w P17 1 +16321 w F17 1 +16336 b Q7 1 +16361 b S4 1 +163c7 w P5 O5 R3 N3 4 +163f1 w S15 P5 2 +16446 w Q14 1 +16457 b P6 1 +164a7 b Q15 N17 2 +164b0 b H15 1 +164c7 w B4 1 +164e0 w M16 1 +164f1 b E16 1 +16541 w C13 1 +16547 w R17 1 +16550 w R13 1 +165a1 b F6 G7 2 +16607 b D16 1 +16636 b P14 1 +16676 b R7 1 +166a0 b E15 1 +166a6 w Q7 1 +166c1 b C15 1 +16740 w J6 1 +16750 w B19 1 +16761 w R3 1 +16770 w C9 1 +16771 w D2 1 +16781 w O5 R3 2 +16791 b F5 1 +167c7 b D13 1 +16820 b B2 1 +16821 b O4 1 +16831 b F4 G5 2 +16831 w F3 1 +16836 w P17 1 +16847 w E18 1 +16896 b S18 1 +168a6 b B5 1 +168c6 b P5 1 +168c7 w R7 1 +168e0 w R5 1 +168f7 b P7 1 +16916 b R4 1 +16937 b H18 1 +16950 b E2 1 +16961 w B3 D17 2 +16966 w R8 1 +169f6 b B6 1 +16a01 w S2 1 +16a17 b N18 1 +16a20 w R5 1 +16a30 w D14 1 +16a46 b P4 1 +16a50 w E6 1 +16a57 b E9 D3 2 +16a61 w C5 1 +16a71 b M17 1 +16a80 b T3 1 +16ad0 w N14 1 +16ad7 b E7 1 +16b51 b R15 O17 2 +16b51 w P18 1 +16b71 w B17 1 +16b86 w R18 1 +16b91 w B17 1 +16bc0 b F8 1 +16be7 w C16 1 +16c00 b B5 1 +16c01 b P2 1 +16c27 w Q17 1 +16c41 w B2 1 +16c46 w G4 1 +16c57 b J2 1 +16c70 w D6 1 +16c87 w R6 1 +16ca0 w B14 1 +16ce0 w S2 1 +16cf1 w O2 1 +16d10 w E8 1 +16d26 w G4 1 +16d30 b G16 1 +16d51 b M14 1 +16d56 b P17 1 +16d57 w C4 1 +16d80 w Q4 1 +16da0 b Q14 1 +16db1 w B19 1 +16dd1 b D18 1 +16de7 w Q16 1 +16df7 b N4 1 +16e21 b R16 1 +16e21 w D4 1 +16e30 w D14 1 +16e86 b C6 1 +16ef1 b N17 1 +16f00 b C12 1 +16f07 b E2 1 +16f16 b R18 1 +16f26 w E15 1 +16f50 b R5 1 +16f51 b Q4 1 +16f60 b O15 1 +16f70 w E3 1 +16f96 w Q5 P4 2 +16fd7 b D14 1 +16fd7 w B12 1 +16ff0 b R17 1 +17007 b F2 1 +17020 w Q4 1 +17021 w S12 1 +17027 b M3 1 +17041 w F18 1 +17047 w F16 1 +17057 b R13 1 +17066 b R3 1 +17067 b E8 1 +17091 w D2 1 +170a6 b E16 D15 2 +170a7 w C16 1 +170b1 b R15 1 +170d0 b N6 1 +17137 w C2 1 +17156 w C6 1 +17180 b P15 1 +17190 b N17 1 +171a0 b C8 1 +171e6 b P2 1 +171e7 w Q15 1 +171f6 w E5 1 +17207 w R4 1 +17226 b C15 1 +17237 b Q16 O2 2 +17241 w R7 1 +17261 b P3 1 +17281 w C2 1 +172c1 b R4 1 +172d6 w F18 1 +172e0 b F3 1 +17301 b E5 1 +17301 w H17 1 +17320 w G8 1 +17346 w R12 1 +17361 w C16 D18 2 +17366 w P16 Q15 2 +17380 w Q18 1 +173b7 w E5 1 +173c1 b C4 1 +173c1 w E14 1 +173d0 b D6 1 +17411 b B5 1 +17411 w O18 1 +17467 w B16 1 +17471 w E2 1 +17496 w F14 1 +174c6 w E17 1 +174d7 w O14 1 +174f0 b E15 1 +174f7 b Q4 Q6 Q18 3 +17500 w O9 1 +17537 b Q14 1 +175a1 w C14 1 +175a6 b R17 1 +175d0 w B5 1 +17606 b P5 Q4 2 +17610 b M15 1 +17697 b P13 1 +17707 b C14 1 +17710 w R17 1 +17747 w P7 1 +17766 b Q6 Q3 2 +17786 w S2 1 +177b6 w S12 1 +177c7 w R2 1 +177d6 b L3 1 +17841 b H16 1 +17886 b Q17 1 +17890 w R3 1 +178a1 w H16 1 +178b1 b O15 1 +178c6 w D14 1 +178f1 b R17 O15 R14 Q14 4 +17906 w O13 1 +17930 w C6 1 +17990 w M5 M6 2 +17991 w Q18 1 +179d6 b J2 1 +179e0 b R13 1 +17a00 b F3 1 +17a10 b N15 Q13 2 +17a11 w B3 1 +17a16 w R17 1 +17a36 w B5 1 +17a57 b N18 1 +17a66 w R13 1 +17a97 b F18 1 +17aa6 b Q6 1 +17ab7 b Q3 1 +17b00 b E3 1 +17b20 w R13 1 +17b31 w M16 1 +17b51 b O14 1 +17b86 b F16 1 +17b86 w D2 1 +17ba0 w M16 1 +17be1 w P3 B13 2 +17bf6 b H1 1 +17c07 w R3 1 +17c10 b F15 1 +17c60 b S17 1 +17c71 w Q15 1 +17c80 w B6 1 +17d27 w S17 L17 2 +17d37 w R13 1 +17d60 b N14 1 +17d66 w C16 1 +17d67 w Q9 1 +17d97 b C13 1 +17db1 w C16 1 +17dc0 w D15 1 +17df7 b R17 1 +17ec0 b D17 1 +17f61 b C3 1 +17f76 w Q6 1 +17f91 b E3 1 +17fb7 w Q8 1 +17fc6 w N3 1 +17fe7 b F3 1 +18010 w D2 1 +18036 w F17 1 +18051 w E14 1 +18070 b O12 1 +18081 b B13 1 +180b7 w R16 R17 2 +180e1 w C17 1 +180e7 b G4 1 +18101 b E4 1 +18116 b E4 1 +18120 b E1 1 +18127 w P6 1 +18140 w G2 1 +18171 b S4 1 +18177 b R12 1 +181e7 w C16 C17 2 +18261 b E8 1 +18280 w D16 1 +182a7 b R15 1 +18311 w S3 1 +18337 b S17 1 +18351 w B7 1 +18356 w Q6 1 +183a0 w C15 1 +183d1 w H17 1 +18421 b R7 1 +18437 b Q6 1 +18441 b Q16 1 +184a7 b F15 1 +184b6 b B4 1 +184c7 b D13 1 +184f0 b N9 Q15 2 +184f1 w Q3 1 +184f6 w P15 1 +184f7 w B3 1 +18500 w F15 1 +18517 w B17 1 +18530 w R2 1 +18546 b R7 L3 2 +18561 b Q17 1 +18577 b D18 1 +185a1 w O16 P15 2 +185e0 b G4 1 +185e1 b E3 1 +185e6 w P6 1 +18610 b R17 O15 2 +18687 b N18 1 +18691 b O18 1 +186a6 b G16 1 +186a7 w R17 1 +186b1 w E3 1 +186b6 b C3 1 +186d1 w S16 1 +186e1 b B16 1 +18730 b Q2 O4 2 +18736 w G3 1 +18796 b D15 1 +187a7 b F1 1 +187d6 w O2 1 +187e0 w S7 1 +18840 b G16 1 +18841 w S16 1 +188c0 b C15 1 +18926 w E12 1 +18941 w R12 S15 S16 3 +189a1 w Q13 1 +189b7 b O15 1 +18a11 b B3 B6 O4 N5 4 +18a20 b P8 1 +18a67 w J4 1 +18a71 w B9 1 +18a81 w P14 1 +18ac6 w R5 1 +18ac7 w E14 1 +18ae0 w N4 1 +18af6 w O17 1 +18b11 b R3 1 +18b37 w P4 1 +18b40 w L17 1 +18b46 w R7 1 +18b61 b P5 1 +18b70 b D3 1 +18b71 w O4 1 +18b76 w Q8 1 +18b81 w O7 1 +18bb0 b C7 1 +18bd1 b B16 1 +18be6 b M4 1 +18c16 b F13 1 +18c16 w O16 1 +18c47 w H14 1 +18c76 b S3 R12 2 +18cd0 w P16 1 +18d47 w S17 1 +18d76 b F16 1 +18d77 b R8 1 +18da1 w E5 1 +18e11 w B4 1 +18e16 w L4 1 +18e41 w N2 1 +18e60 b P15 1 +18e87 w G3 1 +18ea7 b R9 1 +18ec1 w M4 1 +18ec6 w Q3 1 +18f37 b N6 1 +18f46 w D15 1 +18f57 b S17 1 +18f76 b E17 1 +18f80 w Q3 1 +18f86 w D8 1 +18f91 b S4 1 +18fa7 b S3 1 +18fc0 w C9 1 +18fc6 b G3 1 +18ff1 w R14 1 +18ff6 b E15 1 +19006 w G15 1 +190f6 b O17 1 +19101 b E7 1 +19107 b B16 C18 2 +19137 w O16 1 +19151 w Q16 1 +19156 b F16 G16 2 +19157 b D6 1 +19167 b R16 1 +19190 b S6 1 +19191 w B14 1 +191b0 w P5 1 +191b1 w R17 1 +19200 w N15 1 +19260 b R15 1 +19287 w D5 E6 2 +19297 b F5 D3 2 +19317 w C17 1 +19336 b B17 1 +19350 w Q7 1 +19380 w B5 1 +193a0 b N16 1 +193d6 b Q7 1 +193d7 w R3 1 +19411 w Q14 1 +19456 w N3 P2 2 +19457 w D16 P18 2 +19460 b B13 1 +19466 w P2 1 +19467 w D18 1 +19480 b E13 1 +19481 b B15 1 +194d6 w P16 1 +194e0 w C15 1 +19500 w E13 1 +19510 w P5 1 +19580 b S3 1 +195b1 b G16 H17 J17 3 +19641 w P13 1 +19650 b D14 D13 2 +19661 w O4 R3 2 +19676 w S17 1 +19677 w R18 1 +19690 b F2 Q19 2 +196f0 w D6 1 +19700 w M4 1 +19730 b Q17 1 +19781 w S18 1 +19790 w Q15 1 +197a0 b E16 1 +197b6 w A18 1 +197c1 w P15 1 +197f6 w R17 1 +19820 w B2 1 +19876 b C16 1 +19880 b P16 R11 O17 N17 M17 N16 O16 7 +19880 w P16 O16 R13 R12 R11 Q13 Q12 Q11 8 +19896 w H3 1 +198a7 b H15 Q11 2 +198c7 b O13 1 +198f7 b P3 1 +199c0 b F18 P4 2 +199c1 b R4 1 +199d1 w E2 1 +19a10 w Q5 1 +19a16 w O4 1 +19a20 b E7 1 +19a21 w G18 1 +19aa0 b D15 1 +19ab1 b D6 1 +19ab1 w P7 1 +19af6 b O4 1 +19b00 w R16 1 +19b11 w N18 1 +19b30 w G12 1 +19b40 w P16 1 +19b50 w C3 1 +19b71 b P2 1 +19b90 w E4 1 +19bc6 w O5 O3 2 +19bd0 w C5 1 +19c00 b F5 1 +19c00 w R18 1 +19c06 w B4 1 +19c21 b N13 1 +19c40 w E5 1 +19c91 b C17 1 +19c91 w G3 1 +19d26 w R3 1 +19d31 w E2 1 +19d40 b P16 D5 C7 3 +19d50 b D6 1 +19d51 b J16 1 +19d81 b F4 1 +19d81 w G4 F4 2 +19da0 b Q13 1 +19db1 w C18 1 +19de6 w Q15 1 +19df0 b E17 1 +19e27 b O2 1 +19e50 b C5 1 +19e56 w P18 1 +19e80 b S14 1 +19ea1 w E14 1 +19ea7 w S2 1 +19eb1 w B6 1 +19f06 b R5 1 +19f46 w C12 1 +19f57 b H3 1 +19f70 b S15 1 +19f71 w B17 1 +19fa0 b B15 1 +19fa1 b R2 1 +19fa6 b P3 1 +19fd0 b P13 1 +19fd7 w Q3 1 +19fe1 b Q18 1 +1a046 b Q14 1 +1a060 w M5 1 +1a077 w H16 1 +1a090 w B18 1 +1a097 b D17 1 +1a097 w C17 D16 D15 E14 D14 D13 6 +1a0a0 w D4 1 +1a0b6 b G15 1 +1a0b7 w Q2 R3 Q3 R8 4 +1a0c0 b C16 1 +1a0f7 b Q2 1 +1a100 w R8 1 +1a120 b Q8 1 +1a126 w B17 1 +1a167 b R9 1 +1a171 w E18 1 +1a190 b B5 1 +1a1d0 b J17 1 +1a1d7 w F7 1 +1a1f1 w O5 1 +1a236 w E16 1 +1a257 w C18 1 +1a287 b R17 1 +1a330 b E8 F8 2 +1a336 w N5 N6 2 +1a337 b B3 1 +1a351 w G2 1 +1a361 w B18 1 +1a366 b M15 1 +1a371 b E18 1 +1a377 b R3 1 +1a3a1 b R2 1 +1a3b0 w E4 1 +1a3d1 w B13 1 +1a3f0 b A4 1 +1a4b7 b Q6 1 +1a5c7 b Q17 1 +1a5e0 w Q8 1 +1a5e7 b H5 1 +1a5f7 b D5 1 +1a601 w B5 1 +1a647 b N16 O18 2 +1a650 w C4 1 +1a681 b D6 1 +1a6c1 w N2 1 +1a700 b P6 1 +1a766 b O5 1 +1a766 w C17 1 +1a781 b R4 1 +1a781 w R3 Q4 P4 O5 O4 S7 6 +1a790 w R17 1 +1a7c6 b F16 G16 2 +1a7e6 w B16 1 +1a810 w Q5 1 +1a811 w B18 1 +1a831 w C18 1 +1a837 w E2 1 +1a861 b D11 1 +1a891 w P5 1 +1a8a6 w F16 1 +1a8b6 b C3 D4 R5 3 +1a8c6 b R2 1 +1a8c7 w G16 1 +1a8d0 w S13 1 +1a8d6 b Q15 1 +1a8f6 w D2 1 +1a8f7 w D13 1 +1a910 w C4 1 +1a916 b O4 1 +1a996 w F4 1 +1aa56 w Q14 1 +1aa60 b R3 1 +1aa66 b H2 1 +1aa71 w Q19 1 +1aaa0 w R7 P5 Q7 3 +1aab1 w R7 1 +1aac6 b F16 1 +1aad0 b Q15 1 +1aae1 w R3 1 +1ab01 b R2 1 +1ab61 w O16 1 +1abb1 w A15 1 +1abb6 w B8 1 +1ac20 w S13 1 +1ac61 b P15 1 +1ac81 w F7 1 +1acd6 w E7 F7 2 +1ace6 b S16 1 +1ad00 b R16 1 +1ad21 w F17 1 +1ad26 b S1 1 +1ad37 b C3 1 +1ad77 b C14 1 +1ad77 w B19 1 +1ad96 b Q18 1 +1adc7 b E15 1 +1ae01 w J3 1 +1ae11 w O15 1 +1ae20 w Q14 P14 2 +1ae60 w B15 1 +1ae61 w R4 1 +1aea0 w R11 1 +1aef0 w D7 1 +1af77 w N15 1 +1afa0 b Q5 1 +1afa7 w Q5 1 +1afb7 w S3 1 +1afc0 b S6 1 +1b020 b P3 1 +1b090 b B16 1 +1b0a0 w R9 1 +1b0f1 b N16 1 +1b167 b Q16 1 +1b1c0 w F4 1 +1b1c1 w B17 1 +1b1c6 b P15 1 +1b1c7 w N17 1 +1b1f7 b O2 1 +1b231 b D17 1 +1b240 b P12 1 +1b286 w P4 1 +1b287 w Q14 1 +1b371 b P4 1 +1b371 w Q4 1 +1b381 b R2 1 +1b381 w Q12 1 +1b390 w C5 1 +1b3f1 b B17 1 +1b406 w S5 1 +1b411 b O2 1 +1b417 w R5 1 +1b431 b R16 1 +1b446 b C8 1 +1b451 b S4 1 +1b461 w F16 C17 2 +1b477 b R14 1 +1b4a7 w B2 1 +1b4b1 b G3 1 +1b4b6 w F3 1 +1b4c6 b P3 1 +1b4f1 b M16 1 +1b517 w E4 1 +1b536 b Q14 1 +1b581 w S3 1 +1b590 w D7 E4 2 +1b5a1 w C13 1 +1b5c1 w H15 1 +1b5c7 b S13 1 +1b5d7 w F2 1 +1b5f0 w P14 1 +1b616 w O3 1 +1b661 w Q4 1 +1b667 b E17 1 +1b667 w D2 1 +1b686 b T4 1 +1b6b7 w C17 1 +1b6d0 w E4 1 +1b6e0 b B16 1 +1b701 w B7 1 +1b740 b R14 1 +1b746 b C17 1 +1b7a0 b R1 1 +1b7b1 w R17 1 +1b880 w O6 1 +1b8a7 b S15 1 +1b8b1 b R16 1 +1b8b1 w R17 Q14 Q16 P16 O15 P15 P13 Q13 O17 N17 N16 11 +1b8c6 b O4 1 +1b8e1 b P2 G15 2 +1b8f0 b O3 1 +1b947 b Q12 1 +1b956 b L17 1 +1b981 b S3 1 +1b9a6 w Q3 1 +1b9c7 b M16 1 +1b9d7 b G17 1 +1b9e0 w D6 1 +1ba26 b Q17 P18 2 +1ba57 b R14 1 +1ba70 w G5 D7 2 +1bab0 w H13 1 +1bab7 b S4 M5 2 +1bb06 b O2 1 +1bb16 b C11 1 +1bb50 w D3 D4 G3 E5 C3 5 +1bb86 w O4 1 +1bbc7 b H18 1 +1bbe0 w B8 1 +1bbf7 w E15 1 +1bc00 w D16 1 +1bc06 w P3 1 +1bc11 w N4 Q4 2 +1bc37 w R12 1 +1bc40 b E4 1 +1bc50 b R14 1 +1bc70 w C17 1 +1bc90 w F6 1 +1bc97 w D15 1 +1bcc6 b C6 1 +1bcd6 w P3 1 +1bd17 w Q17 1 +1bd30 b F17 1 +1bd51 b R16 1 +1bd51 w G14 1 +1bd76 b S2 1 +1bd90 b O18 1 +1bd91 b D4 1 +1bd91 w R8 1 +1be17 b C14 1 +1be20 w G17 1 +1be31 b J16 1 +1be57 w B16 1 +1be70 b D18 1 +1be71 b R6 1 +1be80 w N3 1 +1be86 b G3 1 +1beb0 w E7 1 +1bec0 b C11 1 +1bed6 b P17 1 +1bee0 b B16 1 +1bef0 b R5 O6 2 +1bf00 w D13 1 +1bf40 b E8 E7 C5 E5 4 +1bf51 b B4 1 +1bf61 w B16 1 +1bf77 w G5 1 +1bfb6 w E13 1 +1bfd6 w N14 1 +1c010 b C6 1 +1c036 w M17 1 +1c0d1 w G14 1 +1c130 w R14 1 +1c146 w G5 1 +1c171 b E13 1 +1c1b7 b O3 P4 2 +1c1d1 w R11 1 +1c1d7 b C18 1 +1c1f1 w D2 1 +1c217 w D3 1 +1c231 w B12 1 +1c240 b F4 1 +1c256 w Q4 1 +1c260 b F14 1 +1c2a7 w O2 1 +1c2c6 w C14 G17 2 +1c2d6 b F15 1 +1c2f1 b D13 1 +1c2f1 w Q7 1 +1c301 w D2 1 +1c337 b B14 1 +1c366 b P3 Q4 2 +1c367 w D6 1 +1c370 b H16 1 +1c380 w A4 1 +1c387 w C7 D12 2 +1c3a7 b M2 1 +1c3b6 w D6 1 +1c406 b R14 Q16 2 +1c420 b D18 1 +1c427 w A16 1 +1c436 b O4 1 +1c497 b R16 1 +1c4b0 b D14 1 +1c4f6 w B14 1 +1c531 w G2 1 +1c541 w G4 1 +1c551 w C13 1 +1c5a6 b C18 1 +1c5b0 w E5 C3 2 +1c5d0 b Q11 1 +1c5d1 w C2 1 +1c5e1 w C13 1 +1c5f7 w S14 1 +1c627 b S16 R18 2 +1c637 b M4 1 +1c656 w D16 1 +1c680 w P18 1 +1c691 w R16 1 +1c6d7 b R17 1 +1c6e6 w B3 1 +1c706 w F4 1 +1c746 w R16 Q17 2 +1c747 b Q18 1 +1c751 b S17 1 +1c766 w R15 R18 2 +1c7a1 b B16 1 +1c7b0 w B5 1 +1c7b1 b E15 1 +1c800 b P17 1 +1c817 w D15 1 +1c821 w M16 1 +1c856 b S6 1 +1c857 w Q7 1 +1c861 b F5 1 +1c870 b P16 1 +1c897 w D13 1 +1c8b7 b S5 1 +1c8c6 b S2 1 +1c8c7 w Q14 1 +1c8d6 w B6 1 +1c8e7 b D8 1 +1c8f6 b C18 1 +1c911 b Q17 1 +1c930 w C17 1 +1c940 b O2 1 +1c947 w H4 1 +1c956 b E2 1 +1c966 b S5 1 +1c976 w P6 1 +1c9b7 b P2 1 +1c9c7 w B14 1 +1c9d0 b O17 1 +1c9e1 b G17 1 +1ca17 b B2 1 +1ca47 w C17 1 +1ca51 b F15 1 +1ca66 w Q16 1 +1cab6 w D18 1 +1cab7 w O3 1 +1cac1 w R18 1 +1cad7 w P4 1 +1cb31 b Q2 1 +1cb47 w Q7 P3 2 +1cb56 b R18 1 +1cb60 b R3 1 +1cb60 w T3 1 +1cb81 w B15 1 +1cbb1 w B13 1 +1cbb7 b R13 1 +1cbb7 w R18 1 +1cc30 w P6 1 +1cc81 w C4 1 +1cc86 w C15 1 +1cc87 w Q18 1 +1ccb1 b C18 1 +1ccc0 b A5 1 +1ccd1 w P4 R7 2 +1cce6 w F16 1 +1ccf7 b O17 1 +1cd06 w B19 1 +1cd17 b R6 O3 Q6 O4 Q7 N4 R7 N3 R9 L3 10 +1cd51 w N16 1 +1cd70 w E18 1 +1cd80 w C5 1 +1cda6 w E17 1 +1cda7 b N4 1 +1cde1 b N19 1 +1ce10 w R18 1 +1ce11 w B5 1 +1ce16 b R9 1 +1ce37 b Q15 1 +1ce77 w S3 1 +1ceb0 b R8 1 +1ceb1 b H16 1 +1ced7 b S17 1 +1cee1 b R16 S5 2 +1cf11 w H5 1 +1cf31 b N16 1 +1cf37 w D6 1 +1cf51 w R18 1 +1cfa6 b F16 1 +1cfe6 b P17 1 +1cff0 b C9 1 +1d056 w P17 1 +1d0e6 w S15 1 +1d0f6 w B3 1 +1d107 b N18 1 +1d110 w C17 1 +1d1a6 b C9 1 +1d1d1 b P17 1 +1d1f0 w R14 1 +1d217 w Q6 1 +1d240 w P13 1 +1d256 w D5 C6 2 +1d270 b B3 1 +1d277 w E4 F4 D5 E3 4 +1d296 w E15 1 +1d2b7 b G6 1 +1d2d7 w Q3 1 +1d2e0 b F7 1 +1d2e7 b Q13 1 +1d2e7 w E13 1 +1d310 w C6 1 +1d381 w R3 1 +1d3c7 w O16 1 +1d3d6 w E5 1 +1d3e0 b P5 1 +1d426 w R18 1 +1d427 w T19 1 +1d451 w Q4 1 +1d456 b B15 1 +1d457 b B17 1 +1d460 b P5 1 +1d467 b R5 R6 Q5 P4 4 +1d467 w Q4 1 +1d480 b E4 F4 C7 C8 C9 D7 D8 D9 8 +1d480 w E4 C9 F3 G3 H3 G4 F4 7 +1d4a6 b S15 1 +1d4d6 b C12 1 +1d4e1 b P6 Q6 2 +1d516 b B14 1 +1d560 w R7 1 +1d571 w B4 1 +1d5b1 b S6 1 +1d5b1 w B16 1 +1d5d0 w O15 1 +1d606 b E13 1 +1d626 b Q4 1 +1d646 b N2 1 +1d646 w P14 1 +1d650 w D6 S17 2 +1d656 b F6 1 +1d681 w R14 1 +1d686 w D5 1 +1d6b1 b R4 1 +1d6d6 w C17 D16 2 +1d6d7 w S16 1 +1d6f0 b J5 1 +1d776 b Q18 1 +1d786 w D12 1 +1d796 w N4 1 +1d7b0 b E13 1 +1d7b6 b O14 1 +1d7e0 b J15 1 +1d7e6 w D6 1 +1d800 b F3 1 +1d811 w Q17 1 +1d831 b R18 1 +1d840 b D15 1 +1d850 w O17 1 +1d857 w B17 1 +1d8b7 w P3 1 +1d8f0 b E13 1 +1d920 b E17 B17 2 +1d930 w Q17 1 +1d947 b G16 F18 2 +1d9e7 w B5 1 +1da70 b C11 1 +1da80 b N2 1 +1daa0 b M4 1 +1daa6 w S8 1 +1dab6 w G9 1 +1dac7 w E16 1 +1dae1 b O17 1 +1dae6 b D1 1 +1dae7 b S3 1 +1daf1 w R16 1 +1db37 b J3 1 +1db51 w E4 1 +1dbb0 w O4 R3 2 +1dbc1 w N17 1 +1dbd0 w P2 1 +1dc17 w N17 1 +1dc26 w B12 1 +1dc46 w O14 1 +1dc47 w R7 1 +1dca0 w S2 1 +1dcb1 w H16 1 +1dcc7 b S13 1 +1dcc7 w S3 1 +1dd01 b B17 1 +1dd16 b P4 1 +1dd51 b G4 1 +1dd56 w P16 1 +1dd71 b D16 R2 2 +1dd76 b H17 1 +1dd91 w O4 1 +1de31 b E3 1 +1de36 w C2 1 +1de71 b B6 1 +1dea6 b R2 1 +1dec0 b R13 1 +1dee1 w C6 1 +1def1 b D2 C3 D3 C8 4 +1def6 w Q17 1 +1def7 w D14 1 +1df61 w E16 1 +1dfb1 b C13 1 +1dfd6 w S6 1 +1e017 b B3 1 +1e041 b L6 1 +1e070 w D15 1 +1e090 b D11 1 +1e090 w O5 1 +1e0a0 w B18 1 +1e0d1 w C6 1 +1e0f1 b S3 1 +1e110 b D14 B14 2 +1e121 w Q15 1 +1e127 b F3 1 +1e146 b C14 1 +1e167 b L17 P14 P13 3 +1e167 w B4 1 +1e1a0 b G3 1 +1e1b7 w D2 1 +1e1d0 w R18 Q18 2 +1e286 b Q18 1 +1e2b0 w P4 1 +1e2b7 w Q12 1 +1e2f0 w R14 O15 2 +1e327 w F16 1 +1e356 w R2 1 +1e377 w Q17 1 +1e391 b O15 O16 2 +1e3d6 b B13 1 +1e3d6 w O2 1 +1e3f1 w L2 1 +1e3f7 b C4 1 +1e446 b Q14 1 +1e466 w C17 1 +1e477 w D16 1 +1e490 b M15 1 +1e4c0 b M6 1 +1e4e0 w D18 1 +1e551 b E13 1 +1e557 b D4 1 +1e576 b S3 1 +1e580 w P4 1 +1e5a6 b D18 1 +1e5a7 w Q17 1 +1e5f7 w M16 1 +1e601 w O6 1 +1e611 w S16 1 +1e626 b R18 1 +1e640 w E2 1 +1e657 w B3 1 +1e660 w C9 1 +1e676 w N17 P15 2 +1e687 b B14 1 +1e6a7 b Q16 1 +1e6e1 b N16 1 +1e6e1 w S4 1 +1e6e7 w R15 1 +1e6f6 w Q14 1 +1e701 b D3 1 +1e701 w D4 1 +1e720 w G4 1 +1e756 b C18 1 +1e7a6 b F16 1 +1e7c6 w H3 1 +1e801 b S5 1 +1e801 w H17 1 +1e820 w S16 1 +1e830 b Q13 1 +1e841 w P17 1 +1e860 b G16 1 +1e880 b P14 D16 C4 D4 C7 E5 C3 7 +1e8e0 w P16 1 +1e900 w C8 1 +1e940 b N4 1 +1e940 w G13 1 +1e990 w D17 1 +1e991 w Q17 1 +1e9a7 w R2 1 +1e9d7 b P17 P18 Q16 3 +1e9f1 w G6 1 +1ea17 b D4 1 +1ea36 w P17 1 +1ea37 w N4 1 +1ea57 b B3 1 +1ea61 w E18 1 +1ea87 b Q7 1 +1eb16 b N6 1 +1eb26 w D15 1 +1eb60 b Q14 1 +1eba1 w P16 O16 2 +1eba7 w B15 1 +1ebc0 w P6 1 +1ec01 b R4 1 +1ec37 w M3 1 +1ec40 w P6 1 +1ec81 w L4 1 +1ece1 b C2 1 +1ece1 w N3 1 +1ed07 b R4 1 +1ed10 b E4 1 +1ed30 w M17 1 +1ed61 w O3 1 +1ed97 b C13 1 +1edc6 b P16 1 +1ee20 b R6 1 +1ee36 b Q6 Q16 2 +1ee37 w E18 B14 2 +1ee47 w C18 1 +1ee56 b C4 1 +1ee60 b D15 1 +1eea0 b E12 1 +1eeb0 w D6 1 +1eec1 b S3 1 +1eec7 b Q15 1 +1ef06 b Q3 1 +1ef10 b P14 1 +1ef11 b D8 1 +1ef27 w S16 1 +1ef31 w R2 1 +1ef60 w B18 1 +1ef66 b B17 1 +1ef80 w C16 1 +1ef96 b Q7 R7 R3 3 +1ef97 w D4 1 +1efa6 b C8 1 +1efb6 b G17 B15 2 +1efd0 b Q7 1 +1efe1 b O5 1 +1efe1 w Q14 P15 2 +1f010 w H3 1 +1f020 b Q7 1 +1f030 b E15 1 +1f037 w D9 1 +1f041 w G12 O2 2 +1f051 b C17 1 +1f077 b F3 1 +1f086 b D2 1 +1f0a1 b D4 1 +1f0e6 b N17 1 +1f100 b N7 1 +1f110 b E9 1 +1f117 b J16 1 +1f141 b F14 1 +1f161 b R15 1 +1f186 b C14 1 +1f1b6 b B3 N18 2 +1f1d6 b P3 1 +1f1d7 w S3 1 +1f1f7 w A14 1 +1f221 w R4 1 +1f247 w Q4 1 +1f250 w B18 1 +1f266 b O16 1 +1f287 b E7 1 +1f2c6 b M17 1 +1f317 b F3 1 +1f360 w C3 1 +1f366 w E18 1 +1f367 w B18 1 +1f3d6 b F5 1 +1f3f0 w R16 Q17 2 +1f3f1 b R12 1 +1f401 w C6 1 +1f450 b D17 1 +1f460 b Q15 1 +1f477 w R18 1 +1f480 w F5 1 +1f4f6 b C3 1 +1f521 w D11 1 +1f576 w Q18 1 +1f5b7 w G4 1 +1f5c0 w E12 1 +1f5f7 b P14 1 +1f5f7 w Q4 1 +1f611 w O18 1 +1f657 w P18 1 +1f6c7 b E17 E18 D16 3 +1f6d6 w D15 1 +1f6e1 b G15 1 +1f717 w L17 N17 2 +1f720 w O14 1 +1f721 w C17 1 +1f731 w D13 1 +1f761 b E17 1 +1f7b6 w C3 1 +1f816 w M17 1 +1f837 b C14 1 +1f8a6 b S4 1 +1f8a6 w O17 1 +1f8a7 w P17 1 +1f8c0 b F14 1 +1f8d7 w E14 1 +1f930 b F12 1 +1f946 w G17 1 +1f966 w P2 1 +1fa10 b C9 1 +1fa27 w B3 J3 2 +1fad6 b E12 E6 2 +1fb26 b C16 B15 2 +1fb61 w P3 1 +1fb71 b G16 O5 2 +1fb77 b R13 1 +1fb81 w F3 1 +1fb96 w C2 1 +1fba7 w R17 1 +1fbb1 b R4 1 +1fbb6 b C4 1 +1fbb6 w D13 1 +1fbc0 b B5 1 +1fbc7 b Q18 1 +1fbf6 w O14 1 +1fc16 w P7 1 +1fc21 w G3 1 +1fc41 b E3 1 +1fc96 w R2 1 +1fcb1 b D6 E7 2 +1fcc6 w S3 1 +1fcc7 b B15 1 +1fce0 b N15 1 +1fcf6 b Q6 1 +1fd17 w P5 1 +1fd30 b H17 1 +1fd37 b C6 1 +1fd57 b Q4 1 +1fd77 w E3 1 +1fd91 b D5 1 +1fde6 b Q17 1 +1fdf7 b C7 1 +1fe61 w P3 1 +1feb0 b L16 1 +1fec0 w B3 1 +1fee7 b Q2 1 +1ff10 w G16 1 +1ff86 w E16 1 +1ffa0 b C13 1 +1ffb6 w R18 1 +1fff0 w P5 1 +20021 w Q5 O5 2 +20030 b B15 1 +20036 w E15 1 +20051 w B17 1 +20061 w N4 1 +20076 w C13 1 +20097 b P15 O17 2 +200a1 b P15 P14 R17 R13 4 +200a6 w R4 1 +200b7 w P4 1 +200c0 b D16 1 +200c7 b Q18 1 +200d7 w Q7 1 +200e6 w E16 D15 2 +20161 w S17 1 +20180 b O16 1 +20187 w F6 1 +201b6 b D17 1 +201e7 w F5 C3 2 +20217 w S14 1 +20220 w E3 1 +20226 w B7 1 +20247 b C6 1 +20251 w R3 1 +20256 b S7 1 +20266 b R3 1 +20276 w Q2 1 +20291 b Q15 1 +202a6 b E17 1 +202b1 w M4 1 +202c1 b F3 1 +202d6 w R13 P18 2 +202e6 b S18 1 +202f0 b P7 1 +202f0 w L16 1 +202f1 b P17 1 +202f7 w P6 1 +20320 w S3 1 +20346 b C12 1 +20376 b Q7 1 +20397 w F15 1 +203c1 b Q16 1 +203c7 b B7 1 +203d7 w G2 1 +203e6 b B5 1 +203e7 w F17 1 +20400 w N4 Q5 2 +20407 b C18 1 +20437 b S4 1 +20466 b M2 1 +20477 w F3 1 +20486 w D14 D17 2 +204a0 w D17 1 +204b0 b C14 S4 2 +204b1 b R2 1 +204c0 b D11 1 +204c7 b D3 1 +204e1 b Q3 1 +204f6 b P17 1 +20520 b P8 1 +20526 w G4 1 +20530 w E4 1 +20550 w C17 1 +205a0 w E14 1 +205a1 b P2 1 +205b0 b S7 1 +205d6 w D16 1 +20600 b B2 1 +20606 b D14 1 +20611 w E14 1 +20620 w D3 1 +20637 b G14 1 +20646 b F17 1 +20671 b D6 1 +206a6 w M2 1 +206c0 w B15 1 +207b1 w F16 1 +207c1 b P14 1 +20806 w S5 1 +20840 b R4 1 +20880 w F3 1 +208a6 w D5 1 +208b1 b E17 1 +208d7 b G4 1 +20911 w G3 1 +20946 w Q13 1 +20947 b A4 1 +20970 b C15 1 +20980 w E17 1 +209a7 w D13 1 +209d1 w F17 1 +209d6 w F16 1 +209d7 w C16 1 +20a40 b S17 1 +20a77 w F5 1 +20a96 b R6 1 +20a96 w D18 1 +20ab0 b B4 1 +20ab6 w P14 1 +20ac6 w C3 1 +20ae1 w C3 1 +20ae6 w C18 1 +20b10 b P14 1 +20b10 w Q4 1 +20b80 b N17 1 +20b90 b M16 1 +20c10 b M7 1 +20c10 w P3 1 +20c27 b P18 1 +20c40 b C12 1 +20c57 w R8 1 +20cd6 b R4 1 +20ce1 b R15 1 +20d16 b S15 1 +20d41 w C15 1 +20d56 b F2 1 +20d81 b R6 1 +20d87 w Q6 1 +20d91 b L3 N3 2 +20dc6 b C5 1 +20de6 w F7 1 +20df6 w F17 1 +20e11 w F17 1 +20e31 b P14 1 +20e36 w M3 1 +20e60 w E3 1 +20e71 b E5 1 +20f00 b D15 1 +20f06 w R3 1 +20f11 w O16 1 +20f17 b P4 1 +20f87 b N3 1 +20fd0 b D15 1 +20fd1 b Q3 1 +20fe0 b P16 1 +20fe1 w P8 1 +21011 w R12 1 +21021 b S15 1 +21040 w G17 1 +21056 b E5 1 +21066 w F16 1 +210a1 b G3 1 +210c1 b R2 1 +210c6 b H3 1 +210c7 w P4 1 +210d0 w E4 1 +210e0 w D7 1 +210e6 w Q18 1 +210f7 w C3 1 +21101 b R3 1 +21106 b H16 1 +21121 w E15 C14 2 +21157 w N16 R15 2 +21180 b M16 1 +21191 w L17 1 +211a0 w P6 1 +211a1 w E17 E18 D16 3 +211a7 b Q17 1 +211b6 w R17 1 +211d7 w C5 C6 D6 D5 4 +211e1 b D7 E3 2 +211e7 w N19 1 +21247 w L3 P6 2 +21286 w M18 1 +21300 w O16 O15 2 +21320 w S16 1 +21341 w A2 1 +21351 b R3 1 +21351 w P6 1 +21370 b E3 1 +21390 w C12 1 +21397 b J3 1 +21397 w B15 1 +213b7 w F16 1 +213c6 w Q16 1 +213d6 b B15 1 +213e0 w B5 1 +21457 b R12 1 +214a0 b R17 Q13 2 +214b7 w D14 1 +214c0 b R12 1 +214c0 w R16 1 +214c1 w R15 1 +21547 b D13 F12 2 +21556 w G4 1 +215b6 w S4 1 +215f0 w F4 R5 2 +215f1 w P15 O17 2 +21611 w F2 1 +21640 b E15 1 +21667 w O15 R17 2 +21670 w D7 1 +21686 w P6 1 +216a6 b P5 1 +216b0 w P15 1 +216c7 w G5 1 +216d1 w G3 1 +21750 w B4 1 +21757 w B13 1 +21780 w P7 1 +21786 b Q15 1 +217a0 b M17 1 +217e1 w D6 1 +217f7 w D6 1 +21801 b R2 1 +21801 w N17 E7 2 +21806 b P7 1 +21807 b B4 1 +21827 w S4 1 +21850 w A15 1 +21857 w E16 1 +21861 w D14 1 +21896 w D17 1 +218c6 w O16 1 +21907 b C17 1 +21910 w N3 P2 2 +21921 w Q7 S6 2 +21930 b P6 1 +21970 b G3 1 +21976 b P3 1 +219a0 w P3 1 +219a7 w Q16 1 +219c6 b P6 1 +219d1 b D15 1 +219e0 w O8 1 +21a00 b R8 1 +21a31 w D4 1 +21a40 w R17 1 +21a61 w S14 C17 2 +21a81 w E3 E2 D4 3 +21a96 w R15 1 +21aa1 b D16 1 +21ab6 w S5 1 +21ad0 w R13 1 +21ae6 w R12 1 +21b26 b C9 1 +21b27 b B19 Q13 2 +21b40 b Q6 1 +21b46 b F3 1 +21b61 w Q6 1 +21b81 b Q6 Q7 2 +21b90 w R15 1 +21b91 w D6 1 +21ba6 b N3 P5 2 +21ba6 w P13 1 +21bb1 w B7 1 +21bb6 b F2 1 +21bd1 w H17 1 +21c00 b S5 1 +21c26 b R8 1 +21c36 b G5 1 +21c60 b H7 F8 2 +21c80 b R6 E14 2 +21c80 w E12 1 +21c91 w N14 1 +21cb0 b D4 1 +21cd0 b C8 1 +21cd7 b G3 1 +21db0 w F15 1 +21dc0 b R9 1 +21de0 b F4 1 +21e11 w R18 1 +21e31 w G15 1 +21e90 w O3 1 +21ec1 b S2 1 +21f07 w S18 1 +21f17 b Q12 1 +21f46 w O17 P16 2 +21f47 b D18 1 +21f61 b P5 1 +21f61 w R17 1 +21f76 b B18 1 +21f76 w S4 1 +21f77 b H16 1 +21f77 w F13 1 +21f80 w Q5 1 +21f90 b P3 1 +21fb1 w E6 1 +21fe0 b R17 1 +22030 w H16 1 +22031 w B19 1 +22050 b F6 1 +22090 b P12 1 +22090 w F4 1 +220a0 w S5 1 +220b1 w D12 1 +220b6 w E3 1 +220d7 w S3 1 +220e6 b Q6 1 +220f0 w D5 1 +220f7 b E14 1 +22127 b L2 1 +22140 w Q4 1 +22176 b S16 1 +22177 w G19 1 +221c0 w C12 1 +221e0 w O5 1 +22226 b E2 1 +22230 b O13 1 +22247 w P12 1 +22256 b S15 1 +22256 w N3 1 +22257 b C7 1 +22271 w C3 1 +22276 w Q13 1 +22286 w R7 1 +222c7 b D4 1 +222e0 w D12 1 +222e1 w S6 1 +22316 b S17 1 +22317 w P6 Q7 R5 3 +22320 b D3 1 +22367 w D15 1 +22376 w Q18 1 +22380 b F9 1 +22387 b Q3 1 +22397 b P4 Q3 2 +22397 w Q5 R3 R7 O4 M4 5 +223a0 b M14 1 +223c6 b D14 1 +223d1 w B17 D16 2 +223d7 b B18 1 +223e7 w S13 1 +22421 w D14 C13 2 +22441 b Q18 1 +224a0 b D13 1 +224b1 b B3 1 +224b6 b C14 D15 2 +224b6 w Q14 1 +224b7 b B9 1 +224d6 w R17 1 +224f6 b S11 1 +22511 b R16 1 +22520 w F16 1 +22526 w R5 Q4 2 +22537 w L4 1 +22547 b S6 1 +22547 w E6 1 +22551 w C15 1 +22556 w C2 1 +225b1 w Q9 1 +225f6 b R16 1 +22620 b B15 1 +22666 b D14 1 +22687 b J3 1 +226a1 w E6 1 +226d1 w F13 O17 Q15 3 +226d6 w S3 1 +226e0 w P4 1 +22700 b C3 H3 2 +22720 b N3 1 +22727 b O16 1 +22736 b E4 1 +22747 w O14 1 +22786 b P18 1 +227b0 b O15 1 +227d0 b P16 1 +22807 w B5 1 +22810 b F3 1 +22811 w Q17 1 +22820 w P15 1 +22846 b B2 1 +22850 w O6 1 +22857 b R18 1 +22866 w R15 1 +22871 w T3 1 +22880 w E17 B17 2 +22881 w S14 1 +22887 w E5 1 +228b0 w N16 1 +228c6 b Q16 1 +22921 b E18 1 +22970 b L3 1 +229d7 b N17 1 +22a01 w C3 1 +22a06 b S16 1 +22a07 b S5 1 +22a46 b B3 1 +22a60 b C3 G4 2 +22af0 b Q17 1 +22b16 b P13 1 +22b26 w E4 1 +22b57 w D4 1 +22b67 w D2 C3 2 +22b76 b C8 1 +22b90 b D15 1 +22bd1 b C15 1 +22bf6 w P16 1 +22bf7 b S7 1 +22c36 b P7 1 +22cc6 w N6 1 +22d11 b Q8 1 +22d21 w B3 1 +22d36 w C7 1 +22d81 w N8 1 +22da7 w O6 N7 2 +22db6 w Q18 1 +22de0 w D15 1 +22df0 w B4 1 +22e17 w R3 1 +22e31 w D14 1 +22e50 w F17 1 +22e60 b P18 1 +22e60 w F5 1 +22e66 b S7 1 +22e70 w T16 1 +22e71 w S5 1 +22ea1 b Q3 1 +22eb1 b O2 1 +22ed0 b P18 Q17 2 +22ed6 b D5 1 +22f46 w F6 1 +22f60 b Q7 1 +22f61 b P18 1 +22fa1 w H3 1 +22fa6 b C7 1 +22fb7 w B7 1 +22fe0 w T5 1 +22ff0 w R4 1 +23031 w C4 1 +23047 w S14 1 +23051 b D2 1 +23087 b D17 1 +23097 b R7 1 +230a7 b Q18 1 +230b6 b C16 1 +230c1 w C11 F15 G15 3 +230d1 w C3 1 +23110 b D8 1 +23171 w B6 1 +23180 w F13 1 +23186 b O16 1 +231e1 b D11 1 +23207 b F5 1 +23240 w F2 1 +23241 w E8 1 +23247 w C4 1 +23261 w D17 1 +23270 b E17 1 +23280 w S16 1 +232a1 b S4 1 +232f6 w P15 1 +23307 b P13 1 +23326 w P6 O3 2 +23336 b B7 1 +23340 b C3 1 +23346 b C2 1 +23351 b H4 1 +23366 w R12 1 +233a0 w R11 1 +233a1 b O14 1 +233b7 b O4 1 +233c6 w B15 1 +233f0 b D2 1 +233f7 w R6 1 +23430 b D16 1 +23440 b E13 1 +23480 b Q13 1 +23481 w Q7 1 +23486 w E13 1 +23487 b E2 1 +23487 w L18 1 +234d0 b D5 E17 2 +23531 w G2 1 +23551 b S17 L17 2 +23557 w O11 1 +23567 w P15 1 +23571 b C6 1 +235c0 w Q5 1 +235e1 w Q17 1 +23601 b E7 1 +23657 b F7 J2 2 +23660 b D9 1 +23676 w S9 1 +23681 b R5 1 +23697 w E2 1 +236b6 b C18 1 +236c6 w P2 1 +236d1 w S2 1 +23720 b C15 1 +23721 w R4 1 +23727 b D12 1 +23737 w C2 F2 2 +23746 b B2 1 +23780 b C17 C15 2 +237a7 w C15 1 +237c0 w N5 1 +237d0 w O15 1 +237e1 b Q4 1 +23837 b R13 1 +23886 w F16 1 +238b7 b E16 D15 2 +238d6 b R6 1 +238f6 b R5 Q4 2 +23927 w G16 H17 J17 3 +23930 w C2 1 +23951 w Q17 1 +23956 b S13 1 +23970 b F17 1 +23990 w P7 1 +239b6 b Q15 1 +239c1 b L17 N17 2 +239e0 b F15 1 +23a36 w R17 1 +23a51 w D4 O5 2 +23a71 w Q11 1 +23aa1 w P6 1 +23af6 w P6 1 +23b10 w E14 1 +23b30 b P13 1 +23b30 w L4 1 +23b77 b B14 1 +23b87 w P4 O4 Q5 P3 4 +23bd7 b E2 1 +23be1 w D9 1 +23be7 b D19 1 +23c01 w S13 P12 2 +23c76 w C14 O7 2 +23cd0 w C12 1 +23d87 b S9 1 +23d96 w Q3 P2 2 +23d97 b O6 1 +23db1 w H6 1 +23db6 b O13 1 +23dc6 b B5 1 +23e07 b D16 1 +23ef7 w P15 P14 R17 R13 4 +23f00 w C17 1 +23f06 b B8 1 +23f47 w P13 1 +23f57 w C4 1 +23f71 w R4 1 +23f91 w F16 1 +23fa0 b E3 F6 2 +23fd7 b G14 1 +23fe0 b C9 1 +24061 b C17 1 +24087 b S6 1 +240d1 w S14 1 +24121 b S18 1 +24161 w N16 1 +241a0 b C7 1 +241a1 w Q18 1 +241b1 b F4 1 +241d1 w D15 1 +24207 b E19 1 +24256 b H15 1 +24266 w D3 1 +24277 w Q5 1 +24286 w C18 1 +24287 b E16 E14 2 +24291 b C7 1 +242b6 b P6 1 +242c1 w D7 1 +242d6 w F14 1 +242e7 b F3 1 +24306 w R3 1 +24326 b C1 1 +24331 b O18 1 +24331 w D3 1 +24336 w F7 1 +243c1 w C18 1 +243e7 w D3 1 +24446 w E17 D16 2 +24466 w E16 1 +24497 b P4 1 +24497 w D7 1 +244e0 b D14 1 +244e6 w R9 1 +244f0 b H17 P2 2 +24501 b P3 1 +24566 w C15 1 +24586 b P2 1 +24586 w C8 1 +245a1 b R14 1 +245c1 w C14 F17 D14 F16 D13 G16 C13 G17 C11 J17 10 +245e1 b R3 1 +245f7 w R11 O15 2 +24600 w Q15 B15 2 +24607 w H4 1 +24610 w B5 1 +24630 w H4 1 +24657 b E4 E6 2 +246a1 b Q15 Q14 P16 R15 4 +246a6 b Q6 1 +246a6 w E17 C17 2 +246d1 w P6 1 +246e1 w Q13 1 +246f7 b P6 1 +24700 w R18 1 +24707 w C4 1 +24717 w D16 1 +24721 b C7 1 +24750 w N3 1 +24770 w R7 1 +24771 w D17 1 +24791 b C6 1 +247a7 w R18 1 +247d7 b E3 1 +247f1 w C11 1 +24817 w B18 1 +24827 w G17 1 +24836 b P17 1 +24847 w T13 1 +24866 w D13 1 +24896 w B12 1 +24897 b L3 P6 P7 3 +248c0 w B7 1 +248c6 b P7 1 +24900 b G4 D5 2 +24910 b L16 1 +24911 b R18 1 +24920 w P3 1 +24930 w R15 1 +24950 b C18 1 +24951 b D6 1 +24987 b O17 1 +249e7 b G16 D16 2 +24a01 w O3 1 +24a10 b E17 1 +24a20 b F17 1 +24aa7 w E18 1 +24ab1 b O17 1 +24ad7 b C2 1 +24b40 b H16 1 +24b41 b R16 1 +24b47 w P4 1 +24b70 w S2 1 +24b97 w C2 1 +24bd7 b R14 1 +24be7 b Q4 1 +24be7 w S18 1 +24c06 w P17 1 +24c27 w F16 1 +24c50 b P7 F16 2 +24c66 w O15 1 +24c91 w D3 1 +24d01 w C7 1 +24d21 w Q4 1 +24d26 b O16 1 +24d27 b G18 1 +24d41 b P16 R17 N17 Q14 Q12 5 +24d41 w Q15 R16 2 +24d50 w F13 1 +24db0 b D14 1 +24db6 w P15 1 +24de6 b F3 1 +24df7 w D16 1 +24e30 w F8 1 +24e31 b R15 1 +24e80 w E6 F4 2 +24eb7 b R14 1 +24ed1 b O16 1 +24f36 b C15 1 +24f47 w R2 1 +24f90 b E7 1 +24f96 w S16 1 +24fa7 w C8 1 +24fe1 w R15 1 +25001 b R17 Q14 R14 M16 O15 5 +25011 b Q16 1 +25026 w D14 1 +25047 w Q12 1 +25051 b Q17 H16 2 +25051 w R17 O16 Q16 Q15 P14 P15 N15 N16 R14 R13 Q13 11 +25067 b D13 1 +25080 b F3 1 +250a6 w N14 1 +250c0 b M15 1 +250d7 b C12 1 +250e7 w E6 1 +250f6 w F6 1 +25121 b D2 1 +25146 b O4 1 +25187 b N15 1 +251b1 w N12 1 +251b6 w E2 1 +251c0 b D18 1 +251c1 w S5 1 +251f6 w O16 1 +25200 b R15 1 +25226 b Q14 1 +25231 w Q17 1 +25237 b E17 1 +25240 w C7 F3 2 +25290 b C15 1 +252a0 w F5 1 +252f7 w G13 1 +25336 b R13 1 +25346 b O4 1 +25377 w Q13 1 +25387 w D18 1 +25397 b R16 1 +25397 w C18 1 +253a7 b B15 1 +253e7 w E12 1 +25401 b S14 1 +25420 b E5 1 +25491 b F8 1 +254d7 w P18 1 +254e0 b E3 E15 2 +254f1 w O7 1 +25500 b C14 1 +25520 w O12 1 +25531 w H14 1 +25581 w O18 1 +255d1 w S5 1 +255e7 b C12 1 +25636 b R12 1 +25671 w O3 1 +25696 w E8 1 +256a1 b B18 1 +256a6 w S7 1 +256b7 w R18 1 +256f0 b R16 1 +25700 b S16 Q16 2 +25711 w C3 1 +25760 w P12 1 +25770 b Q15 1 +25781 w C7 1 +25786 w L17 1 +25807 b S4 1 +25807 w C5 1 +25811 b E8 1 +25811 w D3 1 +25816 w H4 1 +25840 w R4 Q4 2 +25860 b R4 G17 Q13 3 +258a1 w R13 1 +258d7 w H17 1 +258f7 w Q18 1 +25907 w Q16 T18 2 +25916 b R6 1 +25936 b S4 1 +25941 b S12 1 +259b7 b P17 1 +259c0 w D17 1 +259c6 b Q2 1 +259f6 b P5 1 +25a16 b R17 1 +25a30 b Q3 1 +25a51 w Q16 1 +25a61 w O16 1 +25a91 w D5 F5 2 +25ab1 b B2 1 +25ac6 b S6 1 +25b06 w D14 1 +25b46 w C17 G4 2 +25b51 w O2 1 +25b66 b C5 1 +25b67 b D17 E16 C17 3 +25b91 w F17 1 +25ba1 w S14 1 +25bc1 b B3 1 +25bc6 w N5 1 +25be1 b D3 1 +25be1 w C3 F4 D4 D5 E6 E5 G5 G4 C6 C7 D7 11 +25c00 w Q4 1 +25c50 b R8 E12 F12 3 +25c80 w P12 P13 R15 P15 4 +25cb6 w S4 1 +25ce1 b F18 1 +25cf6 b O17 1 +25d11 b F16 G15 2 +25d56 w P16 1 +25d60 b S2 1 +25d76 b H17 1 +25d76 w T3 1 +25d90 w B15 1 +25da6 w D6 1 +25da7 b J18 1 +25dc7 b C17 1 +25dc7 w Q5 Q6 P4 R5 4 +25e06 b C12 1 +25e17 b M3 1 +25e17 w D13 1 +25e46 b C13 E15 2 +25e51 b S5 1 +25e71 w E14 1 +25e87 b E3 1 +25ec7 w E6 1 +25ed6 w P18 1 +25f10 b R8 1 +25f27 b F15 1 +25f36 w P5 1 +25f81 b L3 P6 2 +25ff7 b D2 1 +26000 b Q15 1 +26030 w P1 1 +26061 b D17 1 +26070 b D11 1 +26080 w C14 1 +26081 w Q17 1 +26090 w Q15 1 +260a6 w D18 1 +260f7 w E7 1 +26151 b E3 1 +26161 b F2 1 +26180 w G15 1 +26187 b B1 R9 2 +26196 w N4 1 +261a6 w N17 R11 2 +261b0 b C15 1 +261b6 b R1 1 +261e6 w F14 1 +261f0 w P5 1 +26226 b Q17 1 +26256 b F16 1 +26256 w S17 1 +26261 b F3 D4 2 +26261 w D15 1 +26286 b G4 1 +262c6 w D15 1 +262d1 b A1 1 +262e1 b R5 1 +262f1 b O2 1 +26306 w E18 1 +26321 b C3 F4 F3 D8 E6 5 +26331 w M2 1 +26387 w M4 1 +263d6 w C5 1 +26416 w E13 F13 2 +26437 b R7 1 +26440 w G16 1 +26450 b N15 1 +26470 w B3 B4 2 +26487 b N15 1 +264a6 b R17 1 +264b1 w R4 1 +264c1 b D16 1 +264c6 b D1 1 +26526 w S18 1 +26527 w R7 1 +26531 b N4 1 +26560 w G3 1 +26561 w F6 1 +26571 w M17 1 +26587 w C17 1 +26597 w H17 1 +265a6 w O15 1 +265b6 b O3 1 +265e6 b P18 1 +26616 b R17 1 +26620 b P3 1 +266a6 w E4 1 +266d6 w S3 1 +266e1 b M18 1 +266e7 w F7 1 +26740 w P7 N4 2 +267a0 w P16 1 +267c6 b S18 1 +26876 w B3 1 +26896 b P4 1 +26897 b N16 1 +268b7 b E8 1 +268e0 b Q6 Q7 2 +26901 b B4 1 +26911 b H17 1 +26911 w D7 1 +26917 w D2 1 +26930 b P18 1 +26930 w S7 1 +26936 b B16 1 +26970 b L3 1 +26991 b C18 1 +26997 b B15 1 +269b1 b P6 1 +269e7 w P18 1 +269f6 b R11 1 +26a00 w H16 O16 2 +26a07 w M3 1 +26a36 w C6 1 +26a56 w P5 1 +26a76 b E6 1 +26a97 w O4 1 +26aa1 b E6 D7 C5 3 +26aa6 w C17 1 +26ab1 b O14 1 +26ae0 w C16 D16 2 +26ae6 b O14 1 +26af1 w E17 D16 2 +26b20 b C2 1 +26b26 w R15 1 +26b36 b C4 1 +26b60 w P8 1 +26b81 w Q12 1 +26b91 b D17 1 +26ba6 b N3 1 +26bf6 w C3 1 +26c16 w R6 1 +26c21 w D13 1 +26c26 w C4 1 +26c31 b G15 1 +26c31 w C18 1 +26c60 b C16 1 +26ca0 b B3 1 +26ca7 w D15 1 +26cf7 w D6 1 +26d11 w R2 1 +26d41 b D14 D13 2 +26d47 w R17 1 +26d67 b J15 C16 2 +26d71 b Q13 1 +26da6 b G17 1 +26db7 b H3 E2 D2 3 +26dd0 b B2 1 +26dd6 w E3 Q17 2 +26dd7 b T15 1 +26de7 b B13 1 +26df1 w N3 1 +26e21 b D15 1 +26e26 w L13 1 +26e30 b B16 1 +26e71 w Q3 1 +26ec7 b E15 1 +26ed0 b C17 1 +26ed1 b C4 1 +26f31 b B4 1 +26f41 b B17 1 +26f57 w S4 1 +26f66 w Q3 1 +26f97 w R14 1 +26ff7 w M17 1 +27011 w R8 1 +27040 b Q15 1 +27046 b O4 1 +27051 b D15 1 +27051 w B2 C16 2 +27060 b R13 1 +270b0 w B18 1 +270b7 b O18 1 +270d0 b J17 1 +27110 b D3 1 +27116 b B16 1 +27127 b Q9 1 +27137 w B3 D15 2 +27166 b G15 1 +271e0 w M17 1 +271e1 w R13 1 +27207 b Q4 1 +27207 w Q11 1 +27220 b E6 1 +27270 w G13 1 +272b1 b R18 1 +272c0 b F17 1 +272d0 b J17 1 +272e6 w D4 1 +272f7 b E4 1 +27310 b R18 Q18 2 +27326 b N15 1 +27360 b M4 1 +27386 w S12 1 +27397 w H4 1 +273d6 b C3 1 +273f0 w J16 1 +273f6 w B17 1 +27416 b F15 1 +27426 w N16 1 +27450 b O4 1 +27457 b R6 1 +27461 w D2 1 +27496 b M17 1 +274b6 b B6 1 +274c6 w P18 1 +27556 b C3 1 +27591 w C7 R14 2 +275a0 b P14 1 +275e0 w D2 1 +275e6 w B4 1 +275f1 b C3 1 +27607 w Q17 1 +27610 w E6 1 +27660 w O3 1 +276b1 b D3 D5 2 +276c0 w P17 1 +276c7 b F3 P17 2 +27727 b R14 1 +27731 b O17 1 +27736 b G11 1 +27766 w O4 R4 2 +27776 w E6 1 +27787 b C18 1 +277b0 w N3 1 +277b7 w C2 1 +277e6 w B3 1 +27816 w D13 F6 2 +27820 w S15 G16 2 +27861 b P5 1 +27866 w C1 1 +27876 b D6 1 +27877 w E14 1 +27896 b D3 D5 2 +278a0 w O5 1 +278b1 b N2 1 +278c1 w C17 1 +278f1 b G4 1 +27940 w D4 1 +27980 b Q5 Q6 N3 M3 L3 N4 M4 L4 8 +27980 w Q5 L3 R6 R7 R8 Q7 Q6 7 +27996 b E16 1 +279e7 w S18 1 +27a01 w O16 1 +27a71 b D18 1 +27a76 w E14 1 +27ac1 w F17 D15 2 +27ad7 b R14 1 +27b31 w C18 1 +27b51 b F3 1 +27ba7 b Q5 1 +27c66 w P8 1 +27cb7 b Q17 1 +27ce6 w O3 R7 2 +27cf1 b Q7 1 +27d00 w F17 1 +27d01 b Q17 1 +27d37 b E6 1 +27d61 b O4 1 +27d66 b E18 1 +27d76 b C12 1 +27d91 b D9 1 +27d96 b C4 1 +27db6 b B6 1 +27dc0 w Q18 1 +27de0 b P16 1 +27e07 w C4 C3 2 +27e57 b E17 1 +27e60 b P7 1 +27e70 b E3 1 +27e80 b G16 1 +27e87 w R16 1 +27e96 b D6 1 +27eb7 b F2 1 +27ec6 w C12 1 +27ef7 w S13 1 +27f30 b N2 1 +27f47 b B14 1 +27f86 w P17 1 +27f96 b O16 R16 2 +27fd7 b D18 1 +27ff0 b M4 1 +28011 w P18 1 +28040 w M17 1 +28076 b D5 1 +28080 w F14 1 +28090 w F5 1 +280d1 w J16 1 +28111 w B13 1 +28120 w Q13 1 +28127 b R16 1 +28147 w N17 1 +28176 w F5 1 +281b0 w Q18 P17 2 +281b6 b G5 1 +281c6 b S3 1 +281d0 b F8 1 +281e7 w H3 1 +28227 b L17 1 +28241 b C12 1 +28277 w R16 1 +28280 w C3 1 +28291 b F17 1 +28310 w G5 1 +28327 b F17 1 +28347 b B4 1 +28350 w D6 1 +28366 w R13 L17 2 +28381 w R2 1 +28386 b R16 N2 2 +283a0 b G17 E18 2 +283e7 w L16 1 +283f1 b S8 1 +28407 w Q14 1 +28410 w O17 1 +28411 b R6 1 +28431 b Q13 1 +28456 b C7 1 +28456 w P8 1 +28471 w R15 1 +28496 b T16 1 +28496 w B16 1 +28506 b C17 D16 2 +28527 b D2 1 +28551 w C14 1 +28587 w O4 1 +285b7 b B6 1 +285c1 w B6 1 +28630 b F13 1 +28650 b Q14 1 +28651 w B14 1 +28656 w F16 G16 2 +286e1 w D5 1 +28706 b C6 1 +28720 b G3 E2 2 +28767 b M17 1 +28781 b S2 1 +28786 b S16 1 +28786 w E15 1 +28797 w E8 1 +28826 w T16 1 +28840 b P18 1 +28841 w H4 1 +28850 b D6 1 +28886 w Q17 1 +28887 w M16 1 +288e7 w C6 1 +28917 b Q9 1 +28920 b D4 1 +28937 w Q6 1 +28941 w P5 1 +28967 b L4 1 +28990 w P4 1 +289a0 b E18 1 +289a7 w Q4 1 +289e1 w P14 1 +28a57 b N17 1 +28a76 w S14 1 +28ad0 b O4 1 +28ad7 w N17 1 +28b40 w P4 1 +28ba0 b M4 1 +28bd1 w G16 1 +28c20 b Q4 1 +28c81 w D4 1 +28c86 w B16 1 +28ca6 b O4 1 +28ca7 b N14 1 +28ce6 w S15 1 +28d10 b D15 1 +28d97 b P14 1 +28e16 w S2 1 +28e20 w P5 C3 2 +28e76 w N2 1 +28e86 w C6 D5 2 +28ea6 w D6 1 +28ec7 w C4 1 +28ed1 b O13 1 +28ed1 w Q3 1 +28f16 b S17 1 +28f21 w R18 1 +28f66 w O4 1 +28fc6 w E3 D4 2 +28fc7 b R2 1 +29011 b E12 R6 2 +29020 b P8 1 +29041 w N17 1 +29061 b D18 C17 2 +29066 w O16 N16 2 +29077 w C8 1 +290b6 w E7 1 +290c6 b Q5 1 +290f6 w O4 1 +29126 w Q17 1 +29131 b E16 1 +29166 b D17 1 +29166 w D3 1 +29180 w E13 1 +29187 b B14 1 +29191 b T17 1 +291f1 b M17 1 +29237 w F13 1 +29271 b R5 1 +29286 b C4 1 +292a0 b Q19 1 +292c6 b Q14 1 +292f1 w N6 M6 2 +292f7 w B13 1 +29356 b Q14 1 +29367 b J15 1 +293c7 b Q7 Q6 2 +293c7 w Q6 1 +293d0 b R2 1 +293e1 w R13 1 +29421 b D2 C3 2 +29426 b C7 1 +29427 b J17 1 +29476 b S9 1 +29480 w Q9 1 +29496 w Q6 1 +294a7 w R17 1 +294e0 w D16 1 +294e1 w N17 1 +29500 w R14 1 +29556 w Q15 1 +29590 w P17 1 +29597 w E2 1 +295a1 w H16 1 +295e6 b F18 1 +29627 w P6 1 +29631 w S5 1 +29646 w O16 1 +29696 w B18 1 +296a7 w B3 1 +296c7 w P15 1 +296d1 w Q12 1 +29706 w C9 1 +29721 b P11 1 +29741 b P4 R3 N3 Q6 Q8 5 +29741 w Q5 R4 C2 3 +297a7 w R2 1 +297e0 b O5 1 +297e6 b P4 1 +29800 b C16 D17 2 +29806 b C15 1 +29807 w R17 1 +29826 b B5 1 +29857 b J5 C4 2 +29887 b P5 1 +29896 w G15 1 +298b0 w S3 1 +29916 w F18 D12 2 +29917 w G16 L17 P14 3 +29946 b E3 1 +29950 w B17 1 +29961 b O2 1 +29976 b N3 1 +299b0 w P16 1 +299c1 b R4 1 +299e7 w G15 1 +299f0 w C4 1 +299f7 w C17 1 +29a71 w G5 1 +29a81 w O4 1 +29a96 b S14 1 +29a97 b B15 1 +29aa0 b R5 1 +29ac7 w R7 1 +29ad7 w Q8 M4 2 +29af7 b A5 1 +29b80 w Q3 1 +29b81 b N14 1 +29b81 w Q12 1 +29ba0 w F8 1 +29ba6 w E5 1 +29bb0 w C5 1 +29bd0 b N6 1 +29c46 b P14 R14 2 +29c61 b R6 1 +29c77 b N16 1 +29c87 w B17 1 +29c91 w C16 1 +29c97 b E18 1 +29cb6 b E14 1 +29d07 w O5 1 +29d11 b O4 1 +29d20 b Q4 1 +29d26 b E5 1 +29d46 b O6 1 +29d70 w G3 E5 G4 3 +29d77 w E17 1 +29d81 w Q3 1 +29db1 w N16 1 +29db6 w J3 1 +29dc0 b D18 1 +29de0 w B18 1 +29e00 b E2 E4 2 +29e11 b S9 1 +29e50 w C14 1 +29e51 w Q4 1 +29e56 b O2 1 +29e56 w O5 1 +29e57 w E4 1 +29e66 b Q18 1 +29ee6 w F17 1 +29f01 b P15 1 +29f06 b S14 1 +29f27 w O4 1 +29f30 b R17 D14 2 +29f31 b B15 1 +29f37 w O16 1 +29f60 b R18 1 +29f70 w P7 1 +29f80 w E5 1 +29f96 b C2 1 +29ff1 b R13 1 +2a010 b J16 1 +2a010 w M15 1 +2a020 b C15 1 +2a031 w P5 1 +2a051 b S15 1 +2a066 w P15 1 +2a067 w F16 1 +2a0f1 w C14 1 +2a121 b R11 1 +2a167 w S16 1 +2a176 b N17 P17 2 +2a177 b C15 1 +2a191 b Q4 1 +2a197 b O5 1 +2a207 w S17 1 +2a210 w B2 1 +2a211 b F6 1 +2a231 b B15 1 +2a2a1 w J3 1 +2a2c6 b R18 1 +2a337 w D14 1 +2a346 w Q3 1 +2a361 b L17 1 +2a371 w E6 1 +2a390 b O3 1 +2a3a0 b F15 1 +2a3b7 w G16 1 +2a3e7 b D4 1 +2a411 b C2 1 +2a416 b D17 1 +2a431 b G15 1 +2a456 b B3 1 +2a456 w C4 1 +2a490 b B17 1 +2a497 b B5 1 +2a4d6 w O6 1 +2a4d7 w F18 1 +2a500 w T17 1 +2a511 b E17 C14 2 +2a511 w B15 1 +2a540 b L14 1 +2a561 b C16 P12 2 +2a570 b E6 1 +2a570 w R17 1 +2a587 b O14 1 +2a587 w F4 1 +2a5b7 b O17 1 +2a5f0 b E14 1 +2a5f6 b G14 1 +2a616 b S14 1 +2a616 w B2 1 +2a620 w C16 1 +2a627 w S5 1 +2a650 b B15 1 +2a651 w F3 1 +2a667 w R12 1 +2a687 b N18 1 +2a6e1 b P2 1 +2a720 b P16 1 +2a740 w N4 1 +2a766 b C13 1 +2a786 w R4 1 +2a7c1 w Q18 1 +2a7f1 w F16 1 +2a7f7 b C16 1 +2a831 w F4 1 +2a840 w H3 1 +2a866 w N17 1 +2a871 b O15 1 +2a8a7 w G18 1 +2a8b0 b R14 Q16 R16 3 +2a8d1 w B8 1 +2a8e6 b O17 1 +2a8f6 w O18 1 +2a901 b T13 1 +2a990 w F5 1 +2aa06 w P3 1 +2aa16 w C9 1 +2aa41 w Q15 1 +2aa46 w N16 1 +2aa87 w E14 E3 2 +2aa96 b S17 1 +2aa97 b R15 1 +2aaa1 w C15 1 +2aad1 w S5 1 +2ab06 b E3 1 +2ab07 w Q9 1 +2ab10 b R3 1 +2ab46 b M16 1 +2ab71 b C16 1 +2aba7 w F18 1 +2abb6 w E16 1 +2abc6 b D14 1 +2abd0 w N4 1 +2abd7 b O2 1 +2abf1 b E16 1 +2ac37 w F2 1 +2ac50 w Q14 P17 O15 M17 4 +2ac51 b M16 1 +2ac80 b C6 1 +2ac97 w C3 1 +2acb7 w O4 1 +2acc1 w R16 Q15 R17 3 +2acc7 w F16 1 +2acf6 w B18 1 +2ad00 b E5 1 +2ad56 b R6 1 +2ad56 w Q14 1 +2ad61 w B15 1 +2ad91 b Q17 1 +2adb0 w R13 1 +2adc1 b P12 1 +2add1 w S4 1 +2ade0 b C7 1 +2adf0 w E7 1 +2adf1 w R17 1 +2ae20 b Q14 P17 O15 M17 4 +2ae30 w H5 1 +2ae40 w O4 R5 P6 R8 4 +2ae46 b S3 1 +2ae51 b P5 1 +2ae80 w F18 1 +2af00 w H17 1 +2af01 w Q6 1 +2af36 b B4 1 +2af51 w D13 1 +2af96 w D14 1 +2afa0 b R4 1 +2afc1 b D4 1 +2afe0 b E16 1 +2afe7 w P16 1 +2b006 w P17 S17 F2 3 +2b007 w P4 1 +2b047 w D5 E4 2 +2b076 w S5 1 +2b081 b O4 1 +2b090 w B15 1 +2b0a1 w O17 1 +2b0e1 w C4 1 +2b160 b O3 1 +2b1b0 w P13 1 +2b1d0 w S3 1 +2b1d6 w R2 1 +2b1f7 b S18 L17 2 +2b211 b E2 1 +2b221 b D2 1 +2b247 b M4 1 +2b261 b C5 1 +2b266 b P5 1 +2b270 w O14 1 +2b2e7 b D13 D16 2 +2b321 w N4 1 +2b340 w E15 1 +2b341 b R16 1 +2b360 b D19 1 +2b397 w N4 Q4 2 +2b3c1 w D17 1 +2b3d0 b S5 1 +2b3f0 b R8 1 +2b417 b H3 1 +2b4b7 w C18 1 +2b4e0 b G5 1 +2b530 w Q5 1 +2b531 w N18 1 +2b546 b Q6 1 +2b561 w M17 P18 Q18 3 +2b566 b Q12 1 +2b567 b E5 1 +2b577 b B5 1 +2b5b0 b P6 1 +2b5e1 b Q3 1 +2b5e6 b Q7 1 +2b601 w G18 P17 2 +2b670 w C12 1 +2b686 w O14 1 +2b691 b C3 1 +2b6a7 w Q14 1 +2b6e1 b M16 1 +2b700 b P18 1 +2b700 w R15 1 +2b721 w Q13 1 +2b740 b J4 1 +2b7a0 w H14 1 +2b7c6 b Q15 1 +2b7c7 w O15 1 +2b7d0 b S15 1 +2b7f1 b B13 1 +2b7f1 w C13 1 +2b7f6 b R15 1 +2b870 w O4 1 +2b880 b P14 1 +2b921 w Q2 1 +2b926 w F17 1 +2b950 w N4 1 +2b987 b G4 1 +2ba00 w S17 1 +2ba01 w Q2 S3 2 +2ba20 w B2 1 +2ba26 b H18 1 +2ba56 b R17 1 +2ba57 w B15 1 +2ba61 w H5 1 +2bab6 b Q7 1 +2bab6 w S18 1 +2bab7 w F6 1 +2bac7 w C14 D16 2 +2bad7 w S17 1 +2bb20 b O5 1 +2bb87 b S7 1 +2bba0 w O13 R16 2 +2bba1 b S15 1 +2bbc6 b P3 1 +2bbf7 w M4 1 +2bc07 b P17 1 +2bc61 b C4 1 +2bc71 w D4 1 +2bc80 w R3 1 +2bc81 b D6 1 +2bca0 b H16 1 +2bca7 b Q9 1 +2bcc0 w R4 1 +2bd17 b P16 Q17 2 +2bd17 w Q15 R17 R13 O16 M16 5 +2bd21 w R17 1 +2bd31 w D14 1 +2bd41 b J17 1 +2bd57 w P18 1 +2bd70 w Q14 1 +2bda7 b R16 1 +2bdb7 b D8 1 +2bdd7 b F18 1 +2bdf7 b R5 1 +2be07 b R4 1 +2be07 w Q13 R12 R11 3 +2be36 b D3 1 +2be60 b E17 1 +2be76 b P5 1 +2be86 w R3 1 +2bea1 w D17 G6 2 +2beb6 w P5 1 +2bed0 w R17 1 +2bee0 b B3 1 +2bee7 w D16 1 +2bf07 w Q18 1 +2bf71 w G2 1 +2bf80 b D5 1 +2bf90 w Q6 1 +2bfb1 w S17 1 +2bfc6 w S3 1 +2bfe1 b C18 1 +2c016 w R4 1 +2c027 b C11 1 +2c041 w G15 1 +2c086 b E4 1 +2c097 b C5 1 +2c0f1 b F16 1 +2c0f7 b M17 1 +2c127 w D18 B16 2 +2c176 w R2 1 +2c181 b P3 1 +2c221 w P17 1 +2c227 w S5 1 +2c230 w C14 F15 2 +2c250 w G2 G5 2 +2c257 w F15 D7 2 +2c261 b H18 1 +2c2a0 w Q15 1 +2c2b0 w Q2 B6 2 +2c2b7 w Q6 1 +2c2c6 b J18 1 +2c2d6 b P6 1 +2c316 b F4 F3 C5 E5 C7 D7 D8 7 +2c316 w C3 E3 F4 G4 G3 5 +2c357 b L17 1 +2c367 b O13 1 +2c397 w R4 1 +2c3b0 b Q4 1 +2c3f1 b Q17 1 +2c406 w G17 1 +2c427 w E2 1 +2c441 w C2 C5 D6 C7 B7 5 +2c490 w R13 1 +2c491 b C15 1 +2c4f1 w Q6 P2 2 +2c511 w R4 1 +2c526 w C14 1 +2c557 b Q3 1 +2c557 w Q17 1 +2c5a0 w E4 1 +2c5d1 b G5 1 +2c5d7 b R13 1 +2c5e0 w E17 1 +2c5f6 b C5 1 +2c627 b S13 1 +2c641 b P6 1 +2c6d6 w R19 1 +2c6f0 b O7 1 +2c757 b D5 1 +2c771 w C12 M3 2 +2c871 b B15 1 +2c8b0 b P4 1 +2c8d0 b Q18 1 +2c901 w P19 1 +2c910 b O3 1 +2c917 b E16 F16 2 +2c937 w E17 1 +2c941 b H6 1 +2c946 w P17 1 +2c987 b R17 1 +2c9b0 w Q6 R6 2 +2ca10 w G16 1 +2ca21 b F16 1 +2caa1 b B13 1 +2cab7 w C17 1 +2cac0 b P2 1 +2cae0 w N18 N15 2 +2cb07 w S5 1 +2cb21 b G14 1 +2cb21 w D12 1 +2cb41 b R5 1 +2cb50 b E18 1 +2cb77 b R7 1 +2cb87 w O16 1 +2cb91 w Q3 1 +2cba7 b P5 1 +2cbc0 b C7 F3 2 +2cbc7 w B7 1 +2cbf0 b G3 1 +2cc00 b P17 1 +2cc26 b S3 1 +2cc36 b P3 1 +2cc51 w B16 1 +2cc67 w B5 1 +2cc76 w Q18 C12 2 +2cca7 w R18 1 +2ccb0 w P2 1 +2ccb1 w F7 1 +2ccc7 w R12 1 +2cce1 b R2 1 +2ccf0 w F5 1 +2ccf6 b O6 1 +2cd11 b S4 1 +2cd47 w G17 1 +2cd66 w J17 1 +2cdb6 b F17 1 +2cdc6 w P14 1 +2cde0 b E16 1 +2cdf0 b Q15 1 +2ce67 w S16 1 +2ce76 w E3 C5 2 +2ce80 b R2 1 +2ceb6 w Q14 1 +2cf10 w E17 1 +2cf21 b Q1 1 +2cf67 b D14 C13 2 +2cf77 w Q5 1 +2cfa6 w F14 1 +2cfb1 w E13 1 +2cfe6 w O16 1 +2cff0 w N16 P14 2 +2cff1 w D17 1 +2d017 w P17 1 +2d026 b M16 1 +2d036 w F3 1 +2d050 w D11 1 +2d051 b G6 1 +2d077 b R11 1 +2d097 w F5 1 +2d0c6 w Q5 P4 2 +2d0d7 w C17 1 +2d0e1 w E4 1 +2d0e6 b C6 E5 2 +2d0f7 w P16 1 +2d101 w E8 1 +2d120 b C7 1 +2d130 w F5 1 +2d137 w N5 1 +2d157 w Q4 1 +2d161 w P6 1 +2d186 w E13 1 +2d1b1 w C4 1 +2d1e7 w Q3 1 +2d1f6 b E3 1 +2d216 b E6 1 +2d270 b R2 1 +2d2a7 w C4 1 +2d2b7 w S14 1 +2d2c7 b J18 1 +2d2d1 b R18 1 +2d336 w R16 1 +2d3d0 b G5 1 +2d3f1 w R3 1 +2d406 w Q15 1 +2d416 b P3 1 +2d436 w C4 1 +2d437 b F15 1 +2d441 w S6 1 +2d461 b C13 1 +2d470 b P13 N16 2 +2d4b6 w B16 1 +2d4c1 w S18 1 +2d4d0 w R6 1 +2d510 w A15 1 +2d550 b Q4 1 +2d577 w F16 1 +2d580 w D17 1 +2d587 w S13 1 +2d591 b L4 1 +2d597 b L15 1 +2d5c0 b F5 1 +2d5d0 w O2 1 +2d5d1 w R15 1 +2d5e0 w E8 1 +2d5f1 w T3 1 +2d637 b F16 1 +2d661 w G14 1 +2d677 b N4 1 +2d697 w M4 1 +2d6a0 b D6 C6 C7 F4 H3 H4 J4 7 +2d6a0 w C6 1 +2d6a7 b S13 1 +2d6c0 w E15 1 +2d721 b J16 A14 2 +2d721 w N17 1 +2d731 b Q3 1 +2d750 w Q6 1 +2d766 w F16 1 +2d780 w Q5 R5 2 +2d7a6 b Q3 1 +2d7a7 w M16 1 +2d7b1 w C8 1 +2d7c6 w O16 1 +2d7e0 w N5 1 +2d850 b F4 C5 E6 C8 4 +2d871 w E4 C7 D5 3 +2d877 b G18 H15 2 +2d897 b R4 1 +2d8a0 b E4 1 +2d8b7 w Q3 1 +2d8d6 w F16 1 +2d906 b D7 C7 C3 3 +2d946 b B11 D7 2 +2d956 w C2 1 +2d961 w E5 1 +2d9a1 w R3 1 +2d9e6 b A3 1 +2d9f7 b Q13 1 +2da17 b R11 1 +2da31 w S6 1 +2da67 b D2 1 +2daa0 b O5 1 +2daa1 w Q17 S16 2 +2dae6 b E2 1 +2daf1 b G3 1 +2db16 b E5 1 +2db27 b D16 1 +2db36 w S13 1 +2db70 b C2 1 +2db71 w Q18 1 +2db87 b S4 1 +2dba6 w P7 1 +2dbe7 b R12 1 +2dc20 w E18 1 +2dc21 b M6 1 +2dc31 w P9 R5 2 +2dc41 w S16 1 +2dc76 w C18 1 +2dc97 w C5 B6 B5 3 +2dcb6 b F4 1 +2dcd0 b E1 1 +2dd51 w H16 1 +2dd67 w O3 1 +2dda6 w E15 1 +2ddd0 b P4 1 +2ddd6 w S16 1 +2ddf6 b G4 1 +2de40 w E5 1 +2de46 w E17 1 +2de56 w S3 1 +2de60 w C6 1 +2de70 b E5 1 +2de76 b D5 1 +2de81 b S4 1 +2de91 b Q4 1 +2dea0 b O19 1 +2dec1 b B15 1 +2ded6 b R17 1 +2dee0 w R17 1 +2def7 b G4 1 +2df30 w Q15 1 +2df57 w C16 1 +2dfa6 b C6 1 +2dfe1 b G13 1 +2dfe7 w D14 1 +2dff6 b E4 1 +2e000 b J17 1 +2e016 w H17 1 +2e020 b O16 1 +2e027 w E15 1 +2e036 b B15 1 +2e037 b E18 1 +2e130 w E1 1 +2e136 b S17 1 +2e1b0 b D5 1 +2e1b1 b D6 1 +2e1d1 b S4 1 +2e1d6 w O4 1 +2e1d7 w M14 1 +2e1f1 w M14 1 +2e216 b C12 1 +2e237 b E17 1 +2e276 b E15 1 +2e2b6 b G5 1 +2e2c1 b C9 1 +2e2c7 w S4 R3 2 +2e2f0 w C15 1 +2e301 b O5 1 +2e301 w R17 1 +2e337 b F16 1 +2e350 b C3 1 +2e357 w B12 1 +2e367 b L2 1 +2e3e1 w O16 R17 2 +2e430 w P17 1 +2e460 w C13 1 +2e4d1 b D2 1 +2e506 b O3 1 +2e521 w D12 1 +2e557 w R3 1 +2e577 b S13 1 +2e5e1 b D12 1 +2e600 w L14 1 +2e610 w S17 1 +2e620 w B7 1 +2e627 b O17 1 +2e6a1 b R2 1 +2e6e6 b E8 1 +2e701 b S4 1 +2e710 b M15 1 +2e736 w M16 1 +2e750 b E4 1 +2e757 w D8 1 +2e770 b B6 1 +2e780 w R13 1 +2e796 w N4 1 +2e797 w S5 1 +2e7f1 w Q5 1 +2e861 b P2 1 +2e880 b F7 1 +2e8a6 w S13 1 +2e8e6 b F16 1 +2e8f0 w G2 1 +2e8f1 w M17 1 +2e900 w D7 1 +2e910 w R13 1 +2e937 b P4 F7 2 +2e940 b D18 F16 2 +2e961 w C2 1 +2e966 b P17 1 +2e996 w F16 1 +2e997 w N2 1 +2e9b7 w N6 1 +2e9c7 w E7 C15 2 +2e9d7 w D6 C7 2 +2e9f0 b P12 1 +2e9f6 b B2 1 +2ea31 b P17 1 +2ea57 w E13 1 +2ea86 b E18 B18 2 +2eae7 b F18 1 +2eae7 w Q3 1 +2eb57 b E14 1 +2eb91 b M4 1 +2eb97 w B15 1 +2eba6 b R14 Q14 2 +2ebb1 b E15 1 +2ebc6 b Q12 1 +2ebc7 w D11 1 +2ebd0 w B4 D6 2 +2ebd7 b P12 1 +2ebe1 w Q7 O6 2 +2ebf7 w P2 1 +2ec46 b R7 1 +2ec71 b H18 1 +2ec77 b D12 1 +2eca0 b O4 Q5 2 +2eca1 w B2 1 +2ecb1 w N6 1 +2ece0 b D1 1 +2ed16 w C16 1 +2ed17 w O4 1 +2ed50 w G6 1 +2ed57 w N7 1 +2ed80 b R11 1 +2eda0 b D17 1 +2edd1 b S3 1 +2ee01 b Q7 1 +2ee17 w Q2 1 +2ee21 w F16 1 +2ee26 w Q7 1 +2ee40 w P18 1 +2ee50 w R15 1 +2ef41 w F16 1 +2ef57 w C18 1 +2ef91 b O16 1 +2efb0 w G16 1 +2efd1 w R13 L4 2 +2efe7 b F15 F14 C15 C14 4 +2f007 w F12 1 +2f017 b F17 1 +2f040 w H3 1 +2f081 b O4 1 +2f087 b C14 1 +2f091 w E15 1 +2f126 b H2 1 +2f147 b Q5 1 +2f1a6 b O16 1 +2f1b0 w C18 D18 2 +2f1e6 w C2 1 +2f216 b O18 1 +2f221 w P16 1 +2f231 b E5 1 +2f237 b D16 1 +2f256 w F3 1 +2f2b1 w O5 1 +2f2b6 b G18 1 +2f2d0 w O13 1 +2f307 w N5 1 +2f327 w F14 G13 2 +2f3d1 b M4 1 +2f431 b S15 1 +2f441 b P7 1 +2f460 w E4 1 +2f480 w S18 1 +2f491 b G5 1 +2f4a0 b H3 1 +2f4e1 b R4 1 +2f4f1 b N3 1 +2f5a6 w R4 1 +2f5a7 w E2 1 +2f5b7 w G1 1 +2f5c0 w D4 1 +2f5e0 b F17 1 +2f5e6 b H17 N14 2 +2f620 w C3 1 +2f631 w S3 1 +2f647 w N5 1 +2f6a1 b F16 G17 2 +2f6b1 b E18 1 +2f6d0 w F4 1 +2f6e0 w B13 1 +2f727 b J17 1 +2f7b6 w G16 1 +2f7f1 b C19 1 +2f7f6 b Q3 1 +2f821 b G15 1 +2f857 b H5 1 +2f896 b B3 1 +2f8a1 w D12 1 +2f8b0 b P5 1 +2f8b7 w C18 1 +2f8f0 w T2 1 +2f8f1 b B13 1 +2f907 w C17 1 +2f921 w R3 1 +2f951 b P18 1 +2f966 b C2 1 +2f980 w Q2 1 +2f9a1 b A18 1 +2f9b1 b S6 1 +2f9f0 w C13 1 +2fa01 w T17 1 +2fa30 w S18 1 +2fa41 b F5 1 +2fa60 b H4 1 +2fa70 w F18 1 +2fa81 w M4 1 +2fa87 w F11 1 +2faa1 w F7 1 +2faa6 w P6 1 +2fab6 b D14 1 +2fac7 w O15 1 +2fae1 b R2 1 +2fae1 w P15 1 +2fb07 b C15 1 +2fb11 w C1 1 +2fb20 b J18 1 +2fb50 b C3 1 +2fb50 w M5 1 +2fbb1 w S3 1 +2fc26 w B17 1 +2fc46 b E13 1 +2fc86 w S12 1 +2fc87 b C17 1 +2fc90 b P7 1 +2fcc7 w G4 1 +2fce7 w G3 1 +2fcf1 b D7 1 +2fd01 b D18 1 +2fd10 b H15 1 +2fd36 w O6 1 +2fd41 b S3 1 +2fd61 b F4 1 +2fd70 b R5 1 +2fd76 b C15 1 +2fd86 w O15 1 +2fda6 w E17 B17 2 +2fdb0 b P4 1 +2fdc7 b H3 1 +2fe27 w D13 1 +2fe67 b E12 1 +2fe67 w C2 1 +2fe97 w B17 1 +2fee1 w Q2 1 +2ff01 w F17 1 +2ff21 w C17 C16 J17 H17 E18 D17 6 +2ff57 w P16 1 +2ff87 w E16 F16 D15 E17 4 +2ff96 b G17 1 +2ffa6 b H16 1 +2ffb1 b P7 Q6 2 +2ffc1 b S4 R3 2 +2ffe6 w R7 1 +30000 w Q9 1 +30036 w E18 1 +30037 w D4 1 +30106 b B17 1 +30131 w Q4 1 +30161 w Q3 1 +30197 w P6 1 +301b6 b B16 1 +301f7 w M4 S3 2 +30201 b Q8 1 +302c7 b Q13 1 +302d6 b E15 1 +302e7 w M5 1 +30361 b E5 F5 C3 G3 4 +303d6 b G16 1 +30431 w C6 1 +30446 w B15 1 +30457 b R13 1 +30487 w B12 1 +304d1 b C4 1 +304e7 b M17 1 +30500 w Q2 1 +30521 b G4 1 +30527 w N15 1 +30547 w D4 1 +30570 b D5 1 +30586 w R2 1 +30590 b D11 1 +30590 w D14 1 +305b6 w C16 E16 2 +305d6 b P14 1 +305e1 w F3 1 +30600 b O11 1 +30681 w R17 1 +30686 b E5 1 +306c0 w M13 1 +306c7 b H18 1 +306d0 w Q5 1 +306e6 b H5 1 +30767 w R15 N18 2 +30771 b D7 C8 C9 Q12 Q16 5 +30790 b J4 1 +30796 b P2 1 +307b7 b P3 1 +307b7 w S17 1 +307d0 w G3 E5 2 +307e7 b O15 1 +307f1 w D5 1 +30820 b N3 P2 2 +30867 b B2 1 +30880 b P3 1 +30890 b O13 1 +308a1 b B3 1 +308d1 w B3 1 +30920 w F3 1 +30940 b S5 1 +30977 w N13 1 +309c7 b C15 1 +309e1 w E17 1 +309e6 b H18 1 +30a07 b Q3 1 +30a51 w Q2 1 +30a57 b R17 1 +30a66 w N2 1 +30af0 w S5 1 +30b01 b R17 1 +30b81 b S6 1 +30ba6 b J17 1 +30bb0 w Q6 1 +30bc1 w B6 1 +30bd0 b E15 1 +30bf0 b S17 1 +30c11 w C11 1 +30c17 b Q14 P13 2 +30c41 b B8 1 +30c76 b R17 1 +30c87 b C2 1 +30c90 w F18 1 +30cb0 w R9 1 +30cb6 b R5 1 +30cf1 b S3 1 +30cf6 w P12 1 +30d70 b F16 1 +30d76 b C2 1 +30d86 b B1 1 +30da6 b E15 1 +30dd7 b Q7 O8 2 +30e21 b B2 1 +30e60 b R17 1 +30e60 w D4 1 +30e71 b Q17 1 +30e76 w N4 N3 R3 3 +30e96 w D18 1 +30ea1 b R16 1 +30ea6 b J17 1 +30eb6 b P18 1 +30f27 b J4 1 +30f37 b S5 1 +30f41 b E17 1 +30f46 b Q17 1 +30f91 b R17 P18 N16 3 +30fa6 b R15 1 +30fb7 b G17 1 +30ff1 b T17 1 +30ff6 w D16 1 +31006 w Q15 1 +31041 w J4 1 +31051 w D11 1 +31076 w N16 1 +31087 w R7 1 +31091 b O15 1 +31096 b P15 1 +310d6 w F17 1 +310e1 b S17 1 +31101 w D8 1 +31106 b S14 1 +31146 w R11 1 +31150 b O8 O15 2 +311e0 w O17 1 +311e1 b Q7 1 +31217 w S17 1 +31226 b D5 E4 2 +31230 w F5 1 +31231 b Q18 1 +31240 w F4 C5 E6 C8 4 +31281 b D18 1 +312b6 w O3 1 +312c6 b S3 1 +312d0 b P16 1 +312d1 b P17 1 +312e0 b S2 1 +31300 w F16 1 +31321 w D14 1 +31330 b D15 1 +31331 b O17 1 +31361 w B17 1 +31371 b D5 1 +31376 b F16 1 +31396 w Q4 1 +313b0 w E18 1 +313c0 w B13 1 +313d0 w A17 1 +313e1 w S7 1 +313e6 b F15 1 +313f6 b D15 1 +31436 w D13 1 +31441 w E6 1 +31451 b N15 1 +31460 w C14 1 +31467 b P2 1 +31476 w E18 1 +31487 b G2 1 +31491 w P15 R14 2 +314b7 w B16 1 +314e7 w R17 1 +314f7 w F2 1 +31511 w B14 1 +31537 b C7 1 +31541 w L18 1 +31570 w P16 1 +31587 b R2 1 +315c1 w B18 1 +315c7 w Q3 1 +315d0 b Q15 1 +315d1 b J4 1 +315f0 w R6 1 +315f6 w D14 1 +31601 b L4 1 +31611 w M3 1 +31616 b E5 1 +31631 w D16 1 +31636 w G2 1 +31660 b R4 Q4 2 +31670 b P16 1 +316a0 w O4 1 +316a1 b Q17 1 +316b0 b S3 1 +316c0 b E14 1 +316d1 w A15 1 +316e7 b G3 1 +316f7 w D16 1 +31740 w F5 1 +31760 b E3 1 +317a6 b B8 1 +317b0 w E15 1 +31820 b L17 1 +31896 w M5 1 +318a1 b C6 1 +318a6 w L17 1 +318b1 w Q17 1 +31901 b E2 1 +31916 w E5 1 +31967 b F17 1 +31971 w G17 1 +31976 w S18 1 +31977 w S3 1 +31991 w S15 1 +319a6 b G15 1 +319b7 w P4 1 +31a57 b Q5 1 +31a71 w R14 1 +31a97 b Q13 1 +31ad1 w O18 1 +31ad7 w S4 1 +31b17 w F16 G17 2 +31b27 b E2 1 +31b27 w C5 F3 2 +31b60 w P14 1 +31bd6 b T16 1 +31be1 b Q13 1 +31bf1 b O6 1 +31c21 b C17 1 +31c40 b R6 1 +31c46 b R7 1 +31c70 b D15 1 +31c90 b E16 C11 F17 G17 H17 G16 F16 7 +31c90 w E16 F16 C13 C12 C11 D13 D12 D11 8 +31ca6 w S18 1 +31ca7 b C4 1 +31ce0 b S15 1 +31d01 w R16 1 +31d16 w Q18 1 +31d17 b E14 1 +31d56 b S17 1 +31d96 w D16 1 +31db1 b P4 1 +31e57 w M2 1 +31e70 b C14 1 +31e77 w C17 B15 D13 3 +31e81 w Q18 1 +31e90 w C3 E3 2 +31ef7 b O14 1 +31f10 b P5 1 +31f17 b B6 1 +31f47 b C8 1 +31f50 b F3 1 +31f67 w R3 1 +31f77 b R3 1 +31fa7 b M3 P2 Q2 L16 4 +31fb0 w G3 1 +31fc0 b D9 1 +31ff6 b F6 1 +32081 b D8 H4 2 +32086 b S5 S2 2 +32096 b O15 1 +320a1 w P15 1 +320b7 w Q3 1 +320d0 b H4 1 +320e7 w S16 1 +320f0 w O16 1 +320f1 b R16 1 +320f7 w J6 1 +32107 w C19 1 +32120 b B2 1 +32120 w E3 1 +32126 b D14 1 +32127 b S3 1 +32140 b F17 1 +321a0 w F15 1 +321b0 w C6 1 +321d1 b C17 1 +32210 w O4 1 +32211 b R3 1 +32251 b C17 1 +32257 b D17 1 +32266 w D13 1 +32287 w B3 1 +32296 w P15 1 +322a7 w L2 1 +322b6 b R16 1 +322c7 w H4 1 +32311 b G5 1 +32340 w P3 1 +32357 w H18 1 +32376 b S5 1 +32386 w C3 1 +323b7 w B17 1 +323c0 b R13 1 +323c0 w O12 1 +323c6 w E17 1 +32421 w F3 1 +32466 b O3 1 +32480 b S13 1 +324b7 w C3 1 +324c1 w O15 1 +324c6 b F3 E4 2 +324f7 w S17 1 +32500 b J16 1 +32507 b D12 1 +32510 w F5 1 +32571 b G16 1 +32577 b P6 1 +325c0 w G6 1 +325e1 w M13 1 +325f6 b C6 1 +32621 b D6 C7 Q6 3 +32630 b B17 1 +32676 b D6 1 +326e0 b D16 1 +32736 b R2 1 +32746 b P8 1 +32761 b C4 1 +32766 b R5 1 +32776 w O6 1 +32797 w S17 1 +327b1 b E18 1 +32827 w P4 1 +32841 w B15 1 +32860 b R6 1 +32896 b F14 1 +32897 w D7 1 +328a6 b E4 1 +328c0 b E17 1 +328c0 w E5 H5 2 +328c1 b Q11 1 +328f0 b B2 1 +32900 b F16 1 +32900 w P4 1 +32931 w E2 1 +32951 w R3 1 +32966 w C8 1 +32996 w D13 C13 C17 3 +329a0 w O2 C17 G16 3 +329c1 w G3 1 +32a16 w E12 1 +32a20 w R5 1 +32a40 b E16 1 +32a56 w F3 1 +32a60 b E6 1 +32a81 w G3 1 +32a97 w D2 1 +32ac0 b M17 1 +32b10 w Q13 1 +32b46 w G2 1 +32b67 w D14 1 +32b76 w F4 1 +32b87 b E8 1 +32b87 w D3 1 +32ba0 w D6 1 +32bb0 b Q5 R7 2 +32bb1 b R17 Q16 Q15 P14 Q14 Q13 6 +32bb1 w Q17 1 +32be6 w B14 1 +32c57 w N3 M2 2 +32c67 w Q6 1 +32cd1 w B4 1 +32ce6 b F17 1 +32cf0 b D6 1 +32d21 w B3 1 +32d36 b E5 1 +32d87 b O18 1 +32da1 b S17 1 +32df0 w M16 1 +32df7 w M17 1 +32e07 w E15 1 +32e30 w Q16 1 +32e57 b O6 1 +32ea1 w F18 N4 O6 3 +32ec0 b E4 1 +32ec1 b P18 1 +32ed1 b D17 1 +32ed1 w C5 1 +32f10 b B16 D14 2 +32f31 b N4 1 +32f37 w N15 1 +32f46 w D2 1 +32f61 w E16 1 +32f80 w D15 1 +32f90 w R3 1 +32fa0 b F2 1 +32fb6 b D2 1 +32fc0 w G16 1 +33016 w Q14 1 +33036 b E3 F5 2 +33090 w F17 1 +33096 b D5 1 +330b0 b R14 1 +330c0 w R17 M17 2 +330e0 w H5 G5 E3 E5 4 +33131 w R12 1 +33151 b Q6 1 +33190 b F18 1 +33197 w R15 1 +331a1 w O7 1 +331d1 b D17 1 +331d6 b M17 1 +33217 b G18 1 +33221 w E14 1 +33231 b R13 1 +33260 b P5 1 +33291 w F4 1 +33297 b N18 1 +332c0 b D17 1 +332d6 b R7 1 +332f7 b B1 1 +33317 b C6 1 +33377 b O17 1 +333b1 w C11 1 +333d7 b R8 1 +333e6 w R14 1 +33426 w M16 1 +33436 w B15 1 +33476 b H4 F3 2 +334d0 w E2 1 +334d1 b R7 1 +334d6 w R6 Q7 2 +334d7 w S8 1 +334e6 w R3 1 +334e7 w E2 1 +33500 b R16 1 +33507 b N15 1 +33537 w C4 1 +33550 w C12 1 +33566 b D16 1 +33566 w E6 1 +33577 w B16 1 +33590 w P5 1 +335d6 b M3 1 +335e6 w S19 1 +335f1 w Q16 1 +33607 w D8 1 +33611 w Q3 1 +33617 b B7 1 +33626 b P3 1 +33661 b M2 1 +33677 w D3 1 +336b0 w F16 1 +33701 w Q15 1 +33726 b R15 1 +33750 b C5 1 +33761 w G4 1 +337c6 w C6 1 +33820 w E13 1 +33877 w P5 1 +338a0 b E4 1 +338b1 b R4 1 +338c1 w C2 1 +338e0 b P18 1 +338e6 w P7 1 +33926 b F7 1 +33970 b C3 1 +33987 w D14 1 +33990 b N15 1 +339a1 w L16 1 +339c0 b G16 1 +339d7 b Q12 1 +33a20 b R4 Q3 2 +33a30 w P14 H15 H14 3 +33a86 b R5 1 +33ab1 w G16 1 +33b00 b P17 1 +33b21 b B3 1 +33b36 b R12 1 +33b56 w C4 1 +33c11 w G4 1 +33c26 b D7 1 +33c50 w D5 1 +33c81 w F13 1 +33cc6 b S7 1 +33ce6 w E7 1 +33d06 w E3 B3 2 +33d56 w F17 1 +33d61 b P4 1 +33d70 w L16 1 +33d80 b R15 1 +33d96 b O17 P16 2 +33d97 w G5 1 +33da0 w Q16 1 +33db0 w O16 1 +33dd0 w H4 1 +33dd6 w R16 1 +33de1 b P18 1 +33de6 b D7 1 +33df6 b P17 1 +33e20 w S2 1 +33e21 b M17 1 +33e31 w C18 P4 P6 3 +33e46 b P12 1 +33e60 w R9 1 +33e67 b S6 1 +33eb0 b P16 O16 R13 R12 R11 Q13 Q12 Q11 8 +33eb0 w P16 R11 O17 N17 M17 N16 O16 7 +33ec1 b Q4 1 +33ec7 w B6 1 +33f00 b H3 1 +33f97 w P15 1 +33fd6 w R8 1 +34006 b S12 1 +34017 b C16 1 +34050 w F3 1 +34051 b D3 1 +34097 b E5 1 +340b1 w C17 1 +340d7 b Q17 1 +340e6 w C14 1 +340f6 w P17 1 +34126 w A3 1 +34156 b Q6 1 +34181 w E4 1 +341a0 w E3 1 +341b1 b O8 1 +341d7 b N15 1 +341e0 b C5 1 +34271 w O4 S5 2 +34281 b P18 1 +34291 w S14 1 +342c6 w D15 1 +342d7 b H17 1 +342f0 b B2 1 +34320 b P15 1 +34320 w R2 1 +34347 b Q8 1 +34370 w P2 1 +343a0 w O5 1 +343b7 w B5 1 +343c0 b D5 1 +34406 b N2 1 +34431 b F6 1 +34431 w E2 1 +34436 w S6 1 +34440 b M18 1 +34446 w P5 1 +34450 w E16 1 +34471 w R16 1 +34490 w G15 1 +344a1 b D3 1 +344b0 b P4 1 +344b1 b F2 1 +344b7 w C18 1 +344c7 w J6 1 +34516 w O17 O16 2 +34537 w D17 1 +34550 b R5 N15 2 +34550 w P7 1 +345a0 b R12 1 +345a7 w F16 G15 2 +345b0 b M5 N5 P3 P5 4 +345c7 w S19 1 +345d1 b D3 1 +345e6 b M16 O17 2 +34691 w B16 1 +34697 w G6 1 +346b6 b S16 1 +346b6 w E6 1 +346c7 w S15 1 +34731 w R3 1 +34776 b Q7 1 +34791 b B4 1 +347a0 b D15 1 +347a1 w P17 1 +347b0 b D14 1 +34801 b C18 1 +34827 b R5 1 +34857 w E17 1 +34860 w F1 1 +34867 w N17 1 +348d1 b O2 1 +348e6 b N16 1 +348f0 w B15 C16 2 +34916 w H17 1 +34926 w Q6 1 +34937 b D14 1 +34950 b S3 1 +349a6 w R15 1 +34a10 b E3 1 +34a51 w R3 1 +34a71 b E2 1 +34a87 b C5 B5 D4 3 +34aa0 w E15 1 +34ad7 b D2 1 +34ae0 w N3 P5 N4 3 +34af0 w O5 1 +34af1 b E5 1 +34b17 b Q4 1 +34b40 b C6 1 +34b40 w R3 R5 2 +34b41 b F12 1 +34b57 b C17 1 +34b67 w N15 1 +34ba6 b O16 1 +34bb0 b F17 1 +34bc0 b H5 1 +34c17 b F17 1 +34c31 b M17 1 +34c31 w E3 1 +34c50 b O6 1 +34c96 b R15 1 +34ca1 w D3 1 +34cf7 w R18 1 +34d11 w N15 1 +34d17 w C3 1 +34d97 b E15 F17 2 +34db6 b E16 1 +34e07 b N18 1 +34e21 w P3 1 +34e26 b O15 1 +34e30 b C5 1 +34e31 b D5 1 +34e31 w E4 E5 D7 C3 R7 5 +34e51 w H17 1 +34e60 w Q18 1 +34eb0 b E14 F16 2 +34f10 w R18 1 +34f51 b R14 1 +34f67 w O8 1 +34f97 b N18 1 +34ff6 b C2 1 +35066 b R8 1 +350c1 b A17 1 +350c7 b P6 1 +350e0 b E5 1 +350f1 b C14 1 +35101 w Q7 1 +35140 w O6 1 +35150 w E2 1 +35170 b C3 1 +35190 b F5 1 +35190 w O4 1 +351a6 b H4 1 +351a7 b C16 1 +351d0 w O15 1 +35221 b E7 1 +35257 w R16 1 +352a7 w P3 1 +352d0 w B17 E5 2 +352f0 w P16 1 +35316 w R15 1 +35320 b P15 1 +35336 b D12 1 +35346 b P6 R6 2 +35346 w Q5 1 +35366 b M17 1 +35376 b F16 1 +35377 w B17 1 +35380 w J3 1 +35391 b C3 1 +35391 w S9 1 +353d6 b D15 1 +35410 b M5 1 +35420 w P5 1 +35441 b R17 1 +35491 b E5 1 +354c0 b D3 1 +354c6 w Q16 1 +35506 w P17 1 +35507 b E13 1 +35550 b J16 1 +35551 w Q18 1 +35556 w H3 1 +35580 b E15 1 +35587 b E4 Q3 2 +355a0 b C3 1 +355d7 b G4 1 +355d7 w D8 1 +355e1 b C2 1 +355f6 w Q2 1 +35600 w F16 1 +35606 w N4 1 +35671 b G4 1 +356c0 w B4 1 +356e6 w O2 1 +35711 b O3 1 +35716 b F2 1 +35716 w B16 1 +35720 w S4 1 +35760 b R4 1 +35771 w S4 1 +357a0 w E18 1 +357a1 b N16 1 +357d6 b D8 1 +35820 w C17 1 +35827 w R12 1 +35870 b P6 1 +35886 b C5 1 +35886 w E3 1 +358a7 w B17 1 +358d1 w H15 1 +358e0 b S17 1 +358e1 w E9 D3 2 +358e7 w E7 1 +358f0 b M17 1 +35931 b E16 F16 D15 E17 4 +35950 b C17 1 +35971 w E5 G4 2 +35987 b E18 1 +359d0 b N14 C17 E17 3 +35a10 w R2 Q2 2 +35a71 b S15 1 +35a77 w F14 1 +35ac6 b G4 1 +35ad0 w F16 1 +35ae7 w R14 1 +35af6 w P2 1 +35b07 w B3 1 +35b20 b O5 1 +35b30 w E8 1 +35b70 b B14 1 +35b77 w E14 D13 C15 3 +35bb0 w P13 1 +35be0 b B6 1 +35be1 b C17 F15 C14 D14 4 +35bf0 b S3 1 +35bf1 w D4 1 +35bf6 b R18 1 +35c20 b A3 1 +35c21 b R17 1 +35c21 w D18 1 +35da6 b M18 1 +35dc7 b R6 1 +35e81 w B13 1 +35e91 b D3 1 +35ea1 w D4 1 +35f36 w S4 1 +35f37 b E4 1 +35f37 w B3 1 +35f46 w C16 1 +35f47 b D14 1 +35f71 w C4 1 +35fa1 w C17 1 +35fa6 w R17 1 +35fd7 b F16 1 +35fe0 b R14 1 +35ff0 b J6 1 +36000 b R16 1 +36021 w G16 1 +36036 w S2 1 +36037 b E12 1 +36040 b C9 1 +36040 w N4 1 +36047 b C16 1 +36047 w R3 1 +36051 w F7 1 +36086 b S3 1 +360c0 w D2 1 +36137 w O17 1 +36150 b N7 1 +36157 w F4 1 +36177 b P14 1 +36180 w D18 1 +36186 w G15 G14 2 +361b1 b C14 1 +361c0 b E16 1 +361c1 w C16 1 +361c7 b B15 1 +361e0 w A5 1 +361f1 b B5 1 +36210 b R7 1 +36217 b R4 1 +36296 w D3 1 +362b6 b E7 1 +362d0 b F5 1 +362e0 w C5 G6 2 +362f1 w E3 1 +36320 w N16 1 +36327 w O14 1 +36360 w F14 C15 2 +36397 b E17 1 +363b1 w T16 1 +363c6 b C18 1 +363d0 b F5 1 +363f0 b D14 C17 2 +36427 b O3 1 +36441 w Q17 1 +36450 w Q19 1 +364a1 b B18 1 +364c0 w R7 S5 2 +364c1 w R14 1 +364c6 w E4 1 +364f6 b C4 D5 D9 3 +365b7 w R2 1 +365d1 w B15 1 +365d7 w B12 1 +365e1 w Q5 N3 2 +365e7 b Q3 1 +36626 w P2 1 +36636 b C17 1 +36637 w R17 1 +36647 w C2 1 +36677 b B13 1 +36700 b B7 E7 2 +36726 b R12 1 +36740 w E4 1 +36746 w B3 1 +36751 b S4 1 +36771 w P18 1 +36776 w E18 1 +367b1 w S3 1 +367c0 b Q5 1 +36800 w D15 1 +36866 b D19 1 +368b0 w R17 1 +368c6 b F17 1 +368d7 b R2 1 +368e6 b C15 1 +368f0 w C9 1 +368f1 b R8 1 +36907 b B13 1 +36937 b C17 1 +36946 w F5 F3 2 +36947 w C16 1 +36986 b C17 1 +36996 w F2 1 +36a00 w P4 1 +36a16 w B17 1 +36a47 b B7 1 +36aa6 w R5 R2 2 +36ab1 w C5 1 +36ac1 b M3 1 +36ac6 w G4 1 +36b01 w C17 1 +36b31 w N16 R17 2 +36b50 w P17 1 +36b56 w D7 1 +36b57 w R5 1 +36b96 b D6 1 +36bb0 b S17 1 +36bb6 w B17 1 +36bd6 b C16 1 +36be1 b D3 1 +36c01 b P2 1 +36c10 w R8 1 +36c37 b D5 1 +36cb1 b S12 1 +36cb1 w B13 1 +36ce0 w E5 1 +36ce6 w G5 1 +36cf6 b S15 1 +36d11 w C12 1 +36d17 w L16 1 +36d36 b E17 1 +36d36 w P13 1 +36d40 b Q18 Q16 2 +36d50 b E3 1 +36d50 w D3 1 +36d66 w E14 C14 2 +36da6 w D3 1 +36dc7 w P4 1 +36de0 w H3 1 +36e11 w S7 1 +36e86 w R6 1 +36ea1 w B3 1 +36ea6 b D5 1 +36eb6 w P4 1 +36ed1 w F17 1 +36ed6 w B16 1 +36f06 w E18 1 +36f26 b O15 1 +36f27 w C13 1 +36f40 w G5 1 +36f47 w N16 1 +36f67 b Q14 1 +36f71 w R7 1 +36fb0 w B18 1 +36fc1 b P3 1 +37021 w F15 1 +37030 b N5 1 +37060 b C6 1 +37061 b R17 1 +37071 b R2 1 +37076 b G3 B5 2 +37091 w C14 1 +37101 b G17 H18 2 +37146 w C6 1 +37171 w E2 1 +37181 b O2 1 +37186 b O16 1 +371b6 b O14 1 +371e7 b M17 P18 Q18 3 +37217 w P2 1 +37231 w D9 C4 2 +37250 w Q15 1 +37267 b C15 1 +37290 w G15 1 +37296 b E17 1 +372b6 w S16 1 +372c1 w H5 1 +372f0 b C2 1 +37326 w C17 1 +37350 w P4 P3 2 +37367 w S6 1 +37376 w E4 D5 2 +373a6 b C2 1 +373e1 w P14 1 +373e6 b R17 1 +373e7 b B11 1 +373f6 w S16 1 +37430 b P5 1 +37436 b S3 1 +37446 w F5 1 +37450 b O3 1 +37466 w P7 O7 2 +37476 b S17 1 +37510 b D15 1 +37517 b P13 1 +37581 w Q16 1 +37591 w D18 1 +375c0 b C2 P15 2 +375e6 w L3 1 +37600 b E6 1 +37610 w H16 1 +37670 w Q6 Q7 2 +37686 b E13 1 +37687 w D6 1 +37696 w F3 1 +376e6 b C15 1 +37716 b J17 1 +37727 b N14 1 +37730 b S16 1 +37730 w M3 1 +377b6 b N17 1 +377c0 b P15 1 +377c7 w P16 1 +377d1 b C18 F18 2 +37820 w B14 1 +37870 b Q6 1 +37891 w B6 1 +378b1 b P2 1 +378c7 w C5 1 +378d1 w A5 1 +378f6 b R6 Q5 2 +37940 b E16 1 +379a1 b C5 1 +379a6 b D17 1 +379e1 b F6 1 +37a00 b F17 1 +37a57 b E17 1 +37af0 b R7 1 +37af6 w E14 1 +37b01 w B4 1 +37b06 b S18 C7 2 +37b27 w N16 1 +37b46 b S6 1 +37b80 b F16 1 +37b80 w D2 1 +37bc1 w C3 1 +37bd0 w N5 1 +37be0 b R13 1 +37bf6 b L17 1 +37c07 b C4 1 +37c16 w D8 1 +37c26 b C5 1 +37c97 w P3 1 +37cb6 w S5 1 +37d06 w P15 1 +37d10 w N14 1 +37d46 b B8 1 +37d91 b C7 1 +37d96 w E2 1 +37db0 b S15 1 +37db7 w P5 1 +37de0 b F15 1 +37e11 b O16 1 +37e11 w C14 1 +37e26 w B14 1 +37e70 b P14 1 +37e71 w S7 1 +37e77 b Q14 P15 Q4 3 +37e80 b Q5 1 +37e80 w N3 1 +37e81 w C17 1 +37ea7 b D3 1 +37ea7 w F14 1 +37eb1 w P3 1 +37eb6 w F4 1 +37ee0 b E15 1 +37f46 w T8 1 +37f56 b Q7 1 +37fb1 w N18 1 +37fe6 w O5 1 +37ff0 b S3 1 +37ff1 w P5 1 +38000 b C4 1 +38010 w Q2 1 +38036 b P5 1 +38036 w Q13 1 +38040 b R6 1 +38080 w S16 1 +380a0 b P15 1 +380c7 w F3 1 +380d0 b S4 1 +380d0 w R5 1 +380d7 w C19 1 +38100 w R16 1 +38110 b O3 1 +38146 w R15 1 +38166 b F2 1 +38176 b S14 S17 2 +381a6 w E13 1 +381b0 w F4 1 +381d6 w R14 1 +38247 w Q5 1 +38270 b R16 1 +382b6 b R14 1 +382d0 w R2 1 +382e0 b L16 1 +38317 b Q8 1 +38340 w D4 1 +38351 w B15 1 +38371 w Q15 1 +383a6 b E18 1 +383b0 w P12 1 +383b1 b F17 1 +38400 w F17 D16 D17 3 +38421 w O17 1 +38476 w R13 1 +38487 w D15 1 +38490 w M3 1 +384a1 w E18 1 +384a6 w Q14 1 +384d0 b J3 1 +384e7 w S16 1 +38510 b P13 1 +38526 b R6 1 +38531 b R17 1 +38546 w S15 1 +38571 w J3 1 +38576 b R4 1 +38577 b O3 1 +38587 b P15 1 +385a6 b R14 1 +385d0 b P13 1 +385d6 b N14 1 +385f6 w B4 1 +38601 w D5 1 +38617 w R14 P18 2 +38677 b O15 1 +386c7 b Q4 1 +386d1 b O6 1 +386d1 w D15 1 +386e0 w R3 Q7 2 +386f0 w R2 1 +38700 w H4 1 +38721 w P17 1 +387a7 w Q7 R8 R9 3 +387d6 w F3 G4 2 +387e1 w Q17 1 +38837 w D13 1 +388b7 b P19 1 +38951 w D9 1 +38960 b C5 1 +38971 w P5 1 +38990 b P6 1 +389b7 w D14 1 +389d7 b M3 1 +389f0 b Q5 1 +389f1 b P16 Q15 2 +38a47 b D3 1 +38a47 w C3 D4 D5 E6 D6 D7 6 +38a56 b G16 1 +38a67 b C13 1 +38a86 b B4 1 +38a91 b F16 1 +38ac1 w P17 1 +38ac7 b D5 1 +38af7 b D9 1 +38b07 b C6 1 +38b40 w N6 1 +38b51 w S4 1 +38b66 w R11 1 +38b67 w O15 1 +38bb7 b R8 1 +38bf6 w E15 1 +38c26 b Q5 1 +38c27 w B3 1 +38c40 b C9 Q3 2 +38c41 w H17 1 +38c56 b D3 E2 2 +38c80 b M3 1 +38c90 w F16 N4 2 +38c91 w S3 1 +38ca0 b N3 1 +38ca6 b O14 1 +38cb1 b R16 1 +38cb6 w S14 1 +38cc1 w E8 1 +38cd6 b D15 1 +38d26 w G4 1 +38d47 b P18 1 +38d90 b O4 1 +38da7 b O3 1 +38dc0 w F4 1 +38dd6 b E17 D16 2 +38df7 w E4 P15 2 +38e00 b M5 1 +38e00 w T6 1 +38e20 b P6 1 +38e31 w S6 1 +38e40 w S13 P13 2 +38e57 w S14 1 +38e60 b P8 1 +38e70 w E8 F8 2 +38e80 b R3 1 +38e86 w M17 1 +38ea1 b H4 1 +38f11 w B13 1 +38f57 w O16 1 +38f67 w R17 1 +38fc6 b D6 1 +38fd7 b H17 1 +38fe6 w P2 S2 2 +38ff7 b P15 1 +39017 b D8 1 +39026 w O13 1 +39027 b Q2 1 +39046 b E16 1 +39046 w E16 1 +39071 b D14 1 +390c0 b C17 1 +390d0 w C16 D17 2 +390d7 b F6 1 +390e1 w E17 1 +390e7 b C17 1 +39110 w R3 1 +39117 b R6 1 +39120 b O17 1 +39130 w R18 1 +39136 b R17 1 +39147 b O5 1 +39147 w D2 1 +391d7 w G16 1 +391e0 b Q2 1 +39230 w E5 1 +39250 w H3 1 +392d0 w R17 1 +392f0 b G6 1 +39317 w E2 1 +39337 b H3 1 +39367 w E3 1 +393b0 w F16 1 +393b7 b Q9 1 +393c6 b G17 J3 2 +393f6 w Q14 1 +39426 b C15 1 +39447 w R8 1 +39456 w P12 1 +39471 w Q8 1 +39517 w F16 1 +39557 b R4 1 +395d1 b D18 1 +39631 w B4 1 +39640 b Q12 G5 2 +39650 w B15 1 +39656 w R5 1 +39681 w S4 1 +39696 b Q13 1 +396b0 w B16 1 +396c1 w E9 1 +39711 w A15 1 +39740 b C11 1 +39767 b O7 1 +39787 w B6 1 +397e0 b R3 1 +397e1 b B15 1 +39830 w D5 1 +39846 b D5 1 +39846 w D5 1 +39850 w O16 1 +39877 b B16 1 +398b0 w E17 1 +398e0 b Q5 1 +39900 b C2 1 +39921 b P18 1 +39926 b O6 1 +39986 b E16 1 +399a6 b G17 1 +399d6 b T16 1 +39aa0 b D16 G7 2 +39aa7 w C2 1 +39ab1 b A2 1 +39b00 w E12 1 +39b20 w C13 1 +39b30 b F16 1 +39b56 b F5 1 +39b77 b D15 1 +39b81 w B13 1 +39ba1 b R18 1 +39be0 w E18 1 +39be1 b Q11 1 +39c10 b S15 1 +39c77 b O14 1 +39c86 w B13 1 +39ca1 w R12 C4 2 +39cb0 w B18 1 +39cf6 w O4 1 +39d10 b D5 1 +39d36 b R7 R5 2 +39d60 w R11 1 +39d61 b D5 E6 2 +39d61 w Q15 1 +39d77 b C3 1 +39d86 w O16 1 +39db0 w L3 1 +39db1 w C3 1 +39dd1 w S13 1 +39df7 w S18 1 +39e07 b H17 1 +39e17 w O8 1 +39ea0 b B15 1 +39ea0 w C2 1 +39ea6 b H2 1 +39ed6 w R18 1 +39f06 b C15 D16 2 +39f36 b Q17 1 +39f40 b Q11 1 +39f47 w C15 B14 B15 3 +39f71 w E18 1 +39fc7 w B5 1 +3a056 b C18 1 +3a077 w O15 1 +3a0a1 b R2 1 +3a0e0 b P6 1 +3a111 w A6 1 +3a140 w Q5 1 +3a147 b C18 1 +3a1b7 b D4 D6 2 +3a1d6 b C6 1 +3a210 w P18 1 +3a227 w M3 1 +3a230 w Q14 1 +3a241 b Q3 1 +3a261 w E5 1 +3a266 w N15 1 +3a2a1 w F5 1 +3a2b0 w E16 1 +3a2d1 b Q18 1 +3a307 w F2 1 +3a310 w D15 1 +3a351 b F7 1 +3a360 w P5 1 +3a381 w E5 1 +3a3b6 b O14 1 +3a401 w C6 H3 2 +3a421 w E1 1 +3a476 w Q5 1 +3a486 b E5 1 +3a4c0 b F6 1 +3a4e1 b R5 1 +3a4e1 w D16 1 +3a506 b D18 1 +3a541 b G5 1 +3a561 b O14 1 +3a590 w N16 1 +3a5a0 w D15 1 +3a5c6 w S16 1 +3a5e0 b D7 1 +3a641 w D17 B16 2 +3a680 w B17 1 +3a681 b D18 1 +3a6a6 w R16 1 +3a6c1 b A3 1 +3a6c6 w S15 1 +3a6f0 b G13 1 +3a700 b P5 1 +3a737 b O17 1 +3a737 w R2 1 +3a767 w P15 1 +3a7a6 b R6 N3 2 +3a7b0 b R5 1 +3a7c7 w F5 1 +3a7e0 w O3 1 +3a7e1 w F3 1 +3a7e6 b F2 1 +3a800 b C12 1 +3a837 w P6 1 +3a870 w C14 1 +3a8a1 w S6 1 +3a8b0 w D13 1 +3a8b1 b Q13 P17 2 +3a8f0 w F16 1 +3a901 w S17 1 +3a906 w P7 1 +3a937 w F18 1 +3a966 w Q5 1 +3a981 b D4 F2 2 +3a9f0 b G4 1 +3aa06 w F16 1 +3aa26 b Q3 1 +3aa27 b S18 1 +3aa87 w D7 C8 C9 3 +3aad1 b B16 1 +3ab30 b F17 1 +3ab36 w C15 1 +3ab41 b B4 1 +3ab76 b E4 1 +3ab86 w Q15 1 +3ab91 w D4 1 +3abb6 w O3 1 +3abe6 b N4 1 +3abf6 b N5 1 +3ac20 b D17 C16 2 +3ac27 b B5 1 +3ac27 w E3 C6 2 +3ac56 w C13 E18 2 +3ac61 w N3 1 +3ac67 w C6 E2 2 +3ac76 b F14 1 +3acb6 w R18 1 +3acd0 b D5 1 +3acd6 b E7 1 +3ace0 w C2 1 +3ad26 w R4 1 +3ad41 w C17 1 +3ad61 w P4 1 +3ada0 w C15 1 +3adb7 w E15 1 +3ae36 w R4 1 +3ae51 w C6 1 +3ae76 b N16 N17 R17 3 +3ae87 b F18 1 +3aec6 b B17 1 +3af17 b D8 1 +3af17 w G6 1 +3af46 w E17 1 +3af66 b Q6 Q7 2 +3af71 w Q18 1 +3af77 b C4 1 +3af77 w N13 1 +3af81 b R7 S8 2 +3afd0 w R2 1 +3afd6 b E16 E3 2 +3b007 b C3 D4 D5 E6 D6 5 +3b007 w D3 1 +3b036 b P14 1 +3b076 w G16 1 +3b0a0 w Q17 1 +3b0a1 b F5 1 +3b0d7 b Q6 1 +3b0e7 w A3 1 +3b101 b E2 1 +3b110 w R6 1 +3b131 w O8 1 +3b136 b O3 1 +3b1a6 b S3 1 +3b1a7 w N3 1 +3b1e6 w Q13 1 +3b207 b R6 1 +3b250 b D13 1 +3b251 b Q6 1 +3b257 b P12 1 +3b286 b Q7 1 +3b290 b Q15 1 +3b2a6 b E3 E2 2 +3b2b0 w D8 1 +3b2c0 w R18 1 +3b2f0 w S15 1 +3b306 w D15 1 +3b316 b Q5 1 +3b321 w C17 1 +3b420 b C11 1 +3b440 b F16 C15 E14 C12 4 +3b456 w E4 D5 2 +3b486 b B16 1 +3b4a1 w C13 1 +3b4b1 w S5 1 +3b4c6 w E16 F17 2 +3b4d1 w E17 1 +3b4f1 b G3 1 +3b507 b C18 1 +3b547 b S15 1 +3b577 w E2 1 +3b5c1 w G3 1 +3b601 b T2 1 +3b611 b F12 1 +3b630 b P6 1 +3b6a0 b E13 1 +3b6e0 b Q6 1 +3b6f0 b F17 E14 2 +3b767 b B6 1 +3b786 b O3 1 +3b7f6 b D13 1 +3b807 b E17 1 +3b831 w Q16 1 +3b836 b Q14 1 +3b846 w S5 1 +3b876 b O18 1 +3b8b7 b F16 1 +3b8e7 w R2 1 +3b911 b D4 1 +3b941 b O5 1 +3b950 w G15 1 +3b961 b Q8 1 +3b9e0 b D3 1 +3b9f1 b G3 1 +3ba10 w Q5 1 +3ba20 w S18 1 +3ba26 w F15 1 +3ba27 w D3 1 +3ba40 w Q3 1 +3ba76 w E17 1 +3baa7 w R17 1 +3bad7 b E12 1 +3bb00 b E12 1 +3bb01 w S15 Q15 N17 3 +3bb30 b P14 1 +3bb70 b J17 1 +3bb70 w E16 1 +3bb77 b D4 1 +3bb90 w S6 1 +3bb97 w D2 1 +3bba0 b J4 1 +3bbb0 w Q14 1 +3bbb6 w G3 1 +3bbd7 w B16 1 +3bbe7 w H4 D8 2 +3bbf7 b R15 S15 Q16 3 +3bc27 b S4 1 +3bc37 b R15 1 +3bc41 w R4 1 +3bc61 w Q5 1 +3bc76 w H17 1 +3bc81 b R15 1 +3bc87 w R17 1 +3bc91 w C9 1 +3bcc0 b O3 Q4 Q3 3 +3bd07 w P14 1 +3bd11 b F16 1 +3bd40 w E5 1 +3bd47 b P4 1 +3bd97 b D5 E5 G4 C3 4 +3bd97 w E4 1 +3bde0 b G3 1 +3bde1 b F4 1 +3bde6 b P5 1 +3be06 w C3 1 +3be30 b O17 1 +3be31 w S6 1 +3be61 w D16 D14 2 +3be71 b R13 1 +3be81 b F16 1 +3be87 b Q14 P18 2 +3be97 b R4 1 +3beb0 b R11 1 +3bed1 b S5 1 +3bef6 w C19 1 +3bf17 b S6 1 +3bf30 b C3 1 +3bfb6 w O4 1 +3c001 b Q14 1 +3c001 w Q13 Q14 2 +3c010 b D17 1 +3c011 b P15 1 +3c036 w O4 1 +3c050 b Q5 1 +3c066 b C15 1 +3c100 w E3 1 +3c187 w C8 1 +3c191 w C5 1 +3c197 w P18 1 +3c1a0 b A17 1 +3c1c0 b R12 1 +3c1c7 w D8 C15 2 +3c200 w Q4 1 +3c247 w D3 1 +3c271 w M3 1 +3c286 b P6 1 +3c297 b Q17 1 +3c2a0 w O14 1 +3c2b6 b B4 1 +3c2b6 w G3 1 +3c2c7 w C4 1 +3c326 w F16 1 +3c341 w F18 1 +3c347 b O16 1 +3c360 w F16 1 +3c3c0 b S16 1 +3c3e6 w B17 1 +3c400 w O15 1 +3c421 b C7 1 +3c427 w G3 1 +3c4d6 w R14 1 +3c4e1 b R8 1 +3c4f7 b R13 1 +3c510 b Q6 P6 2 +3c531 b S17 1 +3c540 w O14 1 +3c546 b C3 1 +3c557 b D3 E4 C3 3 +3c570 b B3 1 +3c5a1 b E16 C17 G17 D14 D12 5 +3c5a1 w D15 C16 2 +3c5d7 w P5 1 +3c5f0 b D6 1 +3c5f7 w R7 1 +3c607 b N5 1 +3c616 w O16 1 +3c657 b R14 1 +3c681 b T4 1 +3c6d1 b C4 1 +3c700 b O5 1 +3c756 w O5 1 +3c761 b S18 1 +3c7b1 w O18 1 +3c7e1 w R18 1 +3c800 b G14 1 +3c821 b E2 1 +3c876 w C17 1 +3c8a7 w B3 1 +3c8b6 b G18 1 +3c906 b D13 1 +3c936 w O15 1 +3c9d0 b B2 1 +3c9d7 b B4 1 +3c9e6 b Q3 1 +3c9e6 w R4 1 +3ca00 w J14 1 +3ca41 w D2 1 +3ca47 w P5 1 +3caa0 b D2 D4 2 +3cae1 b P5 1 +3cb17 b Q16 1 +3cb36 b O15 1 +3cb40 b S3 1 +3cbb1 b F16 1 +3cc10 b E18 E16 2 +3cc11 w Q18 1 +3cc27 b Q6 1 +3cc66 b M17 1 +3cc67 w R3 1 +3cc91 b E3 1 +3cca6 b P13 1 +3ccd6 w D13 1 +3ccf1 w B4 1 +3cd16 b L17 1 +3cd37 w C15 1 +3cda7 b N16 1 +3cdf6 w P17 Q16 2 +3ce06 b E18 1 +3ce66 b D3 1 +3ce80 w R7 1 +3ce87 b S9 1 +3ce91 w G15 1 +3ceb0 b C5 1 +3ceb6 b P5 1 +3cee6 w C4 1 +3cf26 w C3 1 +3cf37 w M16 1 +3cf41 w D8 1 +3cf70 w P15 1 +3cf81 b S16 1 +3cf87 b C15 C18 2 +3cfa0 w C3 1 +3cfc1 b D4 1 +3cff7 b C2 1 +3d061 b D2 Q17 2 +3d070 w R12 1 +3d086 b C15 1 +3d086 w E4 1 +3d087 w S15 1 +3d0f1 b S3 1 +3d107 w Q14 1 +3d121 b D16 1 +3d121 w C2 1 +3d147 b G16 1 +3d180 b E17 1 +3d191 w D15 F15 2 +3d1a0 b M6 1 +3d1c7 b S16 1 +3d1e7 w F5 F4 2 +3d247 b F18 1 +3d257 w S11 1 +3d266 b F16 1 +3d286 b P16 1 +3d287 b S15 1 +3d2b0 w P17 1 +3d2b7 b Q15 1 +3d2e6 w S6 1 +3d307 w C16 1 +3d361 b Q14 1 +3d381 b P18 1 +3d3c0 w M4 1 +3d3c6 b E13 1 +3d427 w D5 1 +3d436 w P17 1 +3d451 w A14 1 +3d460 b P16 1 +3d461 b O15 1 +3d496 w R19 1 +3d4b7 b O17 1 +3d516 b O18 1 +3d530 b B6 1 +3d540 w D19 1 +3d551 w D5 1 +3d5f0 b S5 1 +3d621 b R2 1 +3d641 b O5 1 +3d646 b L3 O4 2 +3d660 w R12 1 +3d697 w R5 1 +3d6f1 b H3 1 +3d6f6 b B15 1 +3d770 w P16 1 +3d7b7 w Q8 1 +3d7c7 w P2 1 +3d7f0 b R3 1 +3d7f0 w S4 1 +3d806 b N15 1 +3d816 b E17 C17 2 +3d877 w E17 1 +3d896 b N4 1 +3d8b0 b R13 P15 2 +3d8c7 b B15 1 +3d907 b S16 1 +3d907 w R17 1 +3d910 b P6 1 +3d927 b F2 1 +3d927 w N17 1 +3d946 b C4 1 +3d987 w F12 1 +3d9a7 b O17 Q15 2 +3d9b0 w M16 1 +3d9f0 b Q7 1 +3d9f6 b M4 1 +3d9f6 w R9 D3 2 +3da31 b B7 1 +3da37 b E12 1 +3da46 w R16 1 +3da47 w B18 1 +3da67 w D17 1 +3da80 b L3 1 +3da80 w D17 D16 G17 E15 C17 5 +3dac6 w G5 1 +3daf1 b P3 1 +3db00 w P18 1 +3db10 b O4 O2 2 +3db21 b F1 1 +3db41 b M2 1 +3db51 b S15 1 +3db56 b E18 1 +3dc01 w C13 1 +3dc16 b R11 D7 2 +3dc20 b Q9 1 +3dc70 b O15 1 +3dca1 w C12 1 +3dcf1 b Q2 1 +3dd16 w C14 1 +3dd56 b D3 1 +3dd96 w B5 1 +3ddc6 w S19 1 +3ddd7 w S2 1 +3de06 w S4 1 +3de17 w C5 1 +3de60 w P17 1 +3de67 w Q16 1 +3de76 b N17 1 +3de96 b O18 1 +3dec1 b F13 1 +3ded7 w E5 1 +3df07 b J4 1 +3df27 b B4 1 +3df87 w Q3 1 +3dfa0 b Q4 1 +3dfc7 w E14 1 +3dfd0 w P4 1 +3dfd1 b S5 1 +3e041 b R3 1 +3e070 w E2 1 +3e096 b P4 1 +3e0a1 b G7 1 +3e0b0 b R6 1 +3e0b7 b D7 1 +3e110 b B13 1 +3e136 b R1 1 +3e146 w R6 1 +3e147 w D6 1 +3e150 w G14 D17 2 +3e171 b C4 1 +3e177 w D6 1 +3e1e0 b C4 1 +3e1f7 b R18 1 +3e227 b J3 1 +3e256 w C13 1 +3e271 b B5 F2 2 +3e296 b E5 1 +3e2a6 b D8 1 +3e2f1 w N5 1 +3e330 b M17 1 +3e356 w P5 1 +3e3c6 b E2 1 +3e3f6 w D2 1 +3e401 w C3 1 +3e410 w R8 1 +3e450 b C15 1 +3e460 w D7 1 +3e471 w E15 1 +3e487 w P18 1 +3e530 b Q6 S6 2 +3e591 b N5 1 +3e5b1 w D12 1 +3e5c1 b C16 1 +3e600 b H5 1 +3e601 b M16 1 +3e646 w Q6 1 +3e6b1 b R18 1 +3e6b6 w D2 1 +3e6e0 b B17 1 +3e6f0 b Q15 1 +3e6f1 w G16 1 +3e700 b S7 1 +3e730 b O14 D13 2 +3e731 w S2 1 +3e780 w Q3 R4 2 +3e7c7 w R17 1 +3e7d0 w P16 1 +3e7e7 w F6 1 +3e7f7 b O13 1 +3e831 b P4 1 +3e840 b S5 1 +3e847 b C18 1 +3e887 w S3 1 +3e890 w T16 1 +3e891 b C14 1 +3e8b0 w L17 1 +3e8b6 w F5 G4 E6 3 +3e8f0 w P7 1 +3e8f1 w R14 1 +3e907 w D6 1 +3e916 w C14 1 +3e917 b B16 1 +3e987 b D18 1 +3e9b6 b Q7 1 +3e9e1 w S2 1 +3ea27 w C8 1 +3ea96 w D6 1 +3eab6 w R4 1 +3eac6 b P15 1 +3eac6 w E7 N6 2 +3eb11 b P2 P18 2 +3eb16 b E15 E16 2 +3eb91 b P16 1 +3eb91 w Q7 1 +3eba1 b D16 1 +3eba7 w G4 1 +3ebb7 w C3 1 +3ebd0 b R9 1 +3ebe1 b H16 1 +3ebe1 w Q15 1 +3ebe6 w Q14 1 +3ec06 w N17 1 +3ec21 w C15 1 +3ec31 b R16 1 +3ec36 w D15 1 +3ec40 b P9 1 +3ec57 b Q5 1 +3ec61 b B16 1 +3ec67 w C2 1 +3ec80 b D13 1 +3ecf7 w P3 1 +3ed41 b D6 1 +3ed41 w D7 D6 2 +3ed51 b Q6 1 +3ed70 w R7 1 +3ed77 b F18 1 +3edb1 b C6 1 +3ee01 w C16 1 +3ee07 w D17 1 +3ee20 w F15 1 +3ee36 w G4 G3 C3 3 +3ee80 b E14 1 +3ee80 w M4 1 +3ee91 b E6 1 +3eeb6 b C2 1 +3eee6 b G4 1 +3eef0 w B7 E7 2 +3ef00 b F14 1 +3ef16 w E5 1 +3ef40 b D4 1 +3ef46 b P4 1 +3ef67 b B17 1 +3ef67 w P5 1 +3ef86 w P16 1 +3ef90 b O17 1 +3efe7 b B11 1 +3f007 b F2 R13 2 +3f027 b D2 1 +3f067 b D13 1 +3f077 b B15 1 +3f091 w E11 1 +3f096 w C6 1 +3f0a6 b O3 Q4 2 +3f0a6 w R17 1 +3f0b1 w S15 1 +3f0f1 w C16 1 +3f131 w F18 1 +3f161 b R12 1 +3f180 b B5 1 +3f197 w H18 1 +3f1e0 w F18 1 +3f206 b O16 1 +3f226 w D16 1 +3f291 w R14 1 +3f2a7 w R17 P18 N16 3 +3f2b1 b E7 1 +3f2c0 b P15 1 +3f2c7 w E16 1 +3f2d6 w E3 1 +3f2f6 b Q5 1 +3f306 b R5 1 +3f331 w C17 1 +3f351 b O5 1 +3f356 w P13 1 +3f3a6 b E4 D5 2 +3f3d6 w H15 1 +3f411 w R3 1 +3f466 b R18 1 +3f4c6 b S5 1 +3f501 w O14 1 +3f517 w B17 1 +3f530 b F18 1 +3f541 b B15 1 +3f546 w Q14 1 +3f567 b G18 1 +3f571 w E6 1 +3f586 w R5 1 +3f5f1 w C3 1 +3f630 w Q5 1 +3f640 b N16 1 +3f641 b Q9 1 +3f661 w D7 F8 2 +3f6a0 b Q5 1 +3f6d6 w E4 1 +3f720 b C12 1 +3f737 b O5 1 +3f746 b H4 1 +3f760 b F3 1 +3f770 b E17 1 +3f791 w Q6 R7 2 +3f7c6 b R16 1 +3f816 w C15 1 +3f820 w E4 1 +3f821 b C3 G4 2 +3f866 w D15 E16 2 +3f871 w B17 E17 F16 G17 G18 5 +3f8b0 w Q16 1 +3f8c0 b P15 1 +3f8f6 b C15 1 +3f916 b Q14 1 +3f917 b F13 1 +3f971 w P12 1 +3fa01 w O17 1 +3fa06 b B5 1 +3fa11 b Q8 M4 2 +3fa17 b Q2 1 +3fa21 b F2 1 +3fa57 w B17 J17 2 +3fa86 w O2 1 +3faa0 b D16 1 +3fac0 b Q3 1 +3fb07 w R6 1 +3fb70 b Q11 1 +3fb70 w N16 1 +3fb80 b G3 C6 2 +3fbe1 w P2 1 +3fbf1 w D8 1 +3fbf7 b M4 1 +3fc01 b B17 1 +3fc06 w Q15 P16 2 +3fcb6 w P17 R17 2 +3fcd6 b D2 1 +3fce0 w B5 1 +3fd00 b O3 1 +3fd07 w S16 1 +3fd27 w R4 1 +3fd30 w R4 1 +3fd57 w O18 1 +3fd66 w B4 1 +3fd86 b C6 1 +3fdc6 b C2 1 +3fe01 w N17 1 +3fe40 b S18 1 +3fe46 b O5 R6 2 +3fe70 b N4 1 +3fe70 w F3 1 +3fe71 w Q2 1 +3fe91 w O13 1 +3feb1 w R5 1 +3ff01 b H14 1 +3ff01 w C5 1 +3ff06 w H3 1 +3ff07 b N4 1 +3ff11 b E13 1 +3ff11 w E2 1 +3ff20 b N15 1 +3ff21 b C16 E16 2 +3ff30 b C17 C12 2 +3ff41 w R3 1 +3ff50 b R2 1 +3ff60 b C4 1 +3ff70 w M7 1 +3ff77 w M16 Q12 2 +3ffb0 b N15 1 +3ffb1 b C15 B14 B15 3 +3ffd0 w L3 1 +3ffd7 w B14 1 +40007 w L16 1 +40010 b E16 1 +40016 w P4 1 +40031 b E2 1 +40056 b P18 1 +40081 b P17 1 +400c0 w D3 C4 2 +400d7 b B7 E8 2 +400f7 w P4 1 +40130 w O14 1 +40137 b A3 1 +40176 w C15 1 +40197 w O16 1 +401a7 w P5 P6 R3 R7 4 +401c1 b O15 1 +401c6 b N16 1 +401e7 w M15 1 +40206 w F17 1 +40211 w C5 D4 2 +40237 w Q5 1 +40261 b R15 1 +40261 w F5 1 +40266 w T2 1 +40271 b S16 1 +40290 w C16 1 +402e6 b E2 1 +402f7 b R14 O17 Q14 O16 Q13 N16 R13 N17 R11 L17 10 +40326 w R2 1 +40341 b E7 D6 2 +40357 b O4 P5 2 +40357 w O3 1 +403c6 b P5 P4 2 +403c7 b R16 1 +403c7 w R17 Q16 P16 O15 O16 N16 E2 7 +403d6 b G14 1 +403f7 b D6 C3 2 +40416 b Q6 1 +40427 b N17 1 +40450 b D14 1 +40451 w L3 1 +40471 w Q8 1 +40476 b E8 1 +404b7 w Q9 1 +40506 b R15 1 +40516 b P7 1 +40567 b H17 1 +40591 b E17 1 +405b1 b R7 1 +405d7 w C3 1 +405e1 w F6 1 +405f6 w S14 1 +40626 w P13 1 +406a1 b C5 1 +406c6 b R16 1 +406f6 w C16 D17 2 +406f7 w Q3 Q5 2 +40761 b F17 1 +40766 b D3 1 +40770 w E7 1 +40780 w H5 1 +407e0 w C14 1 +407f1 b R4 1 +40850 b N15 1 +40850 w E18 1 +40877 b Q3 D7 2 +40880 w F16 1 +40891 w L2 1 +408b0 b N17 1 +40910 b O16 1 +40910 w J17 1 +40917 b B2 1 +40926 b E2 1 +40930 w R15 1 +40931 w E15 1 +40940 w H17 1 +40956 w E6 1 +40971 w L16 1 +40976 b C16 1 +40980 w C5 1 +40987 w D2 1 +409a0 w P4 1 +409c0 b E3 1 +409e0 b P5 1 +40a21 b F11 1 +40a26 b S13 1 +40a27 w B16 1 +40a40 w S4 1 +40a46 w G18 1 +40a67 w C17 1 +40ac0 b J17 1 +40ac7 w B3 1 +40b70 w D17 1 +40b81 b N17 1 +40bc7 b E5 1 +40c07 w F6 1 +40c20 b S15 1 +40c37 w E14 1 +40c40 w R17 1 +40c60 w Q18 1 +40d06 w P15 1 +40e17 b Q18 1 +40e21 b Q14 1 +40e30 b N6 1 +40e56 b O3 1 +40e57 w R15 S14 S15 3 +40eb1 b H5 1 +40ed6 w P15 1 +40ef1 w C2 1 +40ef7 w S18 1 +40f26 b D14 1 +40f27 w S15 1 +40f40 b Q17 R16 Q15 3 +40f41 w D14 1 +40f47 b G14 1 +40f50 w D5 1 +40f56 b O4 1 +40f87 w D18 1 +40fa0 w N16 1 +40fe6 b R16 1 +41007 w G16 1 +41067 b C16 1 +41070 b C3 1 +410a7 b R7 1 +410b7 b D3 1 +410c7 w B17 1 +410e1 w O2 1 +410f6 b P8 1 +41127 w P2 1 +41170 w O1 1 +411b6 w B5 1 +411f0 w S2 1 +411f6 w E16 1 +41220 b N6 1 +41231 b G17 Q17 2 +41236 b E6 1 +41237 w F5 S17 2 +41251 w M3 1 +41281 w Q5 1 +412a6 w C2 1 +412a7 b R19 1 +412c6 w Q3 1 +41301 b C7 1 +41326 b S15 1 +41327 b Q3 1 +41340 w O17 1 +41346 w G15 1 +41350 b P8 1 +41350 w C4 1 +41360 w B16 1 +41381 b O18 1 +413b7 b C17 1 +413c7 w D4 1 +413e7 b A16 1 +41400 w C12 1 +41450 b R17 1 +41490 b S5 1 +41496 b M15 1 +414c1 w M16 1 +414c6 w D17 1 +414e0 b R3 1 +414f7 b D13 B14 2 +41586 w R14 1 +415a7 b S5 O17 2 +415c0 w F4 1 +415c7 b R3 1 +415d1 w M3 1 +415f7 b Q14 1 +415f7 w S14 1 +41621 w D18 1 +41630 w Q11 1 +41660 w B17 1 +416a0 w H5 1 +416a6 w D12 1 +416f6 w M2 1 +41726 w R4 1 +41727 b R6 1 +41760 w O4 1 +41767 b N4 1 +41787 b P17 1 +41796 w O3 1 +417a1 w R3 1 +417b7 b M17 1 +417e1 w S16 R18 2 +417e7 b S14 1 +41816 w R15 1 +41827 b C17 1 +41860 b P16 N17 2 +41871 b C18 1 +41876 b H17 1 +418a0 w P4 1 +418a6 b P16 1 +418b1 w S13 1 +41901 w O5 1 +41910 w C12 1 +41930 w C2 1 +41937 w C14 1 +41947 b G18 1 +41956 b R4 1 +41996 b O15 1 +419d7 w S17 1 +41a30 b Q13 1 +41a37 w H2 1 +41a61 b B16 1 +41a70 w R3 1 +41a87 b B6 1 +41a91 w C5 1 +41aa6 w D15 1 +41ad7 w C13 G17 2 +41ae6 b C14 D6 2 +41b16 b C4 1 +41b76 w Q13 1 +41bb0 b R14 1 +41c10 w C17 1 +41c81 b G14 1 +41cc0 w O9 1 +41cc1 w E17 N18 2 +41cd7 w B4 1 +41cf6 b C4 1 +41d96 b M3 1 +41da1 b O2 1 +41da1 w Q11 1 +41db0 w D15 1 +41de7 b C5 C6 D5 E4 4 +41de7 w D4 1 +41e51 w S15 1 +41e80 b B15 1 +41e97 b F17 1 +41ef6 b D7 1 +41f31 w G17 1 +41f76 b O17 R13 2 +41f81 w C3 1 +41f91 w B4 C2 2 +41fa1 w E6 1 +41fd7 w C15 1 +42006 w D17 1 +42011 b N5 1 +42031 w B18 1 +42081 w G6 1 +420a1 b O6 1 +420b1 w F2 1 +420d7 w P18 1 +42101 b Q16 1 +42151 b D4 1 +42157 b P16 1 +42180 w D5 1 +42186 b S4 1 +421a6 w E5 B3 2 +421b1 b S13 1 +421c0 b O4 1 +421c0 w S18 1 +421c1 w F6 1 +421e6 b F18 1 +421e6 w E6 1 +42227 w O14 1 +42270 b P6 1 +42287 w C17 1 +422b7 w N4 1 +422d7 b Q6 1 +422f1 b F15 1 +42306 b B5 1 +42321 w C7 1 +42357 w E7 1 +423e0 b P13 1 +423f1 w B16 1 +423f6 b S2 1 +42400 w J17 1 +42446 w H16 1 +42447 w F5 1 +42457 w B17 1 +424a0 w E5 1 +424a7 b R15 1 +424d0 b B4 1 +424d7 b E4 1 +42506 w D17 1 +42521 w Q2 1 +42551 b D9 1 +42580 b L3 N17 2 +42580 w R5 1 +425c1 b C5 1 +425c1 w S7 1 +425f1 w C15 1 +425f7 w Q4 1 +42601 b R3 Q7 2 +42626 w F13 1 +42667 b P8 1 +42681 w R18 1 +42690 w F16 1 +42691 w M3 1 +426a6 b R14 1 +426b0 b E16 1 +426f0 w H4 1 +42716 w O13 1 +42746 b C8 1 +42747 b C14 1 +42757 b B13 1 +42760 w O5 1 +42787 w O13 1 +42797 b S6 1 +427b0 b F15 1 +427d1 w N17 1 +427e0 b Q15 1 +42806 b R12 1 +42827 w Q17 1 +42897 b C4 1 +428c6 b G3 C9 2 +428d6 b Q17 P16 L16 3 +42900 w S6 1 +42906 b R15 1 +42921 b E7 1 +42936 b Q15 1 +42940 w S4 1 +429e6 w F5 1 +42a01 w E17 1 +42a06 b H13 1 +42a30 b G16 1 +42a31 b Q4 1 +42a50 b O8 1 +42a80 b C5 S16 2 +42a97 w D11 1 +42aa7 w B3 1 +42ae1 b D2 B4 2 +42af6 w R4 1 +42b51 w L5 1 +42b57 b G14 1 +42b70 b O5 1 +42ba0 w D9 1 +42ba7 w B14 1 +42bb7 w A6 1 +42be0 w S17 1 +42c00 b O17 1 +42c46 w R16 1 +42c77 b G8 1 +42ce1 b Q17 1 +42d17 w E3 1 +42d20 b H4 1 +42db0 w P3 1 +42dc6 w B17 1 +42dd6 b Q6 1 +42de6 w Q5 1 +42df0 w C17 1 +42e20 w O13 1 +42e46 w R14 1 +42e50 b C17 1 +42e96 b Q3 1 +42ea6 w S4 1 +42ec6 b R6 Q6 2 +42ed1 b P17 1 +42ed6 w C11 1 +42ed7 b Q12 1 +42ef6 b M3 1 +42f21 w E2 1 +42f46 b P3 1 +42f67 b B16 1 +42fb0 b E16 1 +42fc7 b D4 1 +42fe7 b B15 1 +42ff0 b C3 1 +42ff1 b D6 1 +43036 b F6 1 +43040 w P16 1 +43057 b D12 1 +43076 w O4 1 +43077 b R16 1 +43086 b C3 1 +430c0 b E16 1 +430f6 w D7 1 +43141 b D12 H16 2 +43147 b C4 1 +43156 b S14 1 +43176 w C3 1 +43187 b O4 N3 2 +431a0 w B3 1 +431a7 b S14 1 +431b0 b R13 1 +431b1 b D9 1 +43237 b D4 1 +43277 b C7 1 +432a1 w G4 1 +432a6 b B8 1 +432a7 w Q14 1 +432b1 w E7 1 +43321 w R9 1 +43326 w O7 1 +43330 w Q15 1 +43357 w D16 1 +43361 w F8 1 +433b7 w S8 1 +433c1 w H15 1 +433d7 b J17 1 +43420 w N16 1 +43466 w R7 1 +43467 b S6 1 +43470 w G5 D18 D16 3 +434b0 w C17 1 +434c6 w Q19 1 +434d1 b O2 1 +434f0 b R14 1 +434f6 b Q15 1 +43511 w Q7 1 +43536 b C16 1 +43560 b R5 1 +43596 w P17 1 +435a1 w N2 M5 2 +435d6 w Q2 1 +435d7 w H4 1 +435f1 b E16 F15 2 +435f7 w Q5 1 +43616 w D6 1 +43687 b R2 1 +43691 b E13 1 +436a7 w N3 1 +43706 b S3 1 +43727 b S14 1 +43741 b D4 1 +437b0 w Q14 1 +437c7 w G16 1 +437e1 w B3 1 +437f0 b Q18 1 +437f1 w E3 1 +437f7 b R4 1 +43800 b C8 1 +43800 w L3 1 +43830 w L14 1 +43846 w D2 1 +43867 b D6 1 +43896 w R16 1 +438a6 w S5 1 +438d7 w B16 1 +438e1 w F3 1 +438f1 b P14 Q14 2 +43901 w R5 1 +43917 w C18 1 +43947 w S17 1 +43951 b C17 1 +43966 b Q14 1 +43980 b R12 1 +439a0 w C16 1 +439b1 w N5 1 +439b7 w R5 1 +439d7 b R9 O5 N5 3 +43a07 b B7 1 +43a46 w S8 1 +43a81 b C3 1 +43a86 b P17 1 +43ad7 w S16 1 +43b07 b Q16 E3 2 +43b10 b Q5 1 +43b26 w H7 1 +43b47 w S3 1 +43b50 b J4 1 +43b70 b F15 1 +43ba6 b H3 1 +43ba6 w Q18 1 +43ba7 w N14 1 +43bd0 w E7 1 +43bd6 b A17 1 +43bf6 b D14 D13 2 +43c11 w Q2 1 +43c26 b D15 Q15 2 +43c61 w R17 1 +43c91 w N14 1 +43ce6 w B14 1 +43ce7 w D8 1 +43d06 w C14 1 +43d57 b O3 1 +43d61 b E17 1 +43d70 w L17 1 +43d80 w G16 1 +43d87 b P5 1 +43d96 b G12 1 +43db0 b P2 1 +43dd7 w D4 1 +43de6 w F6 1 +43df6 w L17 1 +43e31 w M15 1 +43e60 w H17 1 +43eb7 w M6 1 +43ef0 b C14 1 +43ef6 w R15 1 +43f36 b B14 1 +43f37 b Q13 1 +43f86 b Q15 1 +43f87 b C13 1 +43fb6 b F17 1 +43fb7 w F14 1 +43fd0 b S5 1 +43fd1 w P19 1 +44010 b P8 1 +44036 b R3 1 +44060 w Q17 1 +44081 w P5 1 +44147 b E15 1 +44156 w C15 1 +44177 b R3 1 +44196 b E3 1 +44197 b R17 1 +441e0 w N17 1 +44206 b P15 1 +44210 b G5 1 +44236 b E5 1 +44240 w N16 1 +44256 b H17 1 +44281 w C17 R16 2 +44291 w Q18 1 +442b6 b C7 1 +442d1 w C7 1 +442e0 b D12 1 +44307 b P16 1 +44326 w P4 1 +44330 b S6 1 +44336 b O15 1 +44350 w S2 1 +443b0 b D13 E16 2 +443f7 w D15 1 +44451 b R4 1 +44471 b B13 1 +44477 b P7 1 +444c1 b C5 1 +44551 b E4 1 +44551 w D5 E5 G4 C3 4 +44560 w Q5 1 +445b0 b Q6 Q7 2 +445c0 b G15 1 +445d0 b Q5 1 +445d0 w M3 1 +445f7 w R18 1 +44610 w B6 1 +44676 w F3 1 +44686 w N16 1 +44747 w Q7 1 +44780 w R17 R15 2 +44781 w R4 Q2 Q3 3 +44791 w S16 1 +447c6 b H3 1 +44806 b F16 1 +44806 w C14 1 +44826 w G5 1 +44831 w B14 1 +44837 b Q13 1 +44841 b R7 1 +44846 w O3 1 +44860 w P12 1 +448f1 b P13 1 +44926 w C11 1 +44937 w E15 1 +44940 b O15 1 +44977 w D18 1 +44981 w N15 1 +44996 w G17 1 +449b0 w E2 1 +449b7 b B16 1 +449f6 b R15 1 +44a06 b P3 1 +44a21 b R2 1 +44a30 b Q15 1 +44a97 w S5 1 +44ab1 w F2 1 +44ab6 b C5 1 +44ac1 b B6 1 +44b17 w N4 1 +44b21 w N17 1 +44b26 b F3 1 +44b31 b P3 1 +44b40 b D2 1 +44b66 w D7 1 +44b97 b C18 1 +44bc6 b Q6 1 +44bf0 w C11 1 +44bf6 b G4 1 +44bf6 w J3 1 +44c20 b Q6 1 +44c46 w Q14 Q17 2 +44cb1 b S3 1 +44cd7 b Q18 1 +44ce7 w C17 1 +44d40 b Q8 1 +44d40 w D7 1 +44d61 b D1 1 +44db1 w G16 1 +44db6 w P3 1 +44dc6 w E16 1 +44de7 b J16 1 +44df7 b Q14 1 +44e10 w Q17 1 +44e20 w O17 1 +44e37 b R11 R13 2 +44e50 w R15 1 +44e67 w G5 1 +44e86 w F4 1 +44ec6 w R16 1 +44f11 b S4 1 +44f71 b Q16 1 +44f96 b B3 1 +44ff0 b B15 1 +45026 b S17 1 +45027 b H16 1 +45031 b C4 1 +45047 b P13 1 +45061 b E17 1 +45087 b R17 1 +450a7 b R15 1 +450c7 w O16 1 +450d6 w Q7 1 +450e1 w C17 1 +45100 w J5 1 +45126 w C5 B5 2 +45127 b R13 1 +45147 b R5 1 +45156 b R13 R16 2 +45197 w O6 1 +451e0 w F2 1 +451e1 b B5 1 +451f1 w E2 1 +45217 b B17 1 +45231 w F3 1 +45291 b F5 C3 2 +45296 b Q5 1 +452c6 b H17 1 +452f7 w N4 1 +45307 b C6 1 +45390 w G8 1 +453c1 b D16 1 +453c6 w F5 1 +45421 w C9 1 +45461 w Q4 1 +454b6 w J2 1 +45511 w S12 1 +45531 b D16 1 +45537 w C6 O14 2 +45586 b S19 1 +455a0 b O6 1 +455d0 w P4 1 +45627 w F4 1 +45650 w R13 P17 2 +45681 b R11 R13 2 +456d1 b R4 1 +456e0 b S18 1 +456e1 w O16 1 +45706 w E3 1 +45707 w H4 1 +45766 b Q13 1 +457a6 w S16 1 +457c6 w L3 1 +457d0 b N6 1 +45846 w H18 1 +45871 b M3 1 +45887 b R4 1 +45891 w F2 1 +458a1 b D17 1 +458b7 w H18 1 +458e7 b O2 1 +45901 w Q4 1 +45911 b C8 1 +45911 w C16 1 +45921 w C3 D3 C6 3 +45931 b R17 C6 2 +45940 b C7 1 +45956 b G17 1 +45967 w O16 S17 2 +45986 w C3 1 +45996 w C14 1 +459f1 w E16 1 +45a01 w O14 1 +45a16 b O16 1 +45a16 w O18 1 +45a20 w B16 D14 2 +45a21 b C17 1 +45a30 b R14 1 +45a40 w G17 1 +45a66 b B13 1 +45a97 b B3 1 +45ac7 b H18 1 +45b20 b M15 N15 P17 P15 4 +45b31 w Q6 1 +45b60 w P5 1 +45b71 w N4 1 +45b77 w E2 1 +45b80 w D5 1 +45b91 w D1 1 +45bb1 w Q15 C14 2 +45bc0 b O16 1 +45be7 b Q7 1 +45c11 w L4 S6 2 +45c86 b F15 1 +45c87 w S18 1 +45ce7 b S14 1 +45d07 w G2 1 +45d26 w E18 1 +45d57 w D18 1 +45d76 b B16 1 +45d91 w R18 R15 Q14 R13 S13 5 +45d97 w C13 1 +45de1 w Q3 S4 2 +45de6 w Q8 1 +45de7 w S4 E5 2 +45df1 w H3 1 +45e00 b F16 1 +45e27 b Q4 1 +45e31 w P15 1 +45e76 b Q19 1 +45e81 b O13 1 +45e81 w M16 1 +45e96 b P5 1 +45eb0 b R17 1 +45ec6 w Q4 1 +45ed0 w S3 C15 2 +45ed6 b F17 1 +45f01 b C4 1 +45f16 w R5 P6 2 +45f20 w E5 1 +45fb0 b F3 1 +45ff0 w Q14 1 +45ff7 b S3 1 +46026 b O16 1 +46026 w S16 1 +46036 b Q5 1 +46046 w P5 1 +46096 w E18 1 +46097 b B15 1 +460b6 b H16 1 +460b7 w N14 1 +460c1 w R3 1 +460d1 b B17 1 +46121 b C17 F16 F17 D12 E14 5 +46196 w C3 1 +461b0 b G17 1 +46237 w B5 1 +46256 w E3 1 +46267 b O13 1 +46291 w B5 Q16 2 +462e7 w S5 1 +46301 b D12 1 +46306 w R2 1 +46320 w P17 O14 2 +46326 b C14 D16 2 +46337 w F6 1 +46371 b N16 1 +46377 w R15 1 +46387 w G17 1 +46391 b R11 O15 2 +463a1 b P16 1 +463e7 w B3 1 +463f7 b E6 1 +46436 w R12 1 +46437 w P2 1 +46446 w B16 1 +46487 w D4 1 +46490 b P16 1 +464f6 b Q16 1 +46506 b G16 1 +46550 b D3 1 +46556 w Q17 1 +46571 w B15 1 +465a6 b Q7 1 +465b7 b Q8 1 +465b7 w N6 1 +465c0 w S2 1 +46631 w F8 1 +46656 w Q6 Q3 2 +46686 b R3 1 +46690 w P7 1 +466d1 b J4 1 +466d1 w D3 1 +466e7 b B5 1 +466f0 w D14 1 +466f6 b S3 1 +46741 b F5 1 +46760 w C16 1 +46766 w P17 1 +46771 w R3 1 +46797 b C14 D15 2 +467a1 w D18 1 +467a6 b D4 1 +467b7 w F4 1 +46806 w P18 1 +46817 b O2 1 +46836 w S16 1 +46857 w S16 1 +46876 b C3 1 +46880 w Q15 1 +468a6 w O2 1 +468c0 w C7 1 +468c7 w S16 1 +468e6 b Q14 1 +468f0 w Q3 1 +46907 b F17 1 +46936 b E18 1 +469b1 b E7 1 +469c1 w G16 1 +469d1 b F2 F3 2 +469e6 b C8 1 +469f7 w D16 1 +46a01 b B17 1 +46a06 b C13 1 +46a11 w Q6 1 +46a16 b E15 1 +46a50 b R14 1 +46a71 b O4 1 +46a87 w Q14 1 +46aa0 b S18 1 +46ab0 b D6 1 +46ac1 b C5 1 +46ac7 b R14 1 +46ae6 w N15 F3 D4 3 +46af0 w N17 1 +46b01 w S3 1 +46b06 b C14 1 +46b41 w F18 1 +46b77 w B18 1 +46b96 w B13 1 +46bd0 w Q12 1 +46bf6 b O17 1 +46c21 b S4 1 +46c40 b Q15 1 +46c46 b C17 1 +46cc1 b Q15 1 +46cd0 w F4 C3 2 +46cd7 w D2 1 +46d06 w O17 1 +46d20 b B18 1 +46d57 w P16 1 +46d70 b E18 1 +46da7 w Q8 1 +46df0 w D1 O17 2 +46df1 b B1 1 +46e01 b O6 1 +46e11 w B3 1 +46e16 w D7 1 +46e26 w S4 1 +46e37 w R7 S8 2 +46f41 w D16 1 +46f51 b C5 F3 2 +46f51 w E2 1 +46f81 b C4 1 +46f96 w N5 1 +46fa0 w C4 1 +46fa1 b S5 1 +46fc1 b R18 1 +46fc6 b R19 1 +46fd6 b F7 1 +46ff6 w R14 Q14 2 +47006 w S4 1 +47026 b P4 1 +47036 w Q3 1 +47056 b P3 1 +47067 b G15 1 +470a0 b S14 1 +470c0 w E6 1 +47111 w R16 1 +47131 w P4 1 +47136 b D15 1 +47140 b C17 1 +47146 b D5 1 +47147 w D8 1 +47157 b Q2 1 +47161 w E6 1 +47167 b P14 1 +47171 w P5 1 +47181 w D17 1 +471d1 b D3 1 +471e0 w P15 1 +471e6 b Q14 Q13 2 +471e7 b E2 1 +471f6 w C4 1 +47210 b F4 1 +47226 w C16 1 +47237 b C2 1 +47237 w M16 1 +47277 w S14 1 +47287 w Q5 1 +472c6 w E17 1 +472d6 w B7 1 +47317 b F15 1 +47327 b S18 1 +47350 w N3 1 +47387 b S11 1 +47391 w O4 1 +47397 b N8 1 +473a0 w R14 1 +473f0 b N14 1 +473f1 b O3 F8 2 +47410 w R14 1 +47416 b F5 F3 2 +47436 b P17 1 +47441 b D6 1 +47451 w P6 O6 P3 O3 4 +47467 b D2 1 +47491 b Q1 1 +474a7 b F4 1 +474c0 w Q5 1 +474d7 w O6 1 +474e0 b F16 1 +474e0 w N4 1 +474e1 w O6 1 +474f0 b R3 R8 2 +47521 w R4 1 +47550 b E18 1 +47551 b Q2 1 +47560 b C9 1 +47560 w C2 1 +47597 w Q15 Q14 P16 R15 4 +475a0 w D3 1 +475b7 b N2 1 +475b7 w G13 1 +475c1 w P4 1 +475d0 b P16 1 +475e1 w J2 1 +47607 w G4 1 +47616 b C4 1 +47616 w L2 1 +47617 w F6 1 +47626 w E5 1 +47636 b S16 1 +47647 b G14 1 +47656 b E17 1 +47676 b N4 1 +47687 w P5 1 +476a0 b D8 1 +476a7 w D3 C3 2 +476b1 w L4 1 +476b7 w Q15 1 +476d0 w M17 1 +476e1 b Q19 1 +476f6 w R6 Q6 2 +47701 b Q9 1 +47731 b Q14 1 +47787 w D14 1 +477c7 w D15 1 +477d7 w B3 1 +47821 b D16 1 +47821 w F5 E17 F17 E16 D15 5 +47840 w O16 1 +47877 b R3 1 +478f1 w N18 1 +47916 b F6 1 +47920 w S15 1 +47936 w O18 1 +47950 b E17 1 +47956 w D18 1 +47960 b P17 1 +47997 w P13 1 +479a1 b R3 1 +479a1 w R16 1 +479a7 w F4 1 +479f7 w C2 1 +47a10 w B17 1 +47a26 w P6 Q7 O5 3 +47a70 b N5 1 +47a90 b L16 1 +47ab0 w O5 1 +47ac6 w S8 1 +47af0 b Q5 1 +47b20 w E8 1 +47b26 w S17 1 +47b81 w C11 1 +47b87 b E6 1 +47b90 b P14 1 +47ba6 w F17 1 +47bc1 b P4 O5 2 +47bd7 b D16 1 +47bf1 w O12 1 +47c20 b C13 1 +47c50 w A3 1 +47c61 w S7 1 +47c91 w C3 1 +47cb0 w P16 1 +47d07 w Q17 1 +47d17 w D6 1 +47d20 b B16 1 +47d40 b M15 1 +47d46 w N5 1 +47d51 w Q4 1 +47d66 w D6 1 +47d96 w S3 1 +47e07 b E15 1 +47e10 w B3 1 +47e40 b F5 1 +47e47 b R16 1 +47e51 b C15 1 +47e67 b P18 1 +47e67 w B5 1 +47ec1 b D5 1 +47ef0 w P2 P4 2 +47f10 b T6 1 +47f30 b D17 1 +47f30 w D4 1 +47f81 w S17 1 +47fb1 w S2 1 +48041 w D8 1 +48066 w D12 1 +48070 b Q4 1 +48096 w N3 P5 2 +480a6 b P16 1 +480b6 w Q15 1 +480c0 b F16 1 +480e1 b E3 1 +48120 w J6 1 +48126 b C5 C2 2 +48157 b P11 1 +481c7 w L6 1 +481d6 w B8 1 +481f0 b Q15 1 +481f1 w C17 1 +48200 w O4 1 +48246 b Q6 1 +48257 w P18 1 +48266 b P5 1 +48267 w S3 N15 2 +48287 w R13 1 +48290 b L17 1 +482c7 w J16 1 +482e6 b T3 F18 2 +482f6 b R5 R3 2 +482f6 w O3 1 +482f7 w S3 S6 2 +48310 w C17 C12 2 +48350 w C16 1 +48356 w D6 1 +48361 b Q12 1 +48397 b G3 1 +483e0 w D15 1 +48421 b Q16 1 +48437 b G2 H5 R2 3 +48447 b T5 1 +48481 w D14 1 +484c7 b S2 1 +484d6 b R2 1 +48517 b R5 1 +48531 w C3 1 +48541 b Q7 1 +48566 w L3 1 +48567 b Q16 1 +48567 w S5 1 +48586 b D17 1 +485a6 b R13 L17 2 +485b0 b M17 1 +485c6 w G16 1 +485c7 b R7 1 +485e1 w R5 1 +485e7 w J15 1 +485f6 b R17 1 +48611 b R7 1 +48616 b F4 1 +48631 b P17 1 +48697 b Q7 1 +486b6 b M3 1 +486d1 b N18 1 +48740 w Q6 1 +48750 w C16 1 +48777 b O3 1 +48790 w C5 F6 2 +48791 b G3 1 +487a0 b R17 N16 2 +48810 w C6 A5 2 +48827 w D18 1 +48840 b R7 1 +48890 b F16 C17 2 +48897 b C17 1 +488c6 b F6 1 +488e1 w H14 1 +48906 w S3 1 +48921 b P6 Q7 R5 3 +48941 b C4 1 +48946 w Q6 1 +48980 b C15 1 +48987 b C13 1 +489a1 w M15 1 +489d6 b P16 1 +48a41 b B17 1 +48a57 w D16 1 +48a70 b B15 1 +48a87 w R14 1 +48ac7 b Q7 1 +48ae0 b E5 1 +48b06 b Q18 1 +48b07 b P8 1 +48b16 b Q3 1 +48b17 w D2 1 +48b20 w N17 1 +48b30 b D2 1 +48b31 w C16 1 +48b37 b C18 1 +48b41 w H17 1 +48b61 w N14 1 +48b96 b S16 1 +48bb6 w D14 1 +48c01 b C4 1 +48c20 w O16 1 +48c66 b P18 1 +48c80 b P15 1 +48c91 w P19 1 +48cd0 w E7 G4 2 +48cd7 b D9 1 +48d11 b Q15 1 +48d16 w C14 1 +48d40 b F5 1 +48d40 w C7 1 +48d56 b G17 1 +48d60 b N5 Q7 2 +48d61 b S16 1 +48d77 w C3 1 +48dc6 w P5 1 +48dd1 b B17 1 +48de6 w E16 R17 2 +48e00 w R15 1 +48ec0 w N18 1 +48ee0 b G5 1 +48ef0 w Q5 1 +48f10 b R6 1 +48f30 b D5 1 +48f40 w C4 1 +48f46 b E17 F15 2 +48f77 w S17 Q2 2 +48f91 b R15 1 +48fa0 b S9 1 +48fa7 b J16 1 +49036 b Q17 1 +49077 w S14 1 +49086 b Q5 P4 2 +490c1 b G15 1 +49141 w F3 F2 2 +49150 w E3 1 +49186 b S12 1 +49196 w R4 1 +491a7 b C14 1 +491c1 w Q16 R17 2 +491d1 b P5 1 +49201 b R15 1 +49206 b H3 1 +49241 w R5 1 +49256 w P5 1 +49276 w B16 1 +49281 w D3 1 +49296 b R17 1 +49296 w D18 1 +492d0 b E16 1 +492d0 w B14 1 +49311 w E17 1 +49320 b F15 1 +49341 b S7 1 +49346 b B18 1 +493c1 w C8 1 +493e0 b B17 1 +493f1 w B11 1 +49400 b C15 1 +49481 b D8 1 +49487 b P2 1 +49490 b O18 1 +494a1 b S17 1 +494c6 w B6 1 +494d1 b O9 1 +49517 w D12 1 +49526 b E6 1 +49546 w R5 1 +49571 b B2 1 +49590 b F3 1 +49591 b D16 S4 2 +49620 b P16 1 +49621 b B18 1 +49640 b R17 M17 2 +49641 b B17 1 +49696 b H2 1 +496b0 w P3 1 +496e0 b O15 1 +496e1 w E14 1 +49710 b P5 1 +49721 b F19 1 +49761 w R3 1 +49780 w G4 1 +49786 b P2 1 +49790 b D4 1 +497b1 w Q2 1 +497e1 b S17 1 +497e1 w Q9 1 +49830 b R4 1 +49831 b S14 1 +49841 b P6 1 +49861 b P4 1 +49877 w C9 1 +49890 b F4 1 +49897 w R18 1 +498a0 b S17 1 +498b0 w F11 1 +498b7 w C4 1 +498d0 b G2 1 +49911 w B6 1 +49926 b R16 1 +49966 w C14 1 +499e1 b O4 1 +49a81 w C12 1 +49a97 w P2 1 +49ad6 w B18 1 +49b37 w B13 1 +49ba0 w F16 D15 2 +49bc7 b C3 1 +49bd1 w C12 1 +49bd7 w R15 1 +49c10 w F11 1 +49c11 b P16 1 +49c31 b R3 1 +49c41 w D2 1 +49c57 b P4 1 +49c67 w S12 1 +49c71 w B2 1 +49c87 w S3 1 +49ca7 w Q19 1 +49cd6 w C3 1 +49ce7 w O4 S3 2 +49d30 b S15 1 +49d71 w D17 1 +49d77 w P18 1 +49d90 b E18 1 +49dc1 b N14 1 +49dd6 b D15 1 +49de1 b S3 1 +49df0 w R9 1 +49e37 w D2 1 +49e60 w R2 1 +49e87 b F18 1 +49e90 b D5 1 +49ec1 w S17 1 +49ed6 w P14 1 +49ef7 w B16 1 +49f00 w R3 1 +49f16 b C13 1 +49f46 b O6 1 +49f86 w D17 1 +49fb0 w F5 1 +4a000 w C17 1 +4a017 b R5 1 +4a020 b B4 H4 2 +4a047 b E2 1 +4a0b0 b C15 1 +4a0c0 b D15 1 +4a0f0 b C5 1 +4a101 w Q18 1 +4a141 b Q16 1 +4a141 w N3 1 +4a157 w B15 1 +4a180 b E7 1 +4a1b0 w D4 1 +4a1b1 w Q6 1 +4a1b7 b E13 1 +4a1c0 b S5 1 +4a1c6 w F3 1 +4a236 w F4 1 +4a237 w R13 1 +4a267 b N2 1 +4a277 b D15 G17 2 +4a291 b B4 1 +4a2c0 w Q18 1 +4a2e7 b S15 1 +4a307 w E13 D14 2 +4a340 w L3 1 +4a356 w Q14 Q15 2 +4a360 w M13 O12 2 +4a390 b E3 1 +4a390 w R6 1 +4a396 w N4 1 +4a3d7 w D4 1 +4a436 w R15 1 +4a441 b B15 1 +4a461 w Q6 R3 2 +4a487 b M17 1 +4a496 w E3 1 +4a4c0 b R17 1 +4a4d6 b O7 1 +4a4d6 w Q13 1 +4a4e6 b P14 1 +4a531 b F18 1 +4a546 b R3 1 +4a576 b R14 1 +4a577 w D2 1 +4a586 b B15 1 +4a5a6 b S15 1 +4a5c1 w R3 Q3 R9 R8 S5 R4 6 +4a616 b B5 1 +4a616 w S5 1 +4a636 b B6 1 +4a660 b O17 1 +4a671 b E2 1 +4a6b1 b J6 1 +4a6b6 w F14 1 +4a716 b Q5 R6 2 +4a720 w R16 1 +4a757 b O2 1 +4a761 w C3 1 +4a790 w R6 1 +4a791 b S15 1 +4a7a0 w N7 1 +4a7a6 b C18 1 +4a7c7 w R5 N2 2 +4a7d0 b S7 1 +4a800 w O14 1 +4a830 w B6 1 +4a856 w O15 1 +4a8e7 b P5 1 +4a8f0 b O16 O15 2 +4a8f1 b E3 1 +4a911 b S3 1 +4a951 w N2 1 +4a966 w O17 1 +4a967 w D16 1 +4a980 w B5 1 +4a9a0 b P15 1 +4a9c1 w E6 B17 2 +4a9f7 b C5 1 +4aa01 b M18 1 +4aa11 w H3 1 +4aa21 w C2 1 +4aa46 w R19 1 +4aa61 b G16 1 +4aa90 b H18 1 +4aa90 w B3 1 +4aac6 w D6 1 +4aae0 b R13 1 +4ab30 w H15 1 +4ab31 b H16 1 +4ab51 b E6 1 +4ab70 b N16 1 +4ab90 w Q5 1 +4abe6 b F3 1 +4ac07 w R18 1 +4ac21 w R2 1 +4ac27 b N16 1 +4ac37 b H16 1 +4ac37 w Q5 1 +4ac76 w H3 1 +4ac91 b N4 M3 L3 3 +4aca6 w M4 1 +4acb6 w F18 1 +4acc6 b R9 1 +4acf0 w R15 1 +4ad06 b Q7 1 +4ad17 b Q16 1 +4ad20 w M17 1 +4ad37 w B15 1 +4ad57 b P6 1 +4ad86 b F18 1 +4ada0 w F14 1 +4adc1 b D14 1 +4ade0 b D5 1 +4ae21 w D6 E5 2 +4ae37 b C18 1 +4ae67 w D4 1 +4aea0 b N14 1 +4af00 w B18 1 +4af10 b E19 1 +4af46 b R5 1 +4af67 b G18 1 +4af97 b T2 1 +4b001 w G5 1 +4b020 w D8 1 +4b036 b R17 1 +4b076 w M3 1 +4b081 w R3 1 +4b097 b N2 1 +4b0f1 b S5 1 +4b121 b B7 1 +4b136 b D2 1 +4b176 w F18 1 +4b190 b D14 1 +4b1a6 w N5 1 +4b1c6 w P16 1 +4b1e0 w H7 F8 D16 3 +4b1e7 b Q3 1 +4b226 b Q3 1 +4b246 b R7 1 +4b2a6 w P15 1 +4b2c7 b B5 1 +4b2f7 w E7 1 +4b321 b F5 1 +4b360 b S4 1 +4b381 w C6 1 +4b386 w Q5 1 +4b3b6 b N2 1 +4b3b7 b D12 1 +4b3b7 w G14 1 +4b3e1 w R19 1 +4b3e6 w R3 Q4 2 +4b407 b Q18 1 +4b477 b Q2 1 +4b486 b Q15 1 +4b490 b B2 1 +4b4c1 w R18 1 +4b4e1 b N17 B6 2 +4b4e1 w R6 1 +4b4f0 w S15 1 +4b501 b B16 1 +4b507 b Q17 1 +4b511 w E3 1 +4b546 w E4 1 +4b561 w Q6 1 +4b580 b J4 1 +4b5b0 b R8 O16 2 +4b5b0 w D15 1 +4b5d1 b B5 1 +4b611 b Q11 1 +4b611 w O3 1 +4b617 b D15 1 +4b627 b C2 1 +4b647 w S17 1 +4b667 b O4 1 +4b677 w D17 1 +4b6a6 b O5 N4 P6 3 +4b6b7 b E14 1 +4b6d7 w C17 1 +4b6e7 w E18 1 +4b700 b D15 1 +4b701 w P13 1 +4b706 w G15 1 +4b731 b B17 1 +4b747 w S15 S5 2 +4b7c0 b F11 1 +4b7e0 w O3 1 +4b7e7 b T4 1 +4b7f0 b Q5 L3 R6 R7 R8 Q7 Q6 7 +4b7f0 w Q5 Q6 N3 M3 L3 N4 M4 L4 8 +4b810 b F17 1 +4b810 w F16 F17 G17 D14 C12 D12 D11 7 +4b850 b B17 1 +4b916 w J13 1 +4b926 w B7 1 +4b946 b C9 1 +4b957 b G18 1 +4b957 w Q7 1 +4b996 b P12 1 +4b9a0 w D14 1 +4b9a6 w C12 1 +4b9b7 b D9 1 +4b9d0 w C16 1 +4b9e7 b M3 1 +4b9f7 w C8 1 +4ba01 b P13 1 +4ba16 w H16 1 +4ba30 b C18 1 +4ba30 w C12 1 +4ba36 b C18 1 +4ba50 b R18 1 +4ba56 w N8 1 +4ba61 b C14 E18 2 +4bae7 b B12 1 +4baf0 w L4 1 +4bb17 w G18 1 +4bb27 w D5 1 +4bb37 b A15 1 +4bb40 b P5 1 +4bb56 b R4 1 +4bb76 b R2 1 +4bb77 b D15 F15 2 +4bb90 b P2 1 +4bbf6 w P17 1 +4bc07 b S5 1 +4bc10 b P16 Q17 R16 3 +4bc16 b C16 1 +4bc17 b O16 P15 2 +4bc17 w O17 1 +4bc30 b S18 1 +4bc50 b D17 1 +4bc57 b F12 C15 C14 D15 E16 5 +4bc57 w D16 1 +4bc97 b S6 1 +4bcb1 b E2 1 +4bcc0 w N16 1 +4bcf7 b F4 C3 2 +4bd17 w Q16 1 +4bd21 b B3 1 +4bd41 b C15 1 +4bd56 b N3 1 +4bd61 b G17 1 +4bd70 b O4 1 +4bda0 b N5 1 +4bda6 b F4 1 +4bdc7 b E15 1 +4bdd1 b C17 1 +4be36 w E4 1 +4be87 w D17 1 +4bed1 b C6 1 +4bf46 w D16 1 +4bf51 b P13 1 +4bf71 w P11 1 +4bf81 w Q6 1 +4bf86 w G4 1 +4bf90 b R16 1 +4bfd7 w Q6 1 +4c016 w C2 O3 N4 3 +4c017 b R12 1 +4c021 b E14 1 +4c030 b S18 1 +4c030 w P3 O6 2 +4c050 w N17 1 +4c076 w C12 1 +4c121 b P17 1 +4c127 b D17 1 +4c141 w C8 1 +4c147 w B4 1 +4c160 w R9 1 +4c1a7 b R8 1 +4c1b6 b P15 1 +4c1e6 b N16 1 +4c1f0 b F3 1 +4c1f0 w F4 F3 G3 D6 C8 D8 D9 7 +4c217 b Q16 1 +4c246 w Q4 1 +4c280 b E14 1 +4c296 w O17 P15 2 +4c2a1 b C13 1 +4c2b0 w O4 1 +4c2d0 b H17 1 +4c2d0 w S5 1 +4c327 w R18 1 +4c340 w N5 1 +4c3d1 w G3 1 +4c3d6 b A17 1 +4c3e1 w F7 1 +4c3f6 w P17 1 +4c407 b O15 O14 R15 R14 4 +4c437 w P12 1 +4c451 w D11 1 +4c476 b B3 1 +4c481 b L16 1 +4c4c0 b D13 1 +4c4d0 b F4 1 +4c4e1 w C7 1 +4c4f7 w S8 1 +4c501 b O15 1 +4c506 w R11 1 +4c511 b F6 1 +4c520 b O13 1 +4c530 w P2 Q3 H6 3 +4c587 w P2 1 +4c5a6 b N14 1 +4c5c7 b D16 1 +4c5d0 w L3 1 +4c5e0 b S14 1 +4c5e7 b M16 1 +4c5f0 w Q6 1 +4c5f7 w R3 1 +4c607 w N14 1 +4c671 w Q3 1 +4c6c6 b D5 1 +4c6d0 w G17 1 +4c6d1 b O16 1 +4c741 w Q16 1 +4c756 b S14 1 +4c7b0 w R16 1 +4c7c7 b C17 1 +4c7e7 b M3 1 +4c7e7 w Q18 1 +4c807 b Q14 1 +4c846 w D5 1 +4c866 w B17 1 +4c880 b E14 1 +4c891 w B16 1 +4c8a0 b H17 1 +4c8e7 w C13 1 +4c8f0 w O16 1 +4c906 b F16 1 +4c927 b E18 1 +4c931 w C3 1 +4c950 b Q6 1 +4c951 w Q13 1 +4c966 w R18 1 +4c980 b S7 1 +4c987 b F7 1 +4c996 b O3 R7 2 +4c9a0 b G3 1 +4c9a1 w R3 1 +4c9b0 b R5 1 +4c9d6 b S13 1 +4c9f7 w R15 1 +4ca27 w O16 1 +4ca46 b Q12 R14 2 +4cab0 w Q5 1 +4cac6 w M6 1 +4caf6 w S6 1 +4cb00 b S15 1 +4cb06 w E6 1 +4cb21 w O4 1 +4cb41 b B18 1 +4cb67 b D18 1 +4cb67 w Q14 1 +4cb90 w R16 1 +4cb96 b E7 1 +4cbb0 b H17 1 +4cbb1 w E14 1 +4cbe1 w E6 1 +4cbe6 w R14 S4 2 +4cc40 b F15 1 +4cc70 w O5 1 +4cc96 b R8 1 +4cc97 w G4 1 +4ccb7 b H4 1 +4cd10 w P18 1 +4cd31 b D11 1 +4cd41 b G5 1 +4cd46 w B2 1 +4cd71 b O4 S4 2 +4cd86 w Q15 1 +4cd96 w M3 1 +4cda7 b H3 1 +4cdd7 b E6 1 +4cdf0 b P4 1 +4cdf7 w D2 1 +4ce21 b N17 1 +4ce30 w E8 1 +4cef7 b F15 1 +4cf00 w C4 1 +4cf37 w R3 1 +4cf46 w C3 F8 2 +4cf56 w R12 1 +4cf87 b R2 1 +4cf90 b D7 1 +4cfb0 b N16 B17 2 +4cff1 b D6 1 +4cff1 w E17 1 +4d031 b C3 D4 E4 F5 F4 G4 6 +4d031 w C4 1 +4d056 w E17 1 +4d076 w C3 1 +4d086 w F16 1 +4d0e0 b E17 1 +4d0f0 b E15 1 +4d106 b E3 1 +4d110 w S15 1 +4d146 w Q2 1 +4d1b7 b O17 1 +4d1d1 b S6 1 +4d1f0 w F12 1 +4d206 w C17 1 +4d247 b S3 P3 O4 N3 N2 5 +4d251 b F4 1 +4d280 b F18 1 +4d2a1 w G15 1 +4d2e1 b T6 1 +4d2e1 w Q5 1 +4d300 w M6 1 +4d316 w D14 1 +4d330 b D6 1 +4d366 w R4 1 +4d3e0 b R8 1 +4d410 b B2 1 +4d426 b M5 1 +4d460 b D5 D6 G3 H3 J3 G4 H4 J4 8 +4d460 w D5 J3 C6 C7 C8 D7 D6 7 +4d4a0 b P18 P16 2 +4d4b1 w C18 1 +4d4b6 b O7 1 +4d4c0 b S2 1 +4d541 w E4 1 +4d550 b Q15 1 +4d570 w R17 1 +4d590 b D13 1 +4d591 b P3 1 +4d597 b H2 1 +4d5a7 b G17 1 +4d5f7 w R16 1 +4d610 w Q5 1 +4d611 b S8 1 +4d626 b C17 1 +4d681 b C16 1 +4d690 w F17 1 +4d691 w Q14 1 +4d6a1 b S5 1 +4d6c0 w B17 1 +4d6c1 b D2 1 +4d6f0 b L16 C5 2 +4d707 w D15 1 +4d710 w C17 H17 2 +4d727 b B5 1 +4d750 b M15 1 +4d751 w R3 1 +4d771 w E14 1 +4d777 b E18 1 +4d780 b C14 1 +4d780 w D14 C14 C13 F16 H17 H16 J16 7 +4d7b0 w C6 1 +4d7c6 w N5 1 +4d7e1 b M15 1 +4d896 w B3 1 +4d8b6 b D14 1 +4d8f6 w G17 1 +4d906 w D5 1 +4d911 w R8 1 +4d930 w E13 1 +4d936 w B17 1 +4d937 b D5 1 +4d950 b R3 1 +4d956 b E17 1 +4d9a0 w C17 1 +4d9b1 w E5 1 +4d9b6 w F4 1 +4d9c0 b B3 1 +4d9c1 w Q3 1 +4d9e1 b Q15 1 +4d9f6 b N17 1 +4da10 w N16 1 +4da30 w H16 1 +4da31 b Q2 1 +4da31 w S6 1 +4da50 b R13 1 +4da66 b M14 1 +4db31 b F14 1 +4db37 w C17 E14 F17 F16 4 +4db51 w B14 1 +4db86 b O6 1 +4db97 b F6 1 +4dba0 b Q13 Q17 2 +4dba7 w P18 1 +4dbc0 w E16 1 +4dc06 w C17 1 +4dc56 w C12 1 +4dc67 w P14 1 +4dc86 b Q5 1 +4dc86 w Q5 1 +4dca1 w N4 M6 2 +4dd01 b R13 1 +4dd10 b S6 1 +4dd91 b G4 1 +4dda7 b P15 1 +4ddf6 b D17 1 +4de00 w P14 1 +4de31 w R17 1 +4de56 w C3 1 +4de67 w R15 S5 2 +4de76 w Q6 1 +4de87 b N16 1 +4de91 b R3 1 +4dee0 b R11 1 +4df06 w F5 C6 2 +4df16 b Q14 1 +4df27 w O15 1 +4df30 b M16 1 +4df66 w R15 1 +4df80 w C16 1 +4df86 b S15 1 +4df87 w C4 1 +4dfb0 b R16 1 +4dfc6 b R16 1 +4dfe0 b Q16 1 +4dff0 b Q12 1 +4dff0 w Q12 1 +4e006 w G3 1 +4e030 b D13 1 +4e096 b S17 1 +4e0a1 w R7 1 +4e0b7 b Q7 1 +4e0c7 b N4 1 +4e106 w O17 1 +4e117 w E5 1 +4e141 w D6 E7 2 +4e156 b E16 1 +4e157 w C7 1 +4e176 w E3 1 +4e1a0 w S3 1 +4e1c1 b N16 1 +4e1f1 b E5 1 +4e201 b Q3 1 +4e206 b O17 1 +4e231 w F3 1 +4e241 b C17 1 +4e247 b B3 1 +4e260 w O9 1 +4e266 w P13 O13 2 +4e297 b S14 1 +4e2b1 b F2 1 +4e2b1 w P4 1 +4e2c7 b E3 1 +4e310 w P3 1 +4e317 w O4 1 +4e327 w S14 1 +4e340 w R14 1 +4e370 w O6 1 +4e376 b S12 1 +4e387 b B17 1 +4e396 b F3 1 +4e3f0 w L4 1 +4e3f7 w E4 1 +4e417 w Q18 1 +4e437 w P18 1 +4e441 b Q16 1 +4e447 w S16 R17 R16 M17 4 +4e487 w O15 1 +4e496 w O17 1 +4e4a0 b Q14 Q13 2 +4e4b1 b H2 1 +4e4d0 b O17 P15 2 +4e4f0 b O4 1 +4e501 w D9 1 +4e516 b S2 1 +4e560 w D15 1 +4e576 w D15 1 +4e581 b N2 1 +4e587 w C6 1 +4e5b0 w E17 1 +4e5c6 b D6 1 +4e617 w N16 1 +4e621 b S2 1 +4e626 w D4 1 +4e627 b O8 1 +4e6a0 b P14 1 +4e6c0 w D17 1 +4e6c7 b L5 1 +4e706 w F3 C17 2 +4e736 w P16 1 +4e757 b D3 1 +4e787 w Q14 1 +4e7a7 w G6 1 +4e7b6 w R2 1 +4e7c1 b O17 1 +4e816 b C4 1 +4e817 b F17 1 +4e827 b D3 1 +4e847 w E17 F18 E18 3 +4e851 w D7 1 +4e866 b R6 Q5 2 +4e867 b M4 1 +4e881 b B12 1 +4e8a0 b P14 1 +4e8c0 b P14 O16 2 +4e8c6 w E8 1 +4e8d7 b D13 1 +4e8f0 w F12 1 +4e8f6 b O6 1 +4e900 b Q3 R4 2 +4e901 b Q13 1 +4e911 b Q2 1 +4e941 b Q17 1 +4e957 b R16 1 +4e981 w S14 1 +4e9b0 b C8 1 +4e9c6 w D5 1 +4e9d0 b G16 1 +4e9d1 w N2 1 +4e9e0 b E15 1 +4e9f6 b C18 1 +4ea01 b D14 1 +4ea26 b O3 1 +4ea47 b B3 N4 2 +4ea76 b G16 1 +4ea87 b S15 1 +4eaa1 w N16 1 +4eaa6 w D5 1 +4eac1 b D4 1 +4eac6 b B2 1 +4eb01 w C16 1 +4eb27 w P16 1 +4eb31 w M2 1 +4eb36 w B15 1 +4eb61 w J4 1 +4eb90 b P17 1 +4ec17 b O4 1 +4ec46 w D13 1 +4ec66 b E16 1 +4ec70 b P5 1 +4ecb0 w E16 1 +4ecd1 b S16 1 +4ecf1 w O15 O14 R15 R14 4 +4ed36 w S14 1 +4ed60 w P2 1 +4ed76 b J3 1 +4ed77 b R9 1 +4ed96 w O4 N4 2 +4eda1 b S18 1 +4edf0 w J3 1 +4edf6 w N15 1 +4ee11 b O16 N16 2 +4ee20 w E15 1 +4ee26 w E2 1 +4ee56 b N3 1 +4ee60 w E7 1 +4ee96 b S5 1 +4eea1 b S7 1 +4eea6 w G17 1 +4eeb6 w P4 Q6 2 +4eef7 w E5 F5 C3 G3 4 +4ef06 w P17 1 +4ef16 w F5 1 +4ef27 w S16 1 +4ef36 b C4 1 +4ef90 w G16 1 +4efa0 w F16 1 +4efa7 w R16 1 +4f000 b P2 1 +4f050 b S14 1 +4f076 b L18 1 +4f097 w E17 1 +4f0e7 w R13 1 +4f126 b S4 1 +4f130 b P17 1 +4f136 b R2 1 +4f137 b M14 1 +4f156 b L2 1 +4f197 w C3 B3 2 +4f1a6 w D3 1 +4f1b1 w S15 1 +4f1c7 b M17 1 +4f236 w H2 1 +4f246 w C5 1 +4f251 w R6 1 +4f267 b C18 C15 D14 C13 B13 5 +4f2a0 w O3 1 +4f2a1 b N15 1 +4f2c1 b D17 O15 2 +4f2c7 b R4 1 +4f2d0 w M15 1 +4f301 b R17 S17 2 +4f306 w E14 F17 2 +4f341 b D3 1 +4f347 b C18 1 +4f380 w Q14 1 +4f3a6 b F3 1 +4f3b6 w D7 1 +4f3c1 b P7 1 +4f410 b M4 1 +4f446 w E17 1 +4f461 b C2 1 +4f4d7 w R17 R4 2 +4f4f1 b Q16 1 +4f501 b S18 1 +4f501 w L4 1 +4f520 w F7 1 +4f521 b E15 1 +4f560 w Q3 1 +4f571 b F18 1 +4f576 w Q13 1 +4f590 w D4 1 +4f5a7 b O5 1 +4f5b7 w P5 1 +4f5c7 b P14 1 +4f5d7 b C11 1 +4f5e6 b Q3 1 +4f5f0 w D8 1 +4f637 w R12 1 +4f661 w H3 1 +4f676 b Q19 1 +4f6b0 w E16 1 +4f6b7 w S6 1 +4f6f6 b R16 1 +4f6f6 w D3 1 +4f716 b E16 1 +4f741 w D13 1 +4f770 w G17 1 +4f780 b B13 1 +4f780 w Q8 1 +4f7a0 w R7 O3 2 +4f7a6 b D6 D3 2 +4f7a6 w R3 1 +4f7d1 b B5 1 +4f7e6 w G11 H3 2 +4f801 b S14 1 +4f820 b E3 1 +4f836 w F17 1 +4f850 w B3 1 +4f861 w D17 1 +4f867 w J18 1 +4f871 w R16 1 +4f956 w F3 E4 2 +4f957 b M3 1 +4f977 b E3 1 +4f9a7 w F5 1 +4f9b1 w D6 1 +4f9c7 w Q3 1 +4f9e1 b C11 F15 2 +4f9f7 w P18 1 +4fa07 w P2 1 +4fa10 w Q15 1 +4fa61 w R17 M16 2 +4fa70 b O3 1 +4fae0 b C6 1 +4fb21 b O16 1 +4fb61 w A5 E6 2 +4fba0 w F16 1 +4fbb6 w R18 1 +4fbc0 b Q3 1 +4fbc7 w H3 1 +4fbd1 w D17 1 +4fbe1 w S16 1 +4fbf6 w M17 1 +4fc07 b R18 1 +4fc27 w O3 1 +4fc37 w E16 1 +4fc81 w P6 1 +4fc86 b O16 1 +4fc90 w P17 R18 2 +4fc96 b R13 1 +4fcd6 b C7 1 +4fcf7 b M6 1 +4fd76 w Q17 P16 L16 3 +4fd80 b F17 1 +4fda6 b F4 1 +4fdb7 b R6 1 +4fe17 b S13 1 +4fe26 w D7 1 +4fe56 w G6 1 +4fe66 w P3 1 +4ff20 b Q3 1 +4ff30 b F16 1 +4ff76 b C13 1 +4ff81 w F17 1 +4ff87 b S13 P12 2 +4ff90 b B18 1 +4ffc1 w R3 1 +50011 w O18 1 +50056 b S3 1 +50081 b D17 1 +500b1 w N17 1 +500c0 b R15 1 +500e0 w H17 1 +50106 w Q2 1 +50110 b C9 1 +50120 w H3 1 +50126 w O6 1 +50130 w Q14 1 +50131 w M17 1 +50146 w F3 1 +50191 w S4 1 +501c7 w E18 1 +501d7 w R3 1 +501e0 b B5 1 +501f1 b H4 1 +50231 w E14 1 +50246 b Q3 Q5 2 +50256 w R17 1 +502a7 w D7 1 +502b0 w O16 S15 2 +502c6 w R15 S15 2 +502d6 b S18 1 +502d7 b C7 1 +502e7 w R4 1 +502f1 b F8 1 +502f6 w G15 1 +50310 b Q7 1 +50377 w O14 N13 2 +50386 b E18 1 +503a7 w N4 1 +503b7 w D6 1 +50411 b R18 1 +50417 b E17 1 +50431 w S15 1 +50457 w G3 1 +504a6 b R14 Q15 2 +504c0 b C13 1 +504d7 w C15 1 +504e6 w C15 1 +50500 w E16 1 +50501 b R18 1 +50557 w L17 1 +50577 b O18 1 +50641 b E18 1 +50657 b R5 1 +50666 b O17 1 +50671 b P6 1 +50676 b C12 1 +506c0 w E5 1 +50720 b E15 1 +50731 b P18 1 +50737 w Q16 1 +507a7 b R4 1 +507c1 b R7 1 +50807 b N4 1 +50871 b B14 1 +50887 w M17 1 +508a6 b E4 1 +508b0 b P2 1 +508e7 b Q16 1 +50930 b J17 1 +50961 w O18 1 +509b0 w F3 1 +509c1 b J16 1 +509d6 w S4 1 +509d7 b S3 1 +50a11 b N17 1 +50a16 b Q18 1 +50a31 b F7 1 +50a41 w P18 1 +50a56 w D13 1 +50a61 w M16 1 +50a77 b B3 1 +50a81 b S16 1 +50a87 w O5 1 +50af1 b E15 1 +50af7 w F16 1 +50b00 w R2 B17 2 +50b06 w O17 1 +50b20 w C11 1 +50b26 b C4 1 +50b26 w C4 D3 2 +50b70 w P4 1 +50b86 w D7 1 +50ba1 w P15 1 +50ba6 b D15 1 +50bd0 b G11 1 +50c40 b Q17 1 +50c76 b S16 1 +50c96 w E4 1 +50cb0 b J14 1 +50cc0 b C17 G16 2 +50cd1 b M4 1 +50ce0 b C18 1 +50ce6 w D14 1 +50d77 b G17 1 +50d91 w R3 1 +50da1 w S17 1 +50df6 b C6 1 +50e06 w B5 1 +50e10 b P6 1 +50e31 b M5 1 +50e31 w G14 1 +50e66 b N3 1 +50f01 w B3 1 +50f17 b P15 1 +50f21 b A6 1 +50f36 w L2 1 +50f51 b R4 1 +50f60 b Q13 1 +50f91 b E14 1 +51001 b F3 1 +51010 b M15 1 +51047 b B15 1 +51057 b C17 1 +51067 w B5 1 +510c1 b C3 1 +510d0 b E7 1 +510e6 b E3 1 +510f0 w C13 1 +51130 b P5 1 +51166 b O13 1 +511a1 w C15 1 +511a6 b E6 1 +511d0 b C5 C2 F2 3 +511f0 w F17 1 +51221 b E5 1 +51250 b C5 1 +51277 w E18 1 +512d7 b O16 N17 2 +51337 w E15 1 +51351 b O6 1 +51366 b D16 1 +51371 b F4 1 +51390 b P4 1 +513a1 w O17 1 +513a7 b R16 1 +513f7 w E4 1 +51400 w M5 1 +51471 w S6 1 +51491 b Q14 1 +514b1 w P12 1 +514b6 b Q4 1 +514b7 b G4 1 +514b7 w R17 O16 O17 Q12 P14 5 +514f0 w R12 1 +51556 b O4 1 +51561 b E17 1 +51576 b Q6 R6 P3 P5 N3 N4 M4 F4 8 +51576 w R3 R5 Q6 Q7 R7 5 +51586 b F16 1 +515b6 w P4 O3 F4 G4 4 +515b7 w P3 S7 2 +515c7 b O16 1 +515d0 b B3 1 +515f0 b R3 1 +51656 w C7 1 +51691 b B16 1 +51697 w P17 O18 P18 3 +516b0 w G5 1 +516b6 b O5 1 +516d0 b R9 1 +516f0 w R7 1 +516f6 b O6 1 +51761 w S17 1 +51766 w P5 1 +51791 w C1 1 +51797 b F15 1 +517c6 w E17 G17 2 +518a1 b P3 1 +518b6 b G6 1 +518e6 b S9 1 +518f6 w C16 1 +51947 w P4 O5 2 +51970 b J17 1 +51981 w O13 1 +519c6 b Q6 1 +51a00 w C18 1 +51a26 b E15 D15 2 +51a36 b Q13 1 +51a56 b Q2 1 +51a60 w S14 1 +51a77 w O16 1 +51a80 w N8 1 +51a90 b J16 1 +51b17 b D14 1 +51b31 w E17 1 +51bb6 w O18 1 +51bb7 w P3 1 +51bc1 w R6 P4 2 +51be7 w G15 1 +51c00 w E18 1 +51c10 b D7 1 +51c37 w P2 1 +51c51 b Q14 1 +51c70 w E18 C17 D14 C14 4 +51c71 b D7 1 +51c91 w S14 1 +51c96 b B17 1 +51ca6 w D13 1 +51ca7 b N6 1 +51cb0 w C5 1 +51cb1 b C12 1 +51cb6 b C7 1 +51cc6 b B16 1 +51ce0 w C17 1 +51d20 w D14 1 +51d30 b G4 1 +51d37 b H16 1 +51d61 w C4 1 +51d70 b M5 1 +51d90 b C3 1 +51d97 b B13 1 +51dc0 b P16 1 +51dd1 b D14 1 +51dd7 b F13 B5 2 +51e57 b D6 G6 2 +51e57 w G2 1 +51e97 w B16 1 +51fb0 b B5 1 +51fd7 b R3 1 +52016 b C2 1 +52036 b C16 1 +52061 w E13 1 +52077 w S16 Q17 2 +52097 w G16 1 +520a0 w C2 C17 2 +520a6 w R18 1 +520b1 b C2 1 +520c6 b R3 1 +520e1 w P4 D14 2 +520e6 b D7 1 +52126 w R8 1 +52177 b R17 1 +52187 b H5 1 +52196 b N17 1 +521e6 b C17 1 +521e6 w Q6 1 +521f1 w R2 1 +52207 b R2 1 +52211 w C3 1 +52260 b R12 1 +52271 b H16 D12 2 +52277 w G14 1 +52291 b Q17 1 +522e6 w P17 S17 2 +52311 b R17 N16 2 +52370 w C16 D16 C13 E15 C17 5 +523a1 w S5 1 +523e0 w J17 1 +52411 b R2 1 +52411 w H3 E2 D2 3 +52480 w E16 1 +52510 b R3 1 +52520 w N3 E6 2 +52571 w Q4 R3 2 +525b0 w C14 1 +525d0 b E4 1 +525d6 w F4 E4 2 +525e7 b Q17 1 +525f6 b C13 C15 2 +52671 b D7 1 +526c0 w M15 1 +526d0 b D15 1 +52746 b F17 1 +52757 b P15 1 +52761 w B9 1 +52777 b E1 1 +52780 w S6 1 +527c6 b D18 1 +527e1 b R18 1 +527f1 b F5 1 +52801 w F5 1 +52836 b C16 1 +52837 b M17 1 +52867 w C15 1 +52891 b R18 1 +528a1 b H5 1 +528c0 b L17 1 +528d6 w O5 1 +528e1 w C17 1 +528e6 b F14 1 +528e7 b C8 1 +52927 b F3 1 +52941 b P2 1 +529a7 b G6 1 +529c0 b P6 1 +52a20 w G18 1 +52a86 w Q16 1 +52a96 w O17 1 +52ae0 w O17 1 +52ae1 b B3 1 +52ae1 w B15 1 +52b51 b F2 1 +52b61 w B4 1 +52ba6 w R4 1 +52bc1 b C3 D7 2 +52bd6 w B6 1 +52c00 b F4 1 +52c57 w H14 1 +52c80 w B14 1 +52cb6 w D19 1 +52cb7 b C15 1 +52d17 b B2 1 +52d86 b N17 1 +52d97 b J16 1 +52da7 b B14 1 +52df7 b B7 1 +52e00 b R2 1 +52e06 b C18 1 +52e21 w Q16 1 +52e56 b R14 1 +52e96 b R5 1 +52eb1 b E18 N17 2 +52ec1 b N18 1 +52ed1 w A15 1 +52f16 w Q4 1 +52f27 w S17 1 +52f37 w E13 1 +52f50 b R9 1 +52f70 w D5 1 +52f97 b N5 1 +52fa6 b N5 1 +52fb0 w C4 1 +52fc1 b E14 1 +53000 w E16 1 +53011 b B17 1 +53036 b D5 1 +53077 w N17 R1 2 +53086 b E3 B3 2 +53087 b C3 1 +530b7 w C16 1 +530d0 w D15 C13 2 +530e7 b C8 1 +53116 w N17 1 +53130 w C3 1 +53190 b D7 1 +531c0 w N14 1 +531d1 w H17 1 +531d6 b E17 B17 2 +531e0 w D12 C6 2 +532e7 w C2 C9 2 +53307 b C6 1 +53311 b B2 1 +53381 w G6 1 +533b7 b O16 1 +533c0 b Q4 1 +533c1 w B4 1 +533c7 w G5 1 +53416 w R4 1 +53437 w R3 1 +53461 w E13 1 +53466 b F15 F17 2 +53497 b C14 F17 D14 F16 D13 G16 C13 G17 C11 J17 10 +534a1 w S6 1 +534a7 w D9 1 +534c7 b L5 1 +53546 w Q3 1 +53567 w J16 1 +535a1 b P5 1 +53600 w G17 1 +53641 b P12 1 +53656 b M18 1 +53670 b C12 1 +53676 w S15 1 +536d0 w M17 1 +536e1 b E16 1 +536e6 w D13 1 +53711 w F3 D5 F4 3 +53750 w Q15 1 +53777 b L17 1 +53796 w C3 1 +537b7 w R7 1 +537e6 b M15 1 +53810 w D9 1 +53816 b C8 1 +53817 w P15 1 +53831 b Q8 1 +538a7 b C5 1 +538b6 w O18 1 +538c7 b M17 1 +538e1 b C13 B12 2 +538e6 w E4 1 +53916 w F13 1 +53970 b P4 1 +53980 b M4 1 +539a0 w D15 1 +53a36 b C3 1 +53a60 w R5 O6 2 +53aa6 b D15 F16 2 +53aa6 w R14 1 +53ac0 b F5 1 +53ac1 w Q12 1 +53ae0 w F13 1 +53ae1 b F15 1 +53b01 b D3 1 +53b21 w E6 1 +53b31 w S3 1 +53b57 b R18 1 +53b67 w E3 1 +53bc0 b F11 1 +53be1 b P2 1 +53bf0 w G9 1 +53c21 w E13 1 +53c76 w P6 1 +53ce0 b B3 1 +53cf0 b M3 1 +53d06 w S18 1 +53d16 w O14 1 +53d27 w F5 1 +53d37 w C7 1 +53d41 w D5 1 +53d67 w O15 1 +53d81 w R13 1 +53d91 b R5 1 +53db0 w D16 1 +53db6 b C15 B15 2 +53dd6 b Q2 1 +53dd7 b S8 1 +53e01 w O4 1 +53e10 w P6 1 +53e61 w R3 1 +53e67 w N4 1 +53e76 w Q8 R6 2 +53ec0 b G4 1 +53ed0 w O2 1 +53ee6 w D6 1 +53f60 b E16 1 +53f70 w P13 1 +53f87 w E7 D6 2 +53fa0 w H16 1 +53fb1 w Q15 P16 2 +53fc7 b D18 1 +53fc7 w O16 1 +53fd7 w E17 1 +53fe0 w C6 1 +53ff0 w D3 1 +53ff7 w R5 1 +54041 w D8 1 +540b7 b D2 1 +540c0 w P2 1 +540d0 w P18 1 +540d6 b O4 1 +54100 b F4 1 +54106 w D6 D7 2 +54107 b J3 1 +54110 w H3 1 +54117 w M3 1 +54127 b O18 1 +54130 b C2 D2 2 +54146 w B5 1 +54150 w F15 1 +541f0 b O1 1 +54200 b R6 1 +54211 b C18 1 +54217 w Q8 1 +54220 b N17 1 +54220 w Q17 1 +54221 b P16 1 +54231 b D17 1 +54231 w C17 F16 D16 D15 E14 E15 G15 G16 C14 C13 D13 11 +54237 w Q17 1 +54247 w B1 1 +54260 b E3 1 +54260 w C6 1 +54271 w C3 1 +54287 w N3 1 +542b1 b E5 1 +54357 w O17 1 +543a0 b F13 1 +543b7 b R18 1 +543e1 b D8 1 +54407 w D2 1 +54426 b E3 1 +54446 w D16 1 +54466 w C6 D4 2 +54470 w H5 H6 2 +54471 b R18 1 +54491 w B4 1 +54496 b P15 1 +544b1 w S6 1 +544d1 b G17 1 +54501 w M3 1 +54516 b D15 1 +54537 b P14 1 +54547 w O18 1 +54580 w P17 1 +54597 w G3 1 +545c1 b C3 1 +545d6 w Q3 1 +545e7 b C18 1 +54600 b P15 1 +54611 b E14 1 +54647 w E2 1 +54666 b O3 1 +54697 w R4 G5 2 +546a7 b C3 1 +546e6 w H2 1 +546f0 b E8 1 +54711 b R6 1 +54756 w C6 1 +54766 b R13 1 +54790 b D17 1 +54797 b L4 1 +547f1 b O5 1 +54807 b R3 1 +54840 b R13 1 +54847 b C17 H16 2 +54867 b E19 1 +54870 w P15 1 +54887 w E14 1 +54890 b C6 1 +548a1 w T18 1 +548d6 w C18 1 +548e1 w P6 O4 2 +548e7 w C17 1 +548f0 w L3 1 +54926 b C2 1 +54966 w G5 1 +54976 w R18 1 +54991 b Q17 1 +54991 w Q3 1 +549c0 b Q5 1 +549c0 w Q7 C5 Q3 3 +549c1 b C19 1 +549d0 w O3 1 +54a20 w E5 1 +54a40 b H15 1 +54a67 b R17 1 +54a90 w N3 1 +54a97 w C4 1 +54aa0 w M16 1 +54aa7 w C6 1 +54b01 b P7 1 +54b60 w C3 1 +54b61 w F13 1 +54bb6 b S17 1 +54bd6 b O16 1 +54be1 b D16 1 +54c06 b B13 1 +54c81 w C15 1 +54d26 w C2 1 +54d30 b A15 1 +54d61 w O6 1 +54d91 w O3 1 +54da1 w S3 1 +54dc1 w C15 1 +54e10 w R9 1 +54e11 w R14 1 +54e17 b L3 N3 2 +54e71 w Q2 1 +54e86 w P16 1 +54ee7 b S15 1 +54ef6 b F16 1 +54f06 b D18 1 +54f51 w P2 1 +54f87 w O18 1 +54fe1 b Q3 1 +54fe1 w R3 O4 Q4 Q5 P6 P5 N5 N4 R6 R7 Q7 11 +55040 w F6 1 +550a7 b E3 1 +550b7 w B2 1 +550f6 b O17 1 +55106 b D4 1 +55107 b J3 1 +55126 w P3 Q12 2 +55140 w S5 1 +55156 w M2 1 +55166 w B16 1 +55176 b R18 1 +551a7 w N4 1 +55217 w Q7 1 +55226 w P4 1 +55231 b P7 1 +55247 w Q6 1 +55281 w C4 1 +552a6 b O16 1 +552b1 w C3 1 +552d0 w B17 1 +55310 b F18 1 +55311 w F2 1 +55337 b E17 1 +553a0 b P13 1 +553b1 w R18 P17 2 +553d6 b G16 1 +553f6 w F2 1 +553f7 b C3 1 +55406 b R6 1 +55410 w H15 1 +55417 w Q16 1 +55430 b P18 1 +55457 b D11 E3 2 +55461 b C4 C3 2 +55466 w F4 1 +55470 b L3 1 +55480 b Q4 1 +55481 b D3 1 +554d7 w P16 1 +554e0 w C3 1 +554e7 b B5 1 +55506 w A3 1 +55516 b R14 1 +55540 w R14 1 +55571 w P14 1 +55577 w Q3 1 +55666 b N17 1 +55667 b C17 D16 D15 E14 D14 5 +55667 w D17 1 +55677 w D3 1 +556a6 w P16 1 +55721 b L6 1 +55721 w R13 1 +55740 w M5 1 +55747 w F15 1 +55757 b D2 1 +55790 w E3 1 +557a7 w S16 R7 2 +557d0 w P15 1 +557d6 b C14 D13 2 +557d6 w C4 1 +557e6 b F18 1 +557e6 w F2 1 +557f0 w Q12 1 +55810 b M4 1 +55820 b C3 1 +55820 w R4 1 +55830 w Q6 1 +55840 w D4 1 +55841 w F17 1 +55851 b C5 D2 2 +558a7 b B13 1 +558b1 b P3 1 +558d0 b D18 1 +558e1 w T15 1 +55901 w M14 1 +55907 w Q3 1 +55921 b S6 M2 2 +55947 b S16 1 +55960 w R7 1 +55971 b S2 1 +55980 b D8 1 +55987 b G17 1 +55987 w O18 1 +55991 b Q14 1 +559b0 b C11 1 +559e6 w C16 1 +55a46 b C5 1 +55a56 b P4 1 +55a60 b P3 1 +55aa0 w N4 1 +55aa6 b F7 1 +55ab0 b R16 Q17 2 +55ab6 w C2 1 +55ac7 w F3 1 +55ad0 w L15 1 +55ae7 b G2 1 +55af0 b O14 1 +55b07 b E15 1 +55b20 b R15 1 +55b86 b F14 1 +55ba0 w Q11 1 +55ba1 w O13 1 +55bc6 w E18 1 +55c06 w S5 1 +55c87 b G18 1 +55c96 w J3 1 +55cd7 w P17 1 +55ce0 w O17 1 +55d07 w S15 1 +55d40 b C16 D16 2 +55d47 w A2 1 +55d77 b O3 1 +55da7 b R8 1 +55dd0 b Q15 1 +55df6 b E3 D4 2 +55df6 w D5 1 +55e11 w H16 1 +55e20 w C17 Q6 2 +55e40 b F4 1 +55e46 w G3 C9 2 +55e51 w Q17 1 +55ea0 w G4 1 +55ea6 w P18 1 +55ed0 b P13 1 +55ef0 w O2 1 +55ef6 w F4 1 +55f01 w D12 1 +55f06 w F2 1 +55f21 w R17 1 +55f31 b C16 1 +55f71 b C18 1 +55f77 b R9 1 +55fe1 b C4 C2 2 +55fe1 w C3 D4 E4 F5 F4 5 +55fe7 b Q17 1 +55ff1 w B5 1 +55ff6 w J17 1 +56007 b C13 1 +56017 b Q9 1 +56037 b C17 F16 D16 D15 E14 E15 G15 G16 C14 C13 D13 11 +56037 w D17 1 +56067 b D8 1 +56076 b Q5 1 +560b1 w B18 1 +560f0 b R17 P17 2 +56110 b G17 1 +56136 b E3 1 +56141 b D3 1 +56157 w C5 1 +561b7 b P2 1 +561c1 b D17 1 +561d0 w Q19 1 +561d1 w N4 1 +561f0 b P15 F15 2 +56206 b C2 1 +56207 b D16 1 +56230 w G15 1 +56267 b R5 S5 Q4 3 +56277 w C17 F16 2 +56290 b C19 1 +562a1 b L2 D18 2 +562e1 b H4 Q3 2 +56367 b P18 1 +56390 w P14 1 +563a0 b S12 1 +563e1 w O3 1 +563f1 b R7 1 +563f7 w R18 1 +56411 w P13 1 +56457 b C17 D16 E16 F15 F16 5 +56457 w C16 1 +56487 b F16 1 +564b1 b R8 1 +564c7 w B15 1 +56516 w P4 1 +56527 w O2 1 +56530 w R4 1 +56547 b Q6 1 +56581 b D14 1 +56581 w R13 1 +565a1 b Q14 1 +565c0 w D5 1 +565e1 b B2 1 +565e7 w R13 1 +56620 w C6 1 +56641 w F14 1 +566a0 b C13 1 +566c0 w B13 E13 2 +566c7 b Q17 D4 2 +566d1 w Q18 1 +566e1 b G5 1 +566e7 w O5 1 +566f6 w E5 1 +56701 b R17 1 +56701 w O7 D6 2 +56716 b C3 D4 P17 3 +56730 b P16 1 +56746 w D3 1 +56777 w S2 1 +56787 b B18 1 +567a0 w H13 1 +567a7 w E2 1 +567b0 w F6 1 +567e7 b D4 1 +56867 b H5 1 +56867 w P16 1 +56886 w H17 1 +568d1 w B5 1 +568e1 w S14 1 +56921 b S12 1 +56961 w G16 1 +56981 w G16 1 +569a0 b E16 1 +569b6 b S18 1 +56a16 w L18 1 +56a17 b Q11 1 +56a30 w N15 1 +56a66 b E6 1 +56a77 w P5 1 +56a91 w R2 1 +56a97 b H17 1 +56aa1 b B15 1 +56ac1 b S17 1 +56ac7 b O4 1 +56ad0 w C15 F14 2 +56ae7 b C16 1 +56ae7 w O11 1 +56b20 w N3 1 +56b27 w N7 1 +56b91 b B3 J3 2 +56bd0 w L17 1 +56be6 b P15 1 +56c16 b R11 1 +56c36 b H17 1 +56c40 b E2 1 +56c87 w H15 1 +56ce1 b N15 1 +56d07 w E16 1 +56d60 w R12 D4 2 +56d66 w R4 Q6 2 +56d71 w D6 1 +56d80 b P18 1 +56d81 w C13 1 +56dc0 w M14 1 +56dc6 b F3 1 +56dd7 b D15 1 +56e17 w C16 1 +56e36 b Q2 1 +56e40 w C14 1 +56e81 w O2 1 +56e87 w E12 1 +56ea0 b D17 1 +56ea0 w O6 1 +56f07 b H7 1 +56f10 b P16 1 +56f11 w R7 1 +56f31 w R16 1 +56f46 w O16 1 +56f70 b Q6 1 +56f76 b E3 1 +56f77 w G17 H18 2 +56fa1 w C12 1 +56fa7 b B5 1 +56fc6 b Q17 1 +56fe6 b C9 1 +57036 b D14 1 +57060 w F11 1 +57070 w P5 1 +57087 w Q2 1 +570b1 b D4 1 +570b6 b O5 1 +570d6 b C12 1 +570f0 b E3 1 +570f7 b O16 1 +57101 b P18 1 +57106 b F5 1 +57117 w Q13 1 +57150 w O16 1 +57156 b C5 C3 2 +57160 w Q17 1 +57170 w F16 G16 2 +571a6 b B15 1 +571e6 b B7 1 +571f1 w F17 1 +57210 b R14 1 +57230 b S6 1 +57287 w Q2 1 +572a0 w Q2 1 +572c1 w F14 1 +572d6 w G4 1 +572e7 b H4 1 +572f0 w D17 1 +57301 w B4 1 +57366 b G9 1 +57367 w G4 C5 2 +573f1 w H3 1 +573f7 b Q3 1 +574a1 b G15 1 +574d1 w O4 1 +574d6 b B13 1 +574e0 w F15 1 +574e1 w S19 1 +57516 w O18 1 +57521 b R13 1 +57527 w R12 1 +57531 w F2 1 +57537 b R18 1 +57546 w Q3 1 +57567 w E14 1 +575b7 w P5 1 +575e7 b D3 1 +57610 b O3 1 +57637 w B18 1 +57650 w E12 1 +57651 w F15 C17 2 +57656 w B3 1 +57666 b A2 1 +57677 b B9 1 +57686 b D17 1 +576d7 b E14 1 +576f1 w F16 1 +57730 w B16 1 +57746 b D18 1 +57751 w Q16 1 +57756 w D17 1 +577b0 w R12 1 +577f6 b R17 1 +57807 w Q8 1 +57826 b D4 1 +57837 b Q14 R13 2 +57846 b Q14 1 +57846 w S13 1 +57860 w N4 1 +57897 w N4 1 +578c0 b P2 1 +578e6 w H16 1 +578f0 b L5 1 +578f1 w R17 1 +57901 b F16 1 +57906 b Q5 1 +57906 w Q5 1 +57911 b G7 F18 2 +57936 b O15 Q12 2 +57937 w G17 1 +57941 w E3 1 +57960 w Q4 1 +57967 w D14 1 +57970 w D14 1 +57977 b N3 1 +579e7 w O2 1 +579f1 b P6 1 +57a17 b C16 1 +57a47 w M14 1 +57aa7 b O7 1 +57ad6 w Q12 1 +57b06 w P5 1 +57b20 w S5 1 +57bc7 b Q2 1 +57bd1 w B16 1 +57bd7 w F5 1 +57c37 b E17 1 +57c51 b F18 1 +57c70 b N4 1 +57cc0 b O5 1 +57cd0 b G4 1 +57ce6 w H4 1 +57d30 w S2 1 +57d47 b B17 Q3 P4 R3 4 +57d56 w R6 1 +57d77 w E15 1 +57d86 w R6 1 +57da0 b F3 1 +57dc1 w M18 1 +57dd6 w R16 1 +57e17 w D16 1 +57e40 b Q2 1 +57e57 w D4 1 +57e86 w C18 1 +57e91 b C17 1 +57e96 w Q17 1 +57ea1 b D12 1 +57ef1 b R3 1 +57f00 b R14 1 +57f01 b Q5 1 +57f66 w R5 1 +57f70 b B15 1 +57f80 w P12 1 +57f87 w F15 1 +57fd0 b D12 1 +57fd7 b O3 1 +58006 w P18 1 +58011 b E18 1 +58027 w B2 1 +58031 w R12 1 +58090 w P12 1 +58097 b F7 1 +580a1 b E6 1 +580d7 w H4 1 +58120 b P16 1 +58137 w N5 1 +58170 w Q6 F12 2 +58176 b G4 1 +58176 w B9 1 +58180 b H16 1 +58190 b G13 1 +581a7 w N18 1 +581c0 b B14 1 +581d0 b F15 1 +581d1 b E4 1 +581f7 w E15 1 +58201 b C13 1 +58206 w Q15 1 +58236 w D14 1 +58257 w D17 1 +58261 b G17 1 +58276 w B3 1 +582a0 w E16 E17 2 +582c6 w H1 1 +582e0 b C18 1 +58300 b S18 1 +58301 b N2 1 +58327 w D18 C17 2 +58340 w E5 1 +58377 b E16 1 +583a6 w E4 1 +583c1 w G6 1 +58406 w P17 1 +58426 w S3 1 +584a7 w S18 1 +584e7 w Q16 1 +584f6 b M18 1 +58501 w O17 1 +58527 w T17 1 +58551 w S8 1 +58587 w B17 1 +585c1 b B3 1 +58601 w R14 1 +58616 w P5 1 +58670 w L3 1 +586a1 b H4 1 +586d1 b Q4 1 +586d7 b Q11 1 +586f0 b S2 1 +586f0 w F16 F17 2 +58717 w R3 1 +58736 b C18 1 +58761 w R5 1 +58781 b G3 1 +58786 w E17 1 +58791 b S5 1 +58796 w Q17 R8 2 +587c1 b R7 1 +587c6 b M16 1 +58850 w B13 1 +58856 b F17 G16 2 +58876 w Q6 1 +58887 w C3 F5 C6 D6 4 +588a0 w Q11 1 +588a1 b F13 1 +588a1 w H16 1 +588b0 w N3 P5 2 +588c7 w Q4 1 +588d0 b E17 1 +588d6 b E16 D15 2 +588f1 b F15 1 +58911 w R13 1 +589b0 b D13 1 +589c6 b H6 1 +58a07 b B18 1 +58a10 w G16 1 +58a20 b Q6 G16 2 +58a66 w E7 1 +58a97 w E3 1 +58ab7 w M4 1 +58ad6 b E18 1 +58af7 w R17 1 +58b10 w D15 1 +58b36 w D6 1 +58ba1 b E14 1 +58bb7 w C2 1 +58c30 w G6 1 +58c57 w B16 E15 2 +58c67 w G3 1 +58ca6 w R13 1 +58cc6 w F4 1 +58ce0 b S5 1 +58cf0 w R5 1 +58d10 b P4 1 +58d10 w C3 1 +58d20 b R7 1 +58d60 w D11 1 +58d66 w E3 1 +58d76 b Q4 1 +58da1 b E13 1 +58df1 w G17 1 +58e16 w Q9 1 +58e37 b E18 1 +58e50 b S16 R15 2 +58e60 b E16 1 +58e60 w E4 1 +58e96 b G2 1 +58e97 w D9 1 +58ea6 w B14 1 +58eb0 b H13 F12 2 +58eb6 w N17 1 +58ec1 b P5 1 +58ef0 w F17 1 +58ef1 b P13 Q14 2 +58ef7 b R6 1 +58f07 b E6 F6 E3 F3 Q11 5 +58f07 w Q3 R3 2 +58f17 b J4 1 +58f30 b J7 1 +58f37 b F4 1 +58f87 w N17 1 +58fa6 w O17 R13 2 +58fb6 w C5 1 +58ff1 b B16 1 +58ff6 w E5 1 +59051 b R7 1 +590a6 b J3 1 +590f0 w B18 1 +59166 b D14 1 +59180 w C9 1 +59186 w O5 R6 2 +591d6 b P15 1 +591f6 b R5 1 +59201 b F7 1 +59210 w F14 1 +59211 w C19 1 +59251 b D17 1 +59257 b P17 1 +59260 w C5 1 +59280 b L6 1 +59290 w R18 1 +592a0 w Q13 1 +592b0 w C2 1 +592c7 b P3 Q4 2 +592d0 w B9 1 +592e0 w C3 1 +59320 w R3 1 +59337 b Q3 1 +59371 b D17 1 +59381 w J5 1 +593a0 b N3 P5 N4 3 +593a6 b R15 R18 2 +593d1 b R5 1 +593f1 w D7 1 +59411 w E15 F17 2 +59416 b P3 1 +59457 b L17 N17 2 +59466 w B17 1 +59471 w B4 1 +59481 w B5 1 +594a6 b E6 1 +594e1 b B5 1 +59511 b C4 1 +59530 w Q8 1 +59551 b O4 B16 2 +59566 w C2 1 +59590 w A4 1 +595b0 w B16 1 +595b7 b S2 1 +595d0 b N18 1 +59650 b C14 1 +59660 b O2 1 +59661 w D15 1 +59667 w R3 1 +59687 b P16 1 +59690 w D12 1 +596a6 w B4 1 +596c6 b C5 1 +596e6 b N4 1 +596f7 w Q2 1 +59727 w O4 N5 2 +59761 w D2 1 +59780 w B3 L16 2 +59790 w S14 1 +597d0 b Q15 Q14 N17 M17 L17 N16 M16 L16 8 +597d0 w Q15 L17 R14 R13 R12 Q13 Q14 7 +597d7 b B12 1 +59810 w F18 1 +59811 w R16 1 +59836 b G16 G17 C17 3 +59871 w C7 1 +598b1 b D13 E17 2 +598d7 w P17 1 +59920 w C7 1 +59946 w P2 1 +59976 b P3 1 +59981 w G3 1 +599b7 b D17 1 +599d1 w E2 1 +599e1 b C2 1 +599f6 b S7 1 +59a21 w F2 1 +59a30 w R14 1 +59a56 w G17 1 +59a61 w F3 1 +59a86 w Q6 1 +59a87 w P4 1 +59ac7 w Q3 N16 2 +59ad6 w Q7 1 +59b01 w P18 1 +59b07 b Q8 1 +59b21 w B4 1 +59b31 w Q11 1 +59b40 w D8 1 +59b41 b P6 1 +59b57 b Q6 1 +59be0 b S5 R3 O4 O3 4 +59c26 w C3 D4 2 +59c56 w C16 1 +59c60 w L5 1 +59c67 b E1 1 +59c71 w F4 1 +59c97 w P3 1 +59cb7 b C3 1 +59cd0 b R2 Q2 2 +59cd7 w O2 1 +59cf1 w B19 1 +59cf7 w O2 1 +59d00 w N13 1 +59d06 b D14 D3 2 +59d10 b F8 1 +59d20 b E3 1 +59d30 w P14 1 +59d46 w C15 1 +59d71 w R8 1 +59db0 w S5 Q5 2 +59db1 b Q7 1 +59de0 b C6 1 +59e16 b D14 1 +59e31 w R12 1 +59e51 b R6 1 +59e56 w R14 1 +59e80 w Q7 1 +59ed6 b E6 1 +59ee7 w D7 1 +59f00 w Q17 1 +59f06 b P4 Q5 2 +59f27 b B3 1 +59f56 w H16 F17 2 +59f57 w J4 1 +59f60 b D19 1 +59f66 b Q17 1 +59f91 b Q18 1 +59fa6 w G16 1 +59fe0 w E17 1 +5a016 w C16 1 +5a027 w Q18 1 +5a040 w R18 1 +5a051 b G1 1 +5a091 b F14 G13 2 +5a0c1 w D16 1 +5a0d0 w S19 1 +5a101 b B3 1 +5a121 b Q16 1 +5a131 w R5 1 +5a141 b D5 D6 E4 C5 4 +5a146 b N4 1 +5a150 w O5 1 +5a171 b H18 1 +5a176 b L3 R3 2 +5a1c7 b C2 1 +5a1e0 w P19 1 +5a220 b R3 1 +5a246 w C5 C2 P4 Q5 4 +5a290 b B13 1 +5a291 b R4 C4 2 +5a2b6 b O2 1 +5a310 b C4 D3 2 +5a326 b R12 1 +5a361 b S4 1 +5a367 b H15 1 +5a3a0 b B14 1 +5a3a0 w D14 1 +5a3f1 w R18 1 +5a410 b R5 1 +5a416 w S3 1 +5a427 b Q5 O5 2 +5a427 w H15 1 +5a430 w R9 1 +5a440 w F14 P14 2 +5a451 b R7 1 +5a466 w G14 1 +5a496 b F2 1 +5a4a0 b C17 1 +5a4d0 b S2 1 +5a4e0 b Q14 1 +5a511 w Q7 1 +5a557 w C3 1 +5a571 b D5 1 +5a576 w D15 E16 2 +5a591 b E2 1 +5a5b1 b Q3 1 +5a5b6 w P15 1 +5a5c0 w F18 1 +5a5d7 w C17 1 +5a5f1 w R3 R4 L3 M3 P2 Q3 6 +5a620 b G3 1 +5a636 b D3 1 +5a640 b H4 1 +5a647 b H5 1 +5a667 w D17 1 +5a691 b P8 1 +5a6a1 w O18 1 +5a6d7 b M5 1 +5a707 w D17 1 +5a740 w P8 1 +5a746 b B3 J17 2 +5a757 w B15 1 +5a766 b P15 1 +5a767 b Q13 Q16 2 +5a781 b P15 1 +5a7b0 b C4 1 +5a817 b P18 1 +5a861 w O16 1 +5a870 w D13 1 +5a881 w G2 1 +5a8a7 b C3 1 +5a8b0 b F6 1 +5a8b7 b E5 D7 2 +5a8d1 b D1 1 +5a920 b M17 1 +5a931 b C18 1 +5a941 b D18 1 +5a960 b D6 E4 2 +5a970 b N4 1 +5a970 w R17 1 +5a977 w H6 1 +5a980 w E18 1 +5a9c6 w B5 1 +5a9e1 b E2 1 +5aa06 b E2 1 +5aa11 w R6 1 +5aa26 w E5 1 +5aa27 b B6 1 +5aa51 b R13 1 +5aa70 w C8 1 +5aac1 w D4 F4 2 +5aae1 w C13 C18 2 +5ab06 w R5 1 +5ab07 w T16 1 +5ab17 b H3 1 +5ab66 b D14 F7 2 +5ab77 b R13 1 +5ab91 b Q3 1 +5ab91 w R3 Q4 Q5 P6 Q6 5 +5abc0 w D14 1 +5ac01 b F18 S18 2 +5ac16 w C6 1 +5ac27 w D17 1 +5ac96 w C18 1 +5aca6 b F4 1 +5acf1 w O17 1 +5acf6 b F4 1 +5ad01 b M15 1 +5ad07 w D11 1 +5ad47 b R18 1 +5ada0 w S17 1 +5ada1 b D7 1 +5ada6 w E16 1 +5adb1 b N13 1 +5adb6 w C15 1 +5adb7 b E14 1 +5adc1 b E3 1 +5add7 w R18 1 +5ade0 w D6 E3 F5 H3 4 +5ade1 b M4 Q8 2 +5adf1 w N2 1 +5ae57 b B14 1 +5ae57 w F15 1 +5ae81 b Q9 1 +5ae86 b A4 1 +5ae90 w D15 1 +5aea7 b F4 1 +5aee6 b N3 1 +5aef6 b F18 1 +5af00 w F5 1 +5af30 w Q5 1 +5af31 w B14 1 +5af51 w H3 1 +5af57 w C8 1 +5af90 w D4 1 +5afc6 w C13 1 +5afd6 w C5 1 +5aff1 b D4 1 +5b001 w R3 1 +5b006 b G18 1 +5b006 w G3 1 +5b026 b N5 1 +5b046 b N3 1 +5b067 b E14 1 +5b091 b D4 1 +5b096 b E4 D6 2 +5b0a7 w R18 1 +5b0b6 w C8 1 +5b0d1 w O3 1 +5b0f0 b R5 1 +5b0f1 w F3 1 +5b0f6 b E4 1 +5b106 w R6 1 +5b107 b P16 R13 2 +5b126 w E18 1 +5b127 w C16 1 +5b171 b E7 1 +5b1b1 b S13 1 +5b217 w C15 1 +5b226 w Q7 1 +5b247 w C18 1 +5b277 w F14 1 +5b286 w F17 1 +5b290 b C17 1 +5b297 b S15 1 +5b2a7 w N15 1 +5b2b6 b B4 1 +5b2d0 w D14 D13 2 +5b2d7 w E3 1 +5b2f0 b N2 1 +5b2f1 w F18 1 +5b307 b O4 1 +5b347 w P2 1 +5b356 b F18 1 +5b380 b Q14 1 +5b386 b Q6 D2 2 +5b387 b P16 1 +5b3a0 w D15 1 +5b3c1 w S3 1 +5b3e6 w O4 1 +5b3e7 b D6 E5 2 +5b3e7 w C6 1 +5b3f6 b P3 O5 2 +5b3f7 b S3 1 +5b3f7 w C15 1 +5b416 w H17 1 +5b436 b H18 1 +5b451 b P5 1 +5b456 w Q6 1 +5b477 b P3 1 +5b4a6 w O17 1 +5b4e6 b D15 S15 2 +5b4e6 w D15 1 +5b4f1 w D3 1 +5b4f6 b C18 1 +5b506 w G15 1 +5b521 w D4 1 +5b541 b D6 1 +5b590 b C14 1 +5b5b7 w Q16 1 +5b5d0 b F15 1 +5b5f1 b E16 1 +5b611 b R19 1 +5b616 w O14 1 +5b667 b R4 1 +5b690 b D11 1 +5b6a1 w D17 1 +5b6b7 b O15 R17 2 +5b6f1 w R12 1 +5b701 b C5 B6 B5 R5 4 +5b737 b F13 1 +5b777 w D3 1 +5b7c0 w P2 1 +5b7d0 w D9 1 +5b7d7 b C2 1 +5b7f0 w Q7 S6 2 +5b7f1 b Q4 1 +5b801 w N14 1 +5b807 w P2 1 +5b816 w G16 D19 2 +5b826 w Q7 1 +5b827 b O5 1 +5b851 b N17 1 +5b8a6 b D17 1 +5b8d6 b G8 1 +5b8e7 w Q17 1 +5b8f1 b N17 1 +5b960 b P14 1 +5b971 w O5 1 +5b990 w J17 1 +5b9e0 b C15 1 +5b9f0 b G16 1 +5ba36 w B16 1 +5ba41 w P14 1 +5ba46 b P17 R17 2 +5ba66 b F6 1 +5ba70 w B3 1 +5ba86 b R13 P18 F3 3 +5baa0 w F4 G4 2 +5bab1 w C6 1 +5bad7 w C13 1 +5bae7 b B6 1 +5bb21 b S3 1 +5bb61 w R3 1 +5bbd7 w S5 1 +5bbe0 b F12 1 +5bbf1 b Q6 1 +5bbf1 w H18 1 +5bbf7 w G5 1 +5bc00 b D4 1 +5bc11 w G4 1 +5bc21 b C2 1 +5bc21 w Q16 1 +5bc30 b R18 1 +5bc41 w O15 1 +5bc51 b G16 1 +5bc61 w B5 1 +5bc66 w O5 1 +5bc67 b R6 1 +5bc97 w A4 1 +5bca1 b Q14 1 +5bcc1 b C12 1 +5bd06 b P15 1 +5bd10 b B3 1 +5bd11 w C8 1 +5bd67 b R11 1 +5bd70 b S15 1 +5bd71 b S15 1 +5bda0 w D15 1 +5bdb0 w H16 1 +5bdb1 b R3 1 +5bdb7 w P16 1 +5bdc1 w O4 N5 2 +5be00 w R8 1 +5be17 b C7 1 +5be37 w C7 B8 2 +5be41 b Q4 1 +5be76 w C8 1 +5be91 w D4 1 +5bed7 b S5 1 +5bee6 b D4 1 +5bf01 b R5 1 +5bf36 w C17 1 +5bf90 b F15 D14 2 +5bfb0 b E14 1 +5bfc0 w D16 1 +5c001 w C8 1 +5c036 b O6 1 +5c036 w D18 1 +5c051 w O17 1 +5c071 b B14 1 +5c076 b F2 1 +5c077 b O2 1 +5c0a7 w R3 1 +5c0c1 b E14 D13 C15 3 +5c0c7 w P14 1 +5c147 w N18 1 +5c166 b P12 1 +5c187 b P4 O4 2 +5c1b0 w D5 1 +5c1d7 b F5 1 +5c1e7 b B3 1 +5c206 w C4 1 +5c210 b E16 1 +5c217 b C17 1 +5c221 b D13 D16 2 +5c226 w D14 1 +5c257 w Q7 E15 2 +5c261 w D2 1 +5c2c6 b D12 1 +5c321 b Q2 1 +5c336 w G4 1 +5c351 w F17 1 +5c380 w D5 1 +5c3a6 b B3 1 +5c3b0 w P15 1 +5c3b1 w P6 1 +5c3b7 w O16 1 +5c3f0 b B14 1 +5c400 w C6 1 +5c406 w R6 1 +5c410 b S3 1 +5c437 b C3 1 +5c456 w N14 1 +5c496 w E15 1 +5c4a1 b H16 1 +5c4b1 w B7 1 +5c4b6 b A17 1 +5c4c0 w N6 1 +5c4e6 b F4 1 +5c536 b Q5 1 +5c556 w P14 1 +5c560 w Q3 Q4 2 +5c567 b R19 1 +5c590 w P5 1 +5c596 b F5 1 +5c5b0 w M3 1 +5c5d6 b D15 1 +5c5e0 w S17 1 +5c616 b S18 1 +5c631 b Q2 1 +5c641 w O8 J16 2 +5c660 b D11 1 +5c6a0 b B5 1 +5c6a6 w H18 1 +5c6b0 b O4 1 +5c6c7 b P18 1 +5c6d1 b R13 1 +5c6e7 b O3 1 +5c6f1 b D3 1 +5c720 b P17 1 +5c720 w G6 1 +5c751 b M5 1 +5c766 b D6 1 +5c786 w F16 1 +5c7b6 w N16 1 +5c7f7 b R5 1 +5c830 w O15 1 +5c851 b Q2 1 +5c866 b F14 1 +5c870 w C3 1 +5c876 b B2 1 +5c887 b O15 1 +5c891 b Q17 1 +5c8b1 b D18 1 +5c8b1 w B4 1 +5c907 b D18 1 +5c910 b Q7 1 +5c911 b N15 1 +5c921 b E2 1 +5c946 w P2 1 +5c960 b N16 1 +5c9a0 b O6 1 +5c9a1 b C18 1 +5c9b7 b N2 1 +5ca21 b D3 1 +5ca27 w C14 1 +5ca30 b E6 1 +5ca66 w Q6 1 +5ca91 b C4 1 +5caa0 w H4 1 +5caa6 b G4 1 +5cab1 b O18 1 +5cb27 w E2 1 +5cb37 b S2 1 +5cb60 b R15 1 +5cb66 w D17 1 +5cb70 w M6 1 +5cb90 b B7 1 +5cb91 b O3 1 +5cb96 w C3 1 +5cbd1 b B17 1 +5cbe7 b R12 1 +5cbf0 b E15 1 +5cc07 w B14 1 +5cc10 b Q15 1 +5cc21 b Q4 1 +5cc56 b D3 1 +5cc70 w H4 1 +5cc71 w S3 1 +5cc96 b S3 1 +5cca0 w O15 1 +5cca7 w R16 1 +5cce7 w R3 1 +5cd10 b F5 1 +5cd11 w Q18 1 +5cd41 b R3 1 +5cd57 b E17 1 +5cd77 b N3 1 +5cd90 b S4 1 +5cdb6 b E14 1 +5cdc6 b F5 1 +5cdf0 b B15 1 +5cdf7 b R11 1 +5ce36 b F16 1 +5ce66 w P17 1 +5ce87 b O16 1 +5ce97 w D2 1 +5cef0 b F5 1 +5cf11 b Q13 1 +5cf20 b P2 1 +5cf26 w N4 1 +5cf57 b Q16 1 +5cf57 w S12 1 +5cf60 w S3 1 +5cf61 b O15 1 +5cf70 b R2 1 +5cf71 b B3 1 +5cfa0 w P17 1 +5cfa6 b S14 1 +5cfd1 w R17 1 +5cfe0 b F8 1 +5cfe7 b R7 1 +5d010 b J16 1 +5d016 b P5 1 +5d047 w B6 1 +5d067 b P6 O6 P3 O3 4 +5d097 b R4 1 +5d0c6 w B17 1 +5d0d1 w J17 1 +5d0d6 b D3 1 +5d0f1 w Q6 1 +5d101 w B5 1 +5d106 w L18 1 +5d110 b G13 1 +5d130 b O15 1 +5d186 w M18 1 +5d197 b C17 1 +5d1e7 w E3 1 +5d1f7 w B15 1 +5d217 b E17 1 +5d227 w L16 1 +5d261 w G14 1 +5d271 b D18 1 +5d280 w O3 1 +5d2b7 b F13 1 +5d2b7 w D18 1 +5d2f1 b C3 1 +5d350 w E4 1 +5d370 w M17 1 +5d371 w D17 E16 C17 3 +5d377 w B4 N3 2 +5d3c7 b O3 1 +5d3c7 w P16 1 +5d3d7 w G15 F16 2 +5d407 b G16 1 +5d407 w R13 1 +5d411 w E6 1 +5d471 w F7 1 +5d4a7 w D7 1 +5d4b7 w P3 1 +5d507 b M16 1 +5d567 b O18 1 +5d567 w C18 1 +5d597 b F5 1 +5d5b6 w B6 1 +5d5d0 w S18 1 +5d5d1 b Q17 R17 2 +5d5e7 b M15 1 +5d600 w E16 1 +5d601 w M17 1 +5d646 b O4 1 +5d687 w Q18 1 +5d6a1 b E13 D14 2 +5d6a7 b C14 B14 2 +5d6b6 w H17 1 +5d6c1 w G3 1 +5d6e6 w C17 1 +5d706 b C15 1 +5d711 w C8 1 +5d721 w P16 R13 2 +5d767 b D2 1 +5d781 b R18 1 +5d7a0 b S15 Q15 2 +5d7a7 w Q14 1 +5d836 b S5 1 +5d860 w P15 1 +5d871 b Q2 1 +5d886 w E6 F3 2 +5d8c7 b S3 1 +5d8e0 w R17 Q13 2 +5d940 b R2 1 +5d990 w B14 1 +5d9c7 b Q16 1 +5dac0 w C14 1 +5dae1 b E15 1 +5db11 w N18 1 +5dba1 b E4 1 +5dbb7 w S17 1 +5dbc0 w P18 1 +5dbc1 b S12 1 +5dbe7 w M16 1 +5dbf7 b B3 1 +5dc16 w R6 1 +5dc31 w F4 1 +5dc36 w M1 1 +5dc40 w S15 1 +5dc41 b E15 1 +5dc50 b Q2 1 +5dc50 w C4 1 +5dc51 b N3 1 +5dc60 b P15 1 +5dc67 w F4 1 +5dca0 b C15 1 +5dca1 w G17 1 +5dcb1 b D2 1 +5dcb7 w R18 1 +5dce7 b R16 1 +5dd10 w Q8 1 +5dd21 w O3 1 +5dd41 w Q8 1 +5dd81 w C2 1 +5dd96 b M4 1 +5dd97 b F16 1 +5ddd1 b O5 1 +5ddf0 w P8 O8 2 +5de06 b E6 1 +5de31 b R3 1 +5de81 b Q17 Q18 2 +5dea6 w P7 1 +5dee1 b B12 1 +5def1 w D12 1 +5df31 w P6 1 +5df77 b C3 1 +5e030 w R3 P3 2 +5e056 b Q7 1 +5e081 w R8 1 +5e0a6 b E15 1 +5e0a7 w M18 1 +5e0c1 b P14 1 +5e0d6 b F18 1 +5e186 w P18 1 +5e187 b S4 O3 2 +5e1f6 w B17 1 +5e241 b B3 1 +5e246 w D6 1 +5e260 w R5 1 +5e270 b E13 1 +5e2d1 b H4 1 +5e2e1 b R3 1 +5e306 w R11 D3 E2 3 +5e311 b C17 1 +5e316 w P15 D17 2 +5e337 b R5 1 +5e341 w C15 1 +5e370 b N16 1 +5e386 w G6 1 +5e3d6 b C5 1 +5e3f1 b C7 1 +5e427 w P17 1 +5e446 b D2 1 +5e476 w B17 1 +5e477 b A15 1 +5e486 w D3 1 +5e4a0 b M3 1 +5e4c0 b C16 1 +5e4c6 b S14 1 +5e4d1 w S14 1 +5e4e1 w T17 1 +5e4e7 w Q14 1 +5e500 b E16 1 +5e516 b P6 1 +5e517 w G7 1 +5e540 b F6 1 +5e566 b C18 1 +5e577 w G4 1 +5e5a1 b D17 1 +5e5c0 w Q7 1 +5e5c6 b F14 1 +5e5d0 b D6 1 +5e5d1 b Q12 1 +5e5d6 w N3 1 +5e647 b R8 1 +5e651 b P7 1 +5e651 w O3 1 +5e666 b N16 1 +5e667 b H3 1 +5e671 b C16 1 +5e6a6 w F14 1 +5e6b0 w N6 1 +5e6d6 b N16 1 +5e6e0 w L6 1 +5e6f0 w P3 1 +5e6f7 b S15 1 +5e736 b C7 1 +5e756 b M4 O3 2 +5e781 w C13 H5 2 +5e7b6 w S18 1 +5e806 w C15 1 +5e810 b R5 1 +5e827 w S9 1 +5e830 w E14 1 +5e847 b P13 1 +5e850 b G4 1 +5e8b1 b C17 R3 2 +5e8e0 b S4 1 +5e8e6 w F4 1 +5e946 w R14 1 +5e957 b B4 1 +5e967 w G3 1 +5e970 b F17 1 +5e976 w R3 1 +5e9a0 w S4 1 +5e9a6 w C6 1 +5e9d6 b O4 1 +5e9f6 w F15 1 +5ea30 w N18 1 +5ea60 b L4 1 +5ea67 w C13 1 +5ea80 b R16 1 +5eab1 b R16 1 +5ead6 b E15 1 +5eaf0 b R3 1 +5eb06 b P15 1 +5eb10 w D15 1 +5eb36 w P3 1 +5eb47 w C3 1 +5eb56 w G4 1 +5eb67 w O4 1 +5eb80 w F15 1 +5eb81 w Q9 1 +5eb91 b R13 1 +5eba7 w B16 1 +5ebc0 b R6 1 +5ebc0 w F17 E14 2 +5ebc1 b D3 1 +5ebc7 b G6 1 +5ebe7 w C5 1 +5ebf7 b C4 1 +5ec06 b R4 1 +5ec37 w C18 1 +5ec50 b D6 1 +5ec60 w E7 1 +5ec66 w O4 1 +5eca0 w S7 1 +5ed10 w O3 1 +5ed16 b G15 1 +5ed17 b P2 1 +5ed51 w B17 1 +5ed76 b S16 1 +5ede0 b P15 1 +5edf1 b Q17 1 +5ee20 w P13 1 +5ee21 w R13 1 +5ee27 b Q2 1 +5ee76 b J18 1 +5ee97 w E2 1 +5eeb1 b B2 1 +5eeb7 w F4 1 +5eec0 w E15 1 +5eec6 b O18 1 +5eed0 b E4 1 +5eef7 b C19 1 +5ef17 w C17 F15 C14 D14 4 +5ef21 b F4 1 +5ef37 b R6 1 +5ef41 w D4 1 +5ef80 b E5 1 +5ef90 w D18 E17 2 +5efa1 w C16 1 +5efb7 b R16 1 +5efc7 b O2 1 +5eff6 w D6 1 +5f016 b B13 1 +5f017 b J4 1 +5f031 w D8 1 +5f050 b O14 1 +5f0a7 w R5 1 +5f127 w G6 C5 2 +5f1b0 w H15 1 +5f1b1 b Q9 1 +5f1d7 w C16 1 +5f1e1 w Q13 1 +5f1f1 w B17 1 +5f230 b B3 1 +5f251 w E17 1 +5f290 b N4 Q5 2 +5f2a1 b Q18 1 +5f2c6 w Q13 1 +5f2d0 w D8 1 +5f306 b M16 1 +5f310 b R8 1 +5f316 w Q14 1 +5f3a1 b C17 1 +5f3a1 w J3 1 +5f3f6 b Q7 1 +5f3f6 w R17 1 +5f450 w H3 1 +5f451 b Q2 1 +5f4c0 w B6 1 +5f531 b F16 1 +5f567 w Q18 1 +5f5b0 w P19 1 +5f5b6 w B3 1 +5f5c7 w N16 M17 L17 3 +5f600 b C6 1 +5f606 b N14 1 +5f637 b J16 1 +5f646 w E5 1 +5f660 b E16 1 +5f677 w S5 1 +5f686 b O3 1 +5f6a0 w D14 1 +5f6c6 w C5 1 +5f6e1 w P16 1 +5f707 b P3 C2 2 +5f710 b G16 1 +5f711 b S16 R17 R16 M17 4 +5f726 w G2 1 +5f761 b F5 1 +5f776 b B13 1 +5f7a0 b L3 1 +5f7a0 w G5 1 +5f7c6 b F13 1 +5f7d6 w O5 1 +5f7e1 w O18 1 +5f7f1 w S13 1 +5f800 b C17 1 +5f840 b D18 1 +5f847 w C17 1 +5f877 w Q4 1 +5f8d6 w P4 1 +5f940 b P2 1 +5f940 w R19 1 +5f956 w S4 1 +5f967 w S4 1 +5f990 w F4 1 +5f997 b D13 1 +5f9f7 b R8 1 +5fa36 w F4 1 +5fa51 w D4 1 +5fa77 w B18 1 +5fa91 b O18 1 +5fa91 w P14 1 +5faa0 b D3 D4 2 +5fab0 b R6 1 +5fab1 w B16 1 +5fac6 b F6 1 +5fb57 w B2 1 +5fb91 b B17 1 +5fbe0 b P3 1 +5fc10 b F13 1 +5fc21 b R15 1 +5fc36 w C18 1 +5fc40 b B16 1 +5fc41 b Q15 1 +5fc87 b F4 1 +5fc87 w D18 1 +5fc96 b C2 1 +5fcc7 b A5 1 +5fd31 b D7 1 +5fd56 w E18 1 +5fd80 w E18 1 +5fd91 w D7 1 +5fdb0 b E2 1 +5fe01 w Q3 1 +5fe37 w C16 1 +5fe40 b C2 1 +5fe97 w R5 1 +5fee7 w C7 1 +5ff00 b S17 1 +5ff00 w O15 1 +5ff40 w R12 1 +5ff51 w L3 1 +5ff96 w B14 1 +5ffd1 b G16 1 +60006 w Q6 1 +60016 w H3 1 +60026 w F13 1 +60081 b Q16 1 +600a0 b E5 P12 2 +600a1 b F18 1 +600a6 w Q17 1 +600b1 w D17 1 +600f0 w E4 1 +600f1 b R17 1 +600f1 w E1 1 +60111 b B6 1 +60151 b L16 1 +60156 b S8 1 +60160 w C5 1 +60170 w Q5 1 +60197 b Q4 1 +601a1 w R15 1 +601a7 w O5 1 +601f1 w J17 1 +60200 b M15 1 +60260 b C16 1 +60270 b P14 1 +60296 b O16 1 +602b0 w N17 1 +602d6 b B12 1 +602f0 b S16 1 +60300 b N4 1 +60320 w H4 1 +60321 w N3 1 +60351 b D4 1 +60386 b B6 1 +603a0 w M4 1 +603a6 b P14 1 +603c7 b O12 1 +603d0 b E16 1 +603f7 w E18 1 +60406 w D5 1 +60446 w P18 1 +60450 w F14 1 +604a1 b N3 M2 2 +604b1 b C6 1 +604f6 b G16 1 +60500 w J3 1 +60507 b T17 1 +60526 w R16 1 +60536 b H17 1 +60547 w F5 1 +60580 b R2 1 +605a1 b N2 1 +605b7 b G16 1 +605d6 w B19 1 +605e1 b Q18 S16 2 +60600 w O17 1 +60627 b N12 1 +60630 b P4 1 +60636 b B11 1 +60676 b Q5 P4 2 +60687 b O2 1 +60690 b B15 E1 2 +60696 b S17 1 +606b0 b E2 1 +606e0 b D7 1 +606f6 w C14 D15 2 +606f7 w P6 1 +60706 b R2 1 +60746 w F15 G16 E14 3 +60780 b O17 1 +60796 b L4 1 +60797 b Q4 G4 2 +607a6 w S5 1 +607f0 b S14 1 +60836 b S12 1 +60840 w Q18 1 +60890 b Q7 1 +608b1 w E14 1 +608d0 w M15 1 +60911 b P16 O15 2 +60960 b E4 1 +60961 w D11 1 +60967 w Q2 1 +60977 w B14 1 +60996 b D18 1 +60997 w S5 1 +609b6 w Q6 1 +609e6 b D6 1 +609f1 b R17 1 +60a26 w D2 1 +60a46 b Q2 1 +60a46 w F16 1 +60a61 b P15 1 +60a81 b P16 1 +60a91 b N3 1 +60a91 w Q3 1 +60a96 b S4 1 +60ae0 b Q8 1 +60b01 b Q6 1 +60b06 w D5 1 +60b37 w D2 B4 2 +60b47 b S6 1 +60b70 w S15 R16 2 +60b81 w G3 1 +60bb0 b S16 O13 P2 3 +60bb0 w E13 1 +60bd1 b D14 1 +60bd6 b R17 1 +60bd6 w D14 1 +60be1 b R17 1 +60bf6 b Q2 1 +60c00 b C19 1 +60c31 b O4 1 +60c31 w M17 1 +60c37 b M15 1 +60c46 b R17 1 +60c47 w E17 1 +60c50 b A18 1 +60c50 w D5 1 +60c67 b G6 1 +60c71 w S17 1 +60c80 w R9 1 +60cc7 w S5 1 +60cd0 b R3 1 +60cf6 b E15 D16 2 +60d21 b N4 Q4 2 +60d36 w R5 1 +60d61 w B1 1 +60d80 b O15 Q14 2 +60dc1 b Q2 1 +60dc1 w S5 1 +60de0 w P18 1 +60e07 b B13 1 +60e31 w F6 1 +60e77 b C7 1 +60e77 w C18 1 +60e97 b R4 1 +60ea0 b G15 1 +60ed1 b C13 1 +60f16 w F2 1 +60f31 w P6 R3 2 +60f37 b E14 1 +60f51 b P13 1 +60f57 b E3 1 +60f77 w R16 1 +60fa7 b C4 1 +60fe0 b F3 1 +60ff1 b Q15 P16 2 +61010 b B5 1 +61041 b R17 1 +61061 b D6 1 +61076 w B16 1 +61080 b R17 1 +61097 w M18 1 +610a6 b D13 1 +610b1 b C12 1 +610c7 b D4 1 +610d0 b B4 1 +610d7 b E6 1 +610e0 b D6 1 +610e1 w L18 1 +610f0 w C15 1 +61101 w F16 1 +61136 w F3 1 +61176 b H19 1 +611c6 b P4 1 +611d7 w B4 1 +611e1 b O4 1 +611f7 w R13 1 +61206 b G4 1 +61226 w N16 1 +61257 w S12 1 +61260 w C13 1 +61286 b M17 1 +61297 w B8 1 +612b0 b Q15 1 +612d0 b F4 1 +612d0 w C15 1 +612e6 b F16 E16 2 +61317 w B15 1 +61337 b C17 1 +613c7 w P17 1 +613d7 b Q15 1 +613e0 w F15 1 +61410 b Q4 1 +61436 w B17 1 +61471 b E5 1 +61476 b N6 1 +61490 w H17 O3 2 +61497 w F4 1 +614c0 w P17 1 +61501 b O5 1 +61511 b D14 E13 2 +61520 b D16 1 +61520 w F5 1 +61521 w R17 1 +61556 w C3 1 +61567 w B5 1 +61571 b G16 C3 D4 D5 E6 D6 D7 7 +61571 w D3 1 +61581 w C5 1 +615a7 w E2 B6 2 +615b6 w P16 1 +615e0 b E5 1 +61600 b D2 1 +61600 w E17 1 +61601 b G5 1 +61610 b S5 1 +61616 b E2 1 +61621 b D15 1 +61677 w F6 1 +616b1 w G4 F2 2 +616b6 b D14 1 +616d0 w R13 P15 Q13 3 +616e1 b G14 1 +61727 b E15 1 +61746 w Q15 1 +61751 w G4 1 +61770 b R16 1 +617e1 b R2 1 +617e1 w F3 1 +617e7 w O4 1 +61801 w J17 1 +61807 b E11 1 +61816 w R16 1 +61857 b D3 D4 2 +61890 b B5 1 +61890 w E16 1 +61896 w S5 1 +618a0 b R5 1 +618b7 b D14 1 +618c0 b S18 1 +618c0 w C3 1 +61987 w E18 1 +61997 b L4 1 +61997 w N17 M18 2 +619a7 w D3 1 +619b1 b P5 1 +619d1 b C3 1 +61a31 b D14 1 +61a31 w B17 1 +61a50 b D4 1 +61a56 b E13 1 +61a60 b C6 1 +61a81 b C16 1 +61a91 b D17 1 +61ac1 b N17 1 +61ac6 w Q14 1 +61ad6 w O3 1 +61ae0 w F7 1 +61af0 b P4 C3 2 +61b01 b O16 1 +61b10 b Q6 1 +61b46 w R18 1 +61b50 b R15 1 +61b70 b P7 1 +61b80 b R14 1 +61b80 w Q6 1 +61b91 w E13 1 +61c06 b P18 1 +61c16 w C7 1 +61c26 w C13 1 +61c30 b N16 E4 2 +61cd1 b M4 1 +61cf6 w D17 1 +61d17 w N17 1 +61d21 w F3 1 +61d47 w C7 1 +61d86 w R14 N17 S17 3 +61d90 b C17 1 +61da7 b J17 G17 2 +61df7 w C15 1 +61e07 w C3 1 +61e11 w S4 1 +61eb1 w G4 1 +61ed6 w B7 1 +61ee0 w Q14 P16 2 +61ef1 b R17 P14 O17 O16 4 +61f06 b O3 1 +61f30 w J3 1 +61f37 w E15 1 +61f46 w R14 1 +61f67 b R11 1 +61f71 w H17 1 +61f90 b C3 1 +61fb0 b C2 1 +61fb7 b B5 1 +61fd1 w B14 1 +61fd6 b Q2 1 +61ff0 b O5 1 +62006 w M17 1 +62031 w B11 1 +62070 w Q13 1 +620a6 w P5 1 +620a7 b G5 1 +62111 w R3 1 +62126 b B18 1 +62127 b E4 1 +62137 w E13 1 +62146 w O2 1 +62176 b O16 N16 2 +62187 b R11 1 +62196 w F15 1 +621a1 b R17 1 +621f0 b N5 1 +62200 w C9 1 +62260 b E18 1 +62267 b S7 1 +62276 w S3 1 +62280 w E3 1 +622a6 b E17 B17 2 +622b7 w G15 1 +622f6 b P4 1 +62301 b H4 1 +62310 w M7 O8 2 +62316 w Q12 1 +62326 w Q6 1 +62381 w R18 1 +623a1 w N5 N18 2 +623c0 b P14 1 +623d0 w D15 1 +623e0 w Q4 1 +623e1 w E5 1 +623f0 w C11 1 +62406 b D14 1 +62407 w B17 S6 2 +62411 w D14 1 +62426 w C17 1 +62466 b R3 1 +624f1 w F4 1 +624f6 b H3 1 +62507 b Q14 1 +62510 b N16 1 +62527 w Q13 Q16 2 +62536 w S2 1 +62550 w Q13 1 +62580 b D6 1 +62591 b S7 1 +625a7 w O16 1 +625e7 b A6 1 +62646 b P17 1 +62657 w S12 1 +62666 w D14 1 +62676 b L18 1 +62681 b G4 1 +62687 b N16 1 +626a7 b S17 1 +626d0 b Q7 1 +626d7 b P15 1 +626e7 w C16 1 +62717 b E5 1 +62737 b B17 1 +62756 b O3 1 +62760 b C3 1 +62780 w S17 1 +62821 w H17 1 +62830 b Q5 1 +62836 w C7 1 +62890 b G16 1 +62891 b D2 1 +628a1 w F6 1 +62901 b E16 1 +62907 b C4 1 +62910 w P17 1 +62937 b M15 1 +62987 w D12 1 +62996 w F15 1 +629d1 b N14 1 +629d7 b Q16 Q14 2 +629e1 b D15 1 +62a11 w R6 1 +62a30 b C4 1 +62a31 b B3 1 +62a37 w P8 1 +62a57 w D12 1 +62a60 w D17 1 +62a81 b N13 1 +62a81 w D7 1 +62a91 w D6 1 +62ac0 w R18 1 +62af6 w M13 1 +62b17 w Q6 1 +62b61 b Q16 1 +62b66 w B18 1 +62b87 w C18 1 +62b91 w E17 1 +62ba1 b G4 1 +62bc7 b Q3 1 +62bd0 b C7 R9 2 +62be0 w E3 1 +62bf0 b M3 1 +62c16 b B11 1 +62c36 b S6 1 +62c67 b Q18 1 +62c97 b R2 1 +62ca1 b P5 1 +62cb1 b Q6 1 +62cc0 w D18 1 +62cd1 w A4 1 +62d10 b S4 1 +62d41 w R4 1 +62d71 w P2 1 +62d96 w C11 1 +62dc1 b Q13 1 +62dd6 w D16 1 +62de0 w E2 1 +62e07 b N6 1 +62e17 b D14 E13 2 +62e96 b R17 P17 O16 N16 N17 5 +62e96 w O16 O17 R15 P15 R13 Q13 Q12 7 +62ea0 b D5 1 +62ea6 w R6 1 +62eb0 b D7 1 +62ed1 w M4 1 +62ed6 w O4 1 +62f37 w A17 1 +62f46 b C17 1 +62f91 b R5 1 +62f91 w B17 1 +62f97 b L17 1 +62fa1 b B14 1 +62fc0 w E15 1 +62ff1 b C18 1 +63001 w R15 1 +63046 b C8 1 +63066 w G17 B15 2 +630a7 b F3 1 +630e7 w L2 1 +63107 b P14 1 +63131 b A1 1 +63161 b O1 1 +63180 b D8 1 +631a0 w Q7 1 +63206 w G4 1 +63210 b G14 1 +63246 w D7 1 +63270 w Q13 1 +63290 b F16 1 +632c6 b E18 1 +632e0 w P15 1 +632e1 b F15 1 +632f7 w O19 1 +63311 b D6 1 +63320 b F15 1 +63321 b D5 1 +63337 w S12 1 +63346 w O5 1 +63360 b M14 1 +63381 w M17 1 +63391 b F6 1 +633b0 b S13 1 +63417 w R5 1 +63431 b R17 Q13 2 +63470 b F4 F3 2 +634a1 w B17 1 +634b7 w Q8 1 +63517 w B15 1 +63520 b P12 P13 R15 P15 4 +63540 b E18 1 +63561 b D8 1 +63577 b F15 1 +63580 w E15 1 +63596 w P4 1 +635b7 b D7 1 +635d6 b C4 1 +63607 b R3 1 +63620 b G5 1 +63620 w O3 1 +63626 b R3 1 +63636 b S16 1 +63637 w C18 B4 2 +63647 w R16 1 +63657 w Q17 1 +63660 w M4 1 +63686 w E2 1 +636b6 b Q18 1 +636b7 b P14 1 +636e0 b R5 1 +63700 w Q2 O4 2 +63721 w D14 C17 2 +63736 b P4 1 +63757 w Q5 1 +63771 w F4 1 +637c7 b P18 1 +637f0 w G13 1 +63816 w R9 1 +63841 w C15 1 +63850 w N5 1 +63860 w G2 1 +63877 w H6 1 +63880 w D15 1 +63886 b Q6 1 +63890 b O17 1 +638a1 w C8 1 +638c6 b C5 1 +638d1 b D7 1 +63987 b P18 1 +63991 w O1 1 +639a6 w R11 1 +63a00 b Q6 1 +63a11 w R14 1 +63a66 w O4 1 +63a80 b S5 1 +63aa1 b O4 1 +63ac6 b E5 1 +63af0 b F6 1 +63b00 b C6 1 +63b07 w Q13 1 +63b20 b F6 1 +63b30 b L16 1 +63b56 w D4 1 +63b61 b C16 1 +63b81 w Q4 1 +63b96 b E16 1 +63ba0 w Q12 1 +63bc1 b B18 1 +63c11 w R2 R5 Q6 R7 S7 5 +63c36 b H4 1 +63c41 w L3 1 +63c47 b P16 P14 2 +63c66 w P4 1 +63c71 w R2 1 +63c81 w Q18 1 +63cb0 b P16 1 +63cf0 w F16 C15 E14 C12 4 +63d00 w E3 1 +63dc0 w E17 1 +63dc7 b Q8 1 +63de0 w E17 1 +63e16 w F15 F17 2 +63e30 b P16 1 +63e46 w C7 J3 2 +63e51 b R4 1 +63e70 b H16 1 +63ec7 b D12 1 +63ee7 w P15 1 +63ef6 b D3 D18 2 +63f21 b C17 E18 G16 3 +63fa7 w E5 1 +63fb0 b R7 1 +63fc1 b D14 J3 2 +63fd0 b G4 1 +64000 b O13 1 +64001 w P18 1 +64007 b Q2 1 +64026 b D3 1 +64067 b R6 1 +64070 w C18 1 +640b7 w R16 1 +640c1 b C15 R17 2 +640e6 w O4 1 +64106 w P17 1 +64120 w P5 1 +64141 w E16 E14 2 +64146 w F17 1 +64171 b S3 S6 2 +641a7 b B16 1 +641e6 b E3 1 +64200 w N5 1 +64207 b D4 1 +64211 b Q17 Q15 2 +64211 w D3 1 +64221 b Q6 1 +64246 b M2 1 +64277 w E4 1 +64286 w E5 1 +642c1 b E2 1 +642d0 w P14 1 +642d1 b C3 E6 F3 F4 4 +642e1 b H3 1 +64307 w P14 1 +64310 w M4 1 +64337 b D16 E17 C15 F16 D14 5 +64367 b D2 1 +643b7 b C17 1 +643e0 w G3 1 +64481 b D1 1 +64496 b E5 1 +644a1 w F14 1 +644a7 w E15 1 +644b0 b D15 1 +644c6 b N15 C16 2 +644f0 w D14 1 +64506 w D2 1 +64536 b C7 1 +64541 w D3 1 +64567 b B17 1 +64580 b E18 1 +64580 w J16 1 +64596 w S3 1 +645b1 b R17 1 +645d6 b R13 1 +645e6 w B3 1 +645e7 w O13 1 +64626 b F6 1 +64647 w R14 1 +64656 w C6 1 +64661 w S16 1 +646b1 b G17 1 +646e1 b Q5 1 +646e6 b E4 1 +64746 w O14 1 +64747 w P7 1 +64751 w O17 1 +64800 w O15 1 +64806 b H17 1 +648a1 b Q7 1 +648b0 b T15 1 +648e6 b C11 1 +64901 w F18 1 +64911 w L3 1 +64950 w C17 F16 2 +64951 b B5 1 +64961 b B14 1 +649b1 w E7 1 +649c7 w O15 1 +649f0 b D5 G17 E18 3 +649f6 b D12 C14 2 +649f6 w Q5 1 +64a00 b G4 1 +64a80 w O4 Q5 2 +64ab7 b E3 1 +64ac1 b Q11 1 +64ad0 w E16 1 +64ae1 b B2 1 +64b10 w M4 1 +64b11 b E15 E14 C17 C13 4 +64b36 b R3 1 +64b56 b Q7 1 +64b61 w F4 E5 2 +64b70 b E16 E17 2 +64b97 w E4 1 +64ba1 b Q2 1 +64be7 b C5 1 +64bf0 b Q13 P16 2 +64c10 b B18 1 +64c31 b C2 1 +64c40 b O5 1 +64c47 w P13 F17 2 +64c51 b B14 1 +64c61 w O7 1 +64c77 w D14 1 +64c81 b R15 R14 Q14 Q15 4 +64c96 w R18 1 +64ca7 w O2 1 +64d01 w C14 1 +64d07 b R3 O4 Q4 Q5 P6 P5 N5 N4 R6 R7 Q7 11 +64d07 w Q3 1 +64d57 b Q16 O16 2 +64d76 w O16 1 +64dc6 b E3 1 +64e21 b C4 1 +64e30 b C18 1 +64e70 w Q15 1 +64e81 b R3 O5 R6 Q6 4 +64e86 w O4 P4 2 +64e91 b O4 1 +64e91 w H17 1 +64eb1 b F17 D16 2 +64ec0 w G16 D16 F15 3 +64f11 w N18 1 +64f57 w P5 1 +64f66 b J14 D6 2 +64fa1 w H4 1 +64fc0 b P16 1 +64fe1 b R3 S3 2 +65037 b R18 1 +65056 b S13 1 +65080 b B14 1 +650a1 w Q12 1 +650b0 w E4 1 +650f7 b R15 R14 Q15 P16 4 +650f7 w Q16 1 +65107 b H4 1 +65136 w O4 1 +65150 b Q7 C7 2 +651c6 w B16 1 +651d1 w G8 1 +651e1 b O4 1 +65210 b P5 1 +65230 w N5 1 +65237 w H16 R6 2 +65280 w F8 1 +65281 w B4 1 +65290 w M14 1 +65297 b P3 1 +65297 w E18 1 +652a1 b P18 1 +652a6 b R15 R17 E3 3 +652d0 b R15 1 +652f6 w D3 1 +65310 b E4 C9 F3 G3 H3 G4 F4 7 +65310 w E4 F4 C7 C8 C9 D7 D8 D9 8 +65336 w R17 1 +65356 b P16 1 +65360 w Q7 1 +65370 b L17 1 +653d6 b M16 S2 2 +65400 b J3 1 +65401 b O6 1 +65410 b B3 1 +65437 b D16 1 +65491 w D18 1 +654a6 b O16 1 +654b1 w C8 1 +654d1 w O17 1 +654d7 w D15 1 +654e0 b B7 1 +65521 b C13 1 +65536 b P17 1 +65557 w D13 D16 2 +65560 w F9 1 +65570 w Q14 R17 2 +655a0 b R9 1 +655d7 w Q3 1 +655f0 b N18 N15 2 +65606 b G17 1 +65641 b B6 1 +656a0 b R2 S14 2 +656a1 b D16 1 +656a6 w Q13 1 +656c0 w F4 1 +656d1 b N3 1 +65730 b H4 1 +65737 b F3 1 +65757 w Q6 Q7 2 +65766 b N5 N6 2 +65767 b S4 1 +65770 b C12 1 +65770 w F17 1 +65796 b C3 1 +657e1 b D13 1 +657e7 w D14 E13 2 +657f1 b C13 1 +657f6 w R18 1 +65837 w S7 1 +65850 w S2 1 +65851 b O11 1 +65860 b F5 1 +65870 b P14 1 +65870 w D8 1 +65871 b D16 1 +65871 w F5 1 +65877 b R17 1 +65877 w C18 C11 2 +658a0 b P18 1 +658a1 b A4 1 +658a6 b Q17 1 +658a7 w O12 1 +658d0 b L4 1 +658d6 w P18 S18 2 +65901 w B2 1 +65927 b R4 1 +65941 w H17 1 +65956 b P18 1 +65957 b C4 1 +65967 w D4 1 +65976 w D4 1 +65987 w C2 1 +659b0 w N4 1 +659c1 b R8 1 +659f1 b P18 1 +659f1 w R16 1 +65a07 w O6 N7 2 +65a11 b B15 1 +65a36 b P6 Q7 O5 3 +65a41 b E15 1 +65a67 w R13 1 +65a81 w R8 1 +65a96 w C16 1 +65a97 w C3 1 +65ad0 w C4 1 +65ad6 w Q18 1 +65af7 b B4 1 +65b06 w R4 1 +65b57 b D5 1 +65b81 w Q12 J16 2 +65b97 w R3 1 +65bb7 w E15 1 +65bf0 b R15 1 +65c06 w C15 1 +65c57 w D12 1 +65cc6 b E17 1 +65cd1 w E9 1 +65d50 w G16 1 +65d56 b E16 1 +65d61 b P5 1 +65d61 w R17 1 +65d80 w B2 1 +65d81 b Q17 1 +65d91 b D18 1 +65de1 b Q5 1 +65df1 w S17 1 +65e26 w D7 1 +65e50 w S17 1 +65e77 w R13 1 +65e80 b Q14 S14 N3 3 +65e90 b G4 1 +65ea7 w R15 1 +65eb6 b E4 1 +65eb7 b N3 1 +65ee0 b B3 1 +65f17 w F14 1 +65f26 w S3 1 +65f41 w G18 1 +65f67 b E17 D16 2 +65f91 b Q16 1 +65fa6 w E17 1 +65fe1 b G16 C15 2 +65fe7 w C16 1 +65ff1 b C8 1 +66006 w B5 1 +66007 b Q4 1 +66031 b R18 1 +66040 w F4 G4 2 +66051 w Q17 1 +66066 w C4 1 +66091 w H17 E18 D18 3 +660a6 w Q6 1 +660c6 w D7 1 +660f0 b E2 1 +66117 w S5 1 +66127 b C4 1 +66136 w G4 1 +66176 w R4 1 +661a1 w R2 1 +661b1 b Q14 1 +66206 b Q14 1 +66261 w O2 1 +662b0 w Q13 1 +662e1 w E16 1 +66310 b N6 1 +66317 b O4 1 +66330 b Q11 1 +66337 w G16 1 +66341 b P7 1 +66350 w P5 1 +663b1 w H3 1 +663d6 b B14 1 +663e0 w F8 D4 2 +66401 w R4 1 +66431 b P17 1 +66451 b C13 1 +66496 b Q5 1 +664a7 b O18 1 +664e1 b C12 1 +66517 b B11 1 +66540 b E15 1 +66557 b S13 1 +66586 w D3 1 +66587 w E17 1 +66590 w R17 1 +665a6 w A17 1 +665e1 b R5 1 +66620 b C9 1 +66646 w G8 1 +66657 w E6 1 +66667 w D3 1 +66671 b R6 1 +66676 w P16 1 +666b6 b H3 1 +666b7 w E18 1 +666c7 w N1 1 +666d1 w M16 1 +666d7 b S13 1 +66710 w P8 P7 R5 P5 4 +66711 b C2 1 +66717 w D14 1 +66721 w O13 1 +66727 w Q12 1 +66731 w C14 D15 2 +66741 b P2 1 +66781 b O4 1 +66807 b D16 1 +66830 b F15 1 +66836 b Q5 1 +66847 w Q11 1 +66850 w P4 N3 2 +66856 b Q16 1 +66880 w Q7 1 +66897 b C8 1 +668c0 w R5 1 +668c1 b D2 1 +668c1 w N4 1 +668e7 b E16 1 +66900 w T16 1 +66926 b Q12 1 +66930 w M6 1 +66951 b B3 1 +66980 w P16 N17 2 +66987 w E4 D5 2 +669a6 w E6 C6 S16 3 +669b7 w D17 1 +669c0 b O14 1 +669e6 w M3 1 +66a11 w R17 1 +66a81 b C2 1 +66ab6 w G15 1 +66af0 w O2 1 +66b20 w P5 1 +66b27 w C18 1 +66b46 b E17 1 +66ba6 w F16 1 +66bd0 b P14 1 +66c36 w P14 1 +66c46 w C8 1 +66c50 w Q14 1 +66c51 b Q5 1 +66c51 w F18 1 +66c77 b R4 1 +66cb0 w R6 1 +66cd6 w H2 1 +66cd7 b R3 1 +66cf0 b E16 1 +66d26 w S4 P16 O17 3 +66d47 b L17 1 +66d97 w S3 1 +66db6 b E5 1 +66dd0 w P16 1 +66dd7 b N17 1 +66de6 w N16 1 +66df7 b C18 1 +66e06 b S17 1 +66e26 b Q15 1 +66e26 w Q15 1 +66e41 w Q18 M3 2 +66e51 w R4 1 +66ea7 b Q2 1 +66ed0 w N17 1 +66ef0 w L3 1 +66f16 w S6 1 +66f37 b B17 1 +66f71 b S16 1 +66f81 b Q16 1 +66f91 b C7 1 +66fb6 w C18 1 +66fb7 w S17 1 +66fe7 w D18 1 +67011 b Q14 Q13 2 +67046 b R2 1 +67051 w F16 1 +67097 w B2 1 +670a7 w N16 1 +670d1 w D14 1 +670f7 w E7 1 +67100 b Q3 1 +67101 w R3 1 +67110 w G4 1 +67111 b R7 1 +67117 b R6 1 +67157 b E4 1 +67157 w G5 1 +67171 b D16 1 +67176 b Q5 1 +67180 w N17 1 +671c0 w T15 1 +671d0 b G6 1 +671e7 w N7 1 +67200 w J14 1 +67231 b Q18 1 +67240 w E4 1 +67267 b C6 1 +67270 w F2 1 +67281 b C2 1 +67296 b S16 1 +672d1 w N3 1 +67337 w G16 D16 2 +67346 b R12 1 +67370 w S17 1 +67387 b D2 1 +67390 b Q6 1 +673a0 b G17 E15 G16 3 +673d6 b R3 R5 Q6 Q7 R7 5 +673d6 w Q6 R6 P3 P5 N3 N4 M4 7 +673e1 b O14 1 +673f1 b Q2 1 +673f7 b S3 1 +67470 w E14 1 +67476 w P5 1 +67487 w C3 1 +674d1 b E17 1 +674e0 w P15 1 +67547 b O15 1 +67591 w D2 1 +67597 w B5 1 +675c7 w D18 1 +675d7 w D13 1 +675e0 b E7 1 +67636 w C6 1 +67676 w E2 1 +676a7 w D2 1 +67701 w R11 1 +67716 w F17 1 +67730 w E2 1 +67756 b G17 1 +677a7 b D11 1 +677b6 w R6 Q5 2 +677e1 w H5 1 +67821 b F14 1 +67836 b B16 1 +67891 b R14 1 +67896 w N6 1 +67927 w P18 P16 2 +67930 b F5 1 +67947 w C12 1 +67960 w H16 1 +679a6 b O4 1 +679a7 w S4 1 +67a01 b C12 1 +67a11 w B14 1 +67a50 w E18 1 +67a80 w P5 1 +67ae0 b D17 1 +67b41 w G15 1 +67b46 b E15 1 +67b86 b S4 1 +67be1 b R17 1 +67bf0 b E8 1 +67c20 w C15 1 +67c37 w E17 1 +67c56 w C4 1 +67c77 w O4 1 +67c86 b C14 1 +67ca0 b E2 S18 2 +67ca7 b R16 1 +67cc1 w J17 1 +67cc6 w E2 1 +67cf0 b E4 1 +67d07 b D3 1 +67d16 b P6 1 +67d46 b F3 1 +67d51 b P16 1 +67da1 w Q14 1 +67da7 b C4 1 +67dc6 w N14 1 +67dd1 b F18 1 +67dd7 b F15 1 +67de7 w F4 G5 2 +67df1 b O2 1 +67df1 w Q6 1 +67e17 b E17 1 +67e46 w M18 1 +67e57 b R18 R15 Q14 R13 S13 5 +67e96 b D17 1 +67eb0 b O17 Q16 Q17 3 +67eb6 w B15 1 +67ed1 w D17 1 +67ee7 b S18 1 +67f10 w O7 1 +67f71 w R11 1 +68006 w C13 1 +68040 w O16 1 +68047 w P13 1 +68050 w R13 1 +68087 w E7 1 +680b7 b S3 1 +680d6 b B3 1 +680f7 b N3 1 +68121 b C13 1 +68126 b O6 1 +681a6 w F3 1 +681e1 b Q6 R7 2 +68207 b D17 1 +68210 w Q14 R14 2 +68221 w E15 1 +68241 w R6 1 +68257 w N15 1 +68281 w C6 1 +682b7 w D3 1 +682d0 w G17 1 +68300 w D15 1 +68301 w C12 R5 2 +68317 w F18 1 +68327 b P18 1 +68336 b H3 1 +68351 b N14 1 +683a0 w L4 1 +683c1 w N2 1 +683c7 b Q4 1 +683d0 w L3 1 +683f0 b E17 1 +683f0 w Q7 1 +683f6 b S4 1 +68480 w D5 1 +684a6 b P18 1 +684b0 w N9 1 +684d1 b Q4 1 +684e1 b B16 1 +684e7 b C4 1 +68506 w P5 1 +68511 b C16 1 +68546 b B6 1 +68551 w F2 1 +68566 b D13 1 +68566 w C13 1 +685a6 b G3 1 +685b7 b R18 B2 2 +68640 w C18 1 +68680 b Q16 1 +686f1 w O4 1 +68756 b Q13 1 +68777 w G5 1 +68797 w D11 1 +687c0 b E6 1 +687c7 w S17 1 +687f7 w C4 1 +68840 b C12 1 +68876 w F18 1 +68897 b S15 1 +688c1 b P2 1 +68901 b B8 1 +68917 b J17 E14 E13 3 +68957 b P16 1 +68967 b B11 1 +68987 b B5 1 +689a0 b F7 1 +689b0 w D7 S16 2 +689c0 w G17 1 +689d6 w P15 1 +689e7 w N5 O4 2 +68a17 b E7 1 +68a70 w O7 1 +68a87 w S16 1 +68aa1 w C17 1 +68af6 b E16 1 +68b10 w R17 1 +68b31 b B17 1 +68b46 b P17 1 +68b56 w Q17 1 +68b96 b P15 1 +68bb1 b D7 1 +68bb6 b E15 1 +68bc6 b D15 1 +68bd1 b F16 1 +68be0 b H15 1 +68c61 b Q7 1 +68c81 b F4 1 +68c97 b B6 1 +68cb0 w R8 1 +68cb1 w D6 1 +68cc1 b F12 1 +68cc1 w J18 1 +68d00 w A2 1 +68d46 w E4 1 +68d70 w S14 1 +68d86 b C3 1 +68d86 w O15 1 +68d97 b R17 1 +68db7 b C14 1 +68de0 w R4 1 +68e00 b Q14 1 +68e01 b G3 1 +68e10 b C4 1 +68e46 w S5 S2 2 +68f30 w B14 1 +68fa1 b N6 1 +69047 w B8 1 +69050 b P17 1 +69076 b E17 1 +690b6 w F3 C7 2 +690d7 w N15 1 +690e7 b G4 1 +690f0 w J7 1 +69110 b R3 1 +69110 w R17 1 +69111 b M3 1 +69127 b Q4 1 +691e7 b E5 E3 2 +691f1 w F17 1 +69200 w C5 1 +69211 b G3 1 +69237 b Q3 1 +69237 w S3 1 +69250 w P16 1 +69261 b O6 1 +69291 w G16 H14 2 +692b6 w C3 1 +69300 w H15 1 +69341 w R4 1 +69397 b C15 1 +69417 b D4 1 +69440 b T15 1 +69446 b G14 1 +69450 b C17 1 +694b1 b B17 1 +694b6 b P16 O17 2 +694e0 w D14 1 +694e1 b E15 1 +694f1 b L17 P14 2 +69520 b Q7 P4 2 +69521 w R16 1 +69531 w R2 1 +69550 w R6 1 +69570 w D7 1 +695b6 w S13 1 +695c6 w S15 1 +69610 b N5 1 +69616 w F4 C4 2 +69620 b P12 1 +69621 w N4 1 +69627 w O17 1 +69647 w P18 1 +69677 b G16 1 +69697 w C4 1 +69701 w C2 1 +69717 w R4 1 +69727 w P7 1 +69746 b R7 1 +69770 w B5 1 +69771 w R11 1 +69780 w P17 1 +69791 b Q5 1 +697c7 b D16 1 +69800 w R3 N4 2 +69826 w F2 1 +69836 w S16 1 +69867 b H17 1 +69871 b C13 1 +69890 w R16 1 +698b7 w R2 1 +698d7 b D7 D6 2 +698d7 w D6 1 +698e1 b D18 1 +698e6 b D17 1 +69931 b R3 1 +69956 b M4 1 +69976 b S8 1 +69991 b O17 1 +69997 w Q17 1 +699f7 b C16 1 +69a00 w S14 1 +69a07 w B7 1 +69a11 w G18 1 +69a31 w O2 1 +69a40 b E5 1 +69a50 b L17 1 +69a66 w N3 1 +69a77 w E18 1 +69ad1 w E3 1 +69ae0 b F17 1 +69b21 b E15 1 +69b26 b P15 1 +69b30 b E16 1 +69b50 w E14 1 +69b56 b H3 1 +69b60 b F5 D6 2 +69b61 w B14 1 +69ba0 w E2 E4 2 +69be0 b P2 1 +69c37 w Q4 1 +69c57 b Q6 B7 2 +69c81 b P17 1 +69c81 w D18 1 +69c97 w D8 1 +69ca0 w C15 1 +69cd0 b Q9 1 +69cd6 b B9 1 +69ce1 b D12 1 +69d17 w D12 1 +69d30 b B1 1 +69d37 w C14 1 +69da0 b P3 1 +69de6 b P16 1 +69df7 w R5 1 +69e40 w D17 1 +69e91 b B17 1 +69e96 w D17 1 +69eb1 w Q16 1 +69ed7 b J16 1 +69ee6 b B15 1 +69f11 b S16 1 +69f76 b B4 1 +69f90 w Q12 1 +69fb6 w E6 1 +69fc0 b Q2 1 +69fc6 b D5 1 +69fd7 b D15 E15 G16 C17 4 +69fd7 w E16 1 +6a010 w N4 1 +6a026 w Q14 1 +6a046 b G4 1 +6a066 b O7 1 +6a070 w E16 1 +6a076 b B12 1 +6a076 w B3 1 +6a0c6 w F5 1 +6a107 b S3 1 +6a116 w D15 1 +6a131 w P9 Q3 2 +6a137 b F17 1 +6a140 b P2 1 +6a160 b C5 1 +6a170 b E15 1 +6a1a6 b Q5 1 +6a1b1 w P4 1 +6a200 b Q7 1 +6a201 b C7 1 +6a206 b O3 1 +6a221 w S11 1 +6a226 b P2 1 +6a226 w S17 1 +6a231 b G15 1 +6a246 b G5 1 +6a246 w S14 E2 2 +6a271 b S13 1 +6a281 b P7 1 +6a286 w C13 1 +6a287 b F5 F6 C5 C6 4 +6a296 w R5 1 +6a2b0 w B4 C5 2 +6a2b7 b B4 1 +6a2e1 b B5 1 +6a306 b S5 1 +6a310 w C13 1 +6a326 b S11 1 +6a337 b G3 1 +6a337 w R16 1 +6a3d7 w C3 1 +6a400 b C16 D5 2 +6a410 w R13 1 +6a430 w F17 1 +6a457 w C15 1 +6a4b0 w S15 1 +6a4b1 w S6 1 +6a4b7 w R5 1 +6a4e7 b O17 1 +6a511 b R18 1 +6a561 b B3 1 +6a567 b N17 1 +6a577 w N6 1 +6a581 w O3 1 +6a587 w P3 O2 P2 3 +6a597 w E18 1 +6a5a6 b Q18 1 +6a5c6 b P4 1 +6a5c7 w N16 M2 H6 3 +6a5d0 w P14 1 +6a600 b G4 C4 2 +6a6b1 b Q13 R12 R11 3 +6a6c1 w L3 1 +6a741 b S11 1 +6a747 w L16 1 +6a756 b O14 1 +6a780 b H16 1 +6a781 b R15 S14 S15 3 +6a7a0 b R3 1 +6a7b0 w H16 1 +6a7f6 b B18 1 +6a826 w N16 1 +6a831 b P13 1 +6a847 w F16 1 +6a861 w F13 1 +6a871 b Q5 1 +6a881 b C7 1 +6a8c6 w D7 1 +6a8d1 w B5 1 +6a8d6 w P12 1 +6a956 w B6 1 +6a961 b R17 1 +6a970 b C2 1 +6a970 w R13 P15 2 +6a977 b M5 1 +6a9b0 w O7 R4 2 +6a9c1 w S16 1 +6a9e7 w S3 1 +6aa30 b D5 1 +6aa46 b D15 1 +6aa47 b P8 1 +6aa70 w G3 1 +6aa97 w B16 1 +6aab1 b D15 1 +6aac7 w O16 1 +6aae7 b Q14 1 +6aaf0 w C5 1 +6ab11 w R6 1 +6ab17 w G18 1 +6ab46 b E18 1 +6ab61 w B17 1 +6ab91 b A3 1 +6abc7 w N4 1 +6abd0 b R6 1 +6abd7 b S6 1 +6abe1 b L18 1 +6ac21 b B5 1 +6ac40 w O16 N16 2 +6ac41 b F2 1 +6ac56 w S17 1 +6ac60 b B18 1 +6aca7 w C7 1 +6acb6 b D18 1 +6acb7 w C17 1 +6ad10 w S5 E17 2 +6ad40 b L16 1 +6ad56 b N15 1 +6ad87 w P17 S13 2 +6ade1 w M16 1 +6ade7 w O16 1 +6ae06 b C17 1 +6ae36 w E3 1 +6ae51 w E2 1 +6ae76 w B11 1 +6ae96 w C12 1 +6aef6 b F16 1 +6aef7 b F3 1 +6af20 w R17 1 +6af36 b N16 1 +6afa7 w D16 1 +6afc6 w D5 1 +6afe0 w D16 1 +6afe1 b D3 1 +6b006 b D14 D15 2 +6b026 w F16 E16 2 +6b041 w R7 1 +6b076 w P5 1 +6b097 b D17 1 +6b0d1 b Q5 P6 2 +6b0e6 b F4 1 +6b0e7 b C14 1 +6b107 b Q15 1 +6b120 w T17 1 +6b130 w N4 Q4 O5 3 +6b137 b B8 1 +6b141 w F16 G15 B7 3 +6b161 w Q11 1 +6b166 b F17 1 +6b1a7 w Q3 1 +6b1b0 w R14 1 +6b1c0 b S5 1 +6b1f0 w E4 1 +6b1f1 b S6 1 +6b1f6 w Q7 1 +6b237 b D6 1 +6b247 w R16 P16 2 +6b2a0 b O17 1 +6b2a0 w R3 1 +6b2b0 w R3 1 +6b2b7 w E17 1 +6b2e0 b H15 1 +6b2e6 b D5 1 +6b2f7 w Q15 P14 2 +6b310 w M5 1 +6b357 w N5 1 +6b367 w P3 1 +6b3e1 w F5 1 +6b451 w R7 1 +6b476 w L17 1 +6b487 w E2 1 +6b497 w B15 F18 2 +6b4d1 w S3 1 +6b501 w Q16 1 +6b506 b Q6 1 +6b531 w Q16 N16 2 +6b537 w C18 1 +6b561 w N14 1 +6b566 b P16 1 +6b5b6 w R3 1 +6b600 b B2 1 +6b600 w R8 1 +6b626 b G4 1 +6b630 b F4 1 +6b656 b F16 1 +6b677 w Q13 1 +6b681 w C9 1 +6b6c1 w L3 P6 P7 3 +6b6c6 b F18 1 +6b6f0 w M14 1 +6b727 w M6 1 +6b777 b R3 1 +6b781 b D4 1 +6b7b7 b F5 1 +6b7e1 b Q5 1 +6b800 b C12 1 +6b817 w Q6 1 +6b820 b M4 1 +6b826 w S6 1 +6b827 w Q4 1 +6b830 w B17 1 +6b837 w Q8 1 +6b841 w D16 F16 2 +6b867 b C13 1 +6b871 b J4 1 +6b891 b P3 R6 2 +6b891 w S5 1 +6b8a1 b S16 1 +6b8a6 w D14 1 +6b8a7 b G2 1 +6b8d1 b D5 1 +6b8e6 w E3 C3 2 +6b931 w E18 1 +6b951 b R12 1 +6b976 b B17 1 +6b9a1 w C14 1 +6b9a7 w R4 1 +6ba36 w B4 1 +6ba50 b S17 1 +6ba97 w B16 1 +6bac7 b S3 1 +6baf6 w D15 E6 2 +6baf7 b O14 1 +6bb01 w O4 1 +6bb17 b C7 1 +6bb21 b C5 1 +6bb21 w P12 1 +6bb46 b D6 1 +6bb46 w O16 1 +6bb70 b F12 1 +6bb87 w B5 1 +6bb97 w C17 1 +6bba7 w B16 1 +6bc27 b D18 1 +6bc36 w P5 1 +6bc67 b P5 1 +6bca0 b D6 E6 2 +6bca0 w R4 Q3 Q18 3 +6bca7 w B16 1 +6bcc6 b R8 1 +6bce0 b C13 E15 D13 3 +6bd00 b P5 1 +6bd01 w Q7 1 +6bd30 w B15 G3 2 +6bd47 b D18 1 +6bd47 w R2 1 +6bd87 w N3 1 +6bd91 w R4 1 +6bda0 b F17 D16 D17 3 +6bdd6 w J6 1 +6be36 b E3 1 +6be47 b B14 1 +6be50 w N16 1 +6be97 w R16 1 +6beb7 b F17 1 +6bf16 w A4 1 +6bf21 b C3 D6 C6 H4 F5 5 +6bf50 w R14 Q16 R16 3 +6bf81 b P17 1 +6bf90 w O5 1 +6bfd0 w G18 1 +6c031 b C17 1 +6c040 w H15 1 +6c051 b Q15 1 +6c066 b D2 1 +6c081 b L3 1 +6c096 w C19 1 +6c097 b R9 1 +6c0e7 w D6 E7 2 +6c100 b P3 1 +6c106 b B12 1 +6c126 b Q5 1 +6c127 b H5 1 +6c131 b P17 1 +6c136 w O4 1 +6c156 w C15 1 +6c160 w C9 1 +6c1a0 w H16 1 +6c221 w E4 D5 2 +6c236 b P14 1 +6c266 w S7 1 +6c276 b R17 1 +6c2b7 b S4 R2 2 +6c2c7 w A19 1 +6c2e1 w R15 1 +6c307 b E17 1 +6c351 w Q4 1 +6c377 w G17 1 +6c3d6 w R2 1 +6c3e6 b S8 1 +6c3f1 b D15 1 +6c3f1 w B4 1 +6c3f7 b H16 1 +6c426 w N2 1 +6c431 b C17 1 +6c447 w S4 1 +6c450 b S17 1 +6c496 b D3 1 +6c4d6 b N17 1 +6c4f0 w C3 1 +6c520 b E4 1 +6c547 w Q15 1 +6c577 b R16 1 +6c586 b B5 1 +6c587 w S4 1 +6c5d7 b R14 1 +6c5e0 w O8 1 +6c5e6 b F5 1 +6c5f6 w Q17 1 +6c5f7 b Q8 1 +6c610 b E3 1 +6c617 b H3 S13 2 +6c630 b S2 1 +6c630 w E14 1 +6c647 b Q16 1 +6c667 b Q18 1 +6c667 w R17 1 +6c6b0 w G4 D5 2 +6c6b1 b D17 1 +6c6c0 w O2 1 +6c717 w G7 1 +6c727 b R17 Q17 R11 R12 S15 R16 6 +6c746 w P3 O4 2 +6c787 b S2 1 +6c797 w O18 1 +6c7a6 b P18 1 +6c7e6 b O17 1 +6c806 b O17 O16 2 +6c806 w S3 1 +6c821 b B6 1 +6c830 b G3 1 +6c8a0 b N14 1 +6c8a7 b T6 1 +6c8d0 b H4 P5 2 +6c927 w D6 1 +6c940 w O15 1 +6c990 b F16 1 +6c9a1 b Q17 1 +6c9b7 w Q12 1 +6ca87 w M17 1 +6ca96 w D5 1 +6cab0 b H3 1 +6cac0 b N14 1 +6cac7 b P2 1 +6cb67 b H17 1 +6cb91 b M4 1 +6cbc0 w O15 1 +6cc61 b C18 1 +6cc61 w D16 1 +6cc76 b F16 1 +6cc77 w F13 1 +6cc80 b B5 1 +6ccc6 w O4 1 +6ccd0 w B15 1 +6cce1 w R5 1 +6cd10 w O13 1 +6cd20 b N6 1 +6cd37 w Q13 1 +6cd46 b Q13 1 +6cd50 w D8 1 +6cd61 b S2 1 +6cd91 w Q5 Q6 2 +6cdb6 b C3 1 +6cdc7 w P15 1 +6cdf1 b D13 1 +6ce01 w D3 1 +6ce11 b R18 R11 2 +6ce16 b G5 1 +6ce71 w C5 1 +6cef0 b R4 1 +6cef0 w J4 1 +6cef7 w Q16 1 +6cf11 w S8 1 +6cf96 w C13 1 +6cfa6 w D14 E2 2 +6cfb7 w R9 O5 2 +6cfc0 b P1 1 +6cfe1 b C15 C14 D14 D15 4 +6cfe1 w B17 1 +6d050 w C15 1 +6d071 b P17 1 +6d0a6 b R12 1 +6d0a7 w D17 1 +6d0b1 w N6 1 +6d0d1 b H14 1 +6d121 b R17 G17 2 +6d140 b B3 1 +6d196 w P13 1 +6d1d6 b C16 1 +6d1e1 b G17 1 +6d217 b S16 1 +6d287 w F2 1 +6d2a1 w C13 1 +6d2d1 w S9 1 +6d306 b M18 1 +6d310 w S14 1 +6d327 b O18 1 +6d377 w D6 1 +6d386 b C19 1 +6d3a1 b P7 1 +6d3b6 b E17 1 +6d3d6 w D7 1 +6d3f7 b L16 1 +6d470 b G3 1 +6d4f1 w S15 1 +6d501 w P3 1 +6d507 w E4 1 +6d550 w B17 B16 2 +6d566 w M16 1 +6d590 b S15 R16 M4 3 +6d5a0 b D18 1 +6d5a1 w S16 1 +6d5c1 b C18 1 +6d5c1 w P6 1 +6d5e6 w E5 1 +6d5f0 b B3 B4 2 +6d5f7 b Q16 1 +6d670 b R11 1 +6d6c7 b B5 1 +6d6d1 b C16 1 +6d6e0 w Q9 1 +6d6f0 b P2 Q3 2 +6d6f7 b B18 1 +6d717 w E15 1 +6d756 w S13 1 +6d770 b F3 1 +6d790 w E15 1 +6d7a1 b O18 1 +6d7a6 w E2 1 +6d7c0 b P18 1 +6d7e6 b P16 1 +6d7e6 w P16 1 +6d7f0 w N17 1 +6d801 b S17 1 +6d807 w D3 1 +6d817 w D1 J4 2 +6d826 w R17 1 +6d830 b J13 1 +6d840 w E7 1 +6d847 w F12 1 +6d861 b L4 1 +6d880 b P1 1 +6d886 b C6 1 +6d897 b Q18 1 +6d8d7 b C3 1 +6d8f7 w Q18 1 +6d916 w R7 1 +6d936 w R4 1 +6d951 b P7 1 +6d970 w J4 1 +6d981 w Q11 1 +6d996 b E5 1 +6d997 w C17 F16 F17 D12 E14 5 +6d9c1 w M3 1 +6d9c7 b D6 1 +6d9d1 b L2 1 +6d9e7 b C2 1 +6da00 b B17 1 +6da87 w D17 1 +6daa7 b Q17 1 +6dab6 w D14 1 +6dac6 b E17 1 +6db11 w E6 F4 2 +6db27 w D15 E14 2 +6db31 w F4 1 +6db81 b O2 1 +6dc00 w D8 1 +6dc10 w B5 1 +6dc21 w Q16 1 +6dc31 b E4 1 +6dc56 b O18 1 +6dcc0 b R18 1 +6dcd1 b D14 1 +6dd06 w G17 C11 2 +6dd81 w O4 N3 2 +6dd91 b C17 1 +6dda1 w C6 1 +6ddb1 b F17 1 +6ddb6 w R16 S15 2 +6ddc0 w S2 1 +6ddf6 w S4 1 +6de06 w O2 1 +6de17 b O6 1 +6de91 b D3 1 +6de96 b D5 1 +6deb1 b E15 1 +6df06 b D17 1 +6df26 b O14 1 +6df31 b C3 1 +6df46 b R9 1 +6df66 b P12 1 +6df86 w D6 1 +6df90 b Q7 D15 2 +6dfb6 b C16 1 +6dfc1 w G14 1 +6e030 b C14 1 +6e037 b D4 F4 2 +6e0e6 w B18 1 +6e0f1 w F16 1 +6e0f7 b P15 R14 2 +6e121 w E17 1 +6e170 b M17 1 +6e171 b D14 1 +6e171 w D13 D14 2 +6e176 b Q12 1 +6e226 b E5 1 +6e246 b G15 1 +6e250 w F13 1 +6e257 w D6 1 +6e287 w O5 1 +6e296 w C3 1 +6e2b6 b C19 1 +6e2d0 b Q17 1 +6e2e0 w P17 1 +6e2e6 b M17 1 +6e2f0 w F16 F18 2 +6e301 b P13 1 +6e316 b C7 E2 2 +6e357 b R13 1 +6e361 b Q17 1 +6e361 w O17 1 +6e367 b R13 1 +6e370 b C2 1 +6e377 b R16 1 +6e391 b D3 1 +6e3f0 b L3 1 +6e3f1 b G17 1 +6e3f1 w E2 1 +6e437 b Q2 1 +6e466 b J2 1 +6e480 w E17 1 +6e490 w P8 1 +6e4a0 w Q8 1 +6e4c0 b H3 1 +6e4e7 w S16 1 +6e4f0 b D15 1 +6e506 b Q14 1 +6e506 w Q12 1 +6e547 w D4 1 +6e556 b B5 1 +6e577 b N14 1 +6e577 w S15 1 +6e5a1 b P13 1 +6e5a7 w C17 1 +6e5c0 b E16 1 +6e5c1 b D16 1 +6e5f0 b E2 1 +6e600 w E16 1 +6e616 b F3 1 +6e620 w P7 1 +6e671 b D6 C2 2 +6e671 w D2 1 +6e6b1 w D5 P18 2 +6e720 b G2 G5 2 +6e731 b S6 1 +6e731 w B15 1 +6e737 b M2 1 +6e760 w M5 1 +6e766 w S17 1 +6e790 w C9 1 +6e7a6 b C19 1 +6e7b0 b G12 H14 2 +6e7d1 b B4 1 +6e7f0 w O14 1 +6e830 w C17 1 +6e836 w Q13 1 +6e847 w R14 1 +6e870 w P8 1 +6e880 w D5 1 +6e887 b E8 1 +6e887 w Q3 1 +6e890 b Q15 1 +6e891 w C12 1 +6e896 w E3 1 +6e8a0 b A15 1 +6e8d7 w M4 1 +6e8f7 w D4 1 +6e920 w C13 1 +6e936 b N17 1 +6e977 w S16 1 +6e990 b D15 1 +6e9a7 w R13 1 +6e9c7 w C17 1 +6e9f6 b D4 1 +6ea06 b C17 1 +6ea10 w E13 1 +6ea50 b H13 1 +6ea61 b F15 S18 2 +6ea70 w Q6 1 +6ea97 b R3 R4 O3 3 +6eaa0 b G17 C14 2 +6eac1 b E15 1 +6ead0 w E18 D17 2 +6eb11 b O3 1 +6eb27 b P14 1 +6eb27 w P3 1 +6eb56 b E18 1 +6eb81 w J3 G3 2 +6eb86 b D16 1 +6eba6 w A16 1 +6ec00 w R11 1 +6ec37 w B8 1 +6ec81 b C16 1 +6ec81 w C17 D14 D16 E16 F15 E15 E13 D13 F17 G17 G16 11 +6ecc0 w B14 1 +6ecc1 w L4 1 +6ecc7 b D18 1 +6ed27 w P14 R17 2 +6ed30 w R6 1 +6ed77 w B5 1 +6ed97 b F16 1 +6edb1 b R3 1 +6edb6 b Q6 1 +6edf7 b G3 1 +6ee17 b E15 E17 2 +6eea6 b G3 1 +6eeb1 b M18 1 +6eec0 b D8 1 +6eed1 w Q18 1 +6ef10 w G17 1 +6ef16 b Q18 1 +6ef21 w H2 1 +6ef41 w M3 N18 2 +6ef46 b E4 F3 2 +6ef47 w P2 1 +6ef96 w P13 1 +6efa7 w O17 1 +6eff0 b S15 1 +6f000 b P7 1 +6f007 b G3 1 +6f051 b B7 1 +6f056 w M5 1 +6f096 w F16 1 +6f0c6 w M3 1 +6f0d7 w S3 1 +6f100 w N15 Q13 2 +6f140 b E2 1 +6f146 b F4 G4 2 +6f150 w D19 1 +6f151 w R16 1 +6f177 w R15 1 +6f186 b B6 1 +6f187 w P5 1 +6f197 w O4 N4 2 +6f1a1 b P4 R15 2 +6f1c0 b L16 1 +6f211 b S3 1 +6f2d6 b N14 1 +6f336 w R15 1 +6f387 w G3 1 +6f3a0 b A16 1 +6f3a1 w F17 1 +6f3b6 b D17 1 +6f3c7 b P5 1 +6f3d6 b B18 1 +6f410 b C17 1 +6f411 w B18 1 +6f431 b D4 1 +6f466 w R3 1 +6f491 b C3 B5 D7 3 +6f4a6 w P6 1 +6f4d1 b M6 1 +6f4e1 w C2 1 +6f5b1 b F17 1 +6f5c1 b D17 1 +6f5d7 b Q7 1 +6f600 b D7 1 +6f607 w Q2 1 +6f617 b F6 1 +6f646 b F14 1 +6f646 w D6 1 +6f671 b Q17 1 +6f676 w D17 1 +6f680 w J3 1 +6f691 b B16 1 +6f6d1 w D3 1 +6f6f0 b L17 1 +6f6f1 b C2 1 +6f730 w H16 1 +6f737 b N2 1 +6f741 b D14 C13 B3 3 +6f7d7 w D13 1 +6f7e1 w P16 1 +6f800 w Q6 P6 2 +6f821 w S18 1 +6f840 b Q3 R4 2 +6f851 b B18 1 +6f880 b D6 1 +6f890 b Q9 1 +6f8a0 w B15 1 +6f8a6 w D2 1 +6f8a7 w O17 1 +6f8c0 b N5 1 +6f8c1 w J4 1 +6f910 b B15 1 +6f916 w P3 1 +6f941 b E15 1 +6f950 w O5 1 +6f957 w O12 1 +6f960 w P16 1 +6f986 b D3 1 +6f996 b D7 1 +6f9d6 w C18 1 +6f9d7 b O14 1 +6f9e0 w Q17 1 +6f9f0 w B2 1 +6f9f6 b F5 1 +6fa07 w C16 1 +6fa66 w F17 1 +6fa80 w B2 1 +6fac0 b L17 1 +6fad0 b B9 1 +6fad6 b C3 1 +6fae0 w D17 1 +6fb10 w Q15 1 +6fb16 w E4 1 +6fb17 b D4 C3 2 +6fb26 b C14 1 +6fb36 w B14 1 +6fb61 b N7 1 +6fb67 w E3 1 +6fbd7 w B14 1 +6fbe7 b S8 1 +6fbf7 w D13 1 +6fc31 w R16 1 +6fc70 w Q14 1 +6fc80 w R2 1 +6fc86 b N2 1 +6fc97 b R16 J16 2 +6fcd7 b E18 1 +6fce0 b F15 1 +6fcf6 w D14 1 +6fcf7 w N5 1 +6fd90 w D14 1 +6fd97 b R2 1 +6fdb0 b P15 1 +6fde0 b C17 1 +6fe00 b C4 1 +6fe01 b E18 1 +6fe20 w B17 P4 2 +6fe60 w T14 1 +6fe80 w E2 1 +6fe87 b D17 1 +6fe91 w R6 1 +6fea6 b B5 1 +6ff26 w C15 1 +6ff31 b E13 1 +6ff56 w Q5 1 +6ff76 b G16 1 +6ffa0 b N5 1 +6ffa6 w L3 1 +6ffe0 b G7 1 +70001 b S4 1 +70011 b M16 1 +70047 w M14 1 +70057 b M5 1 +70067 b H2 1 +70076 b S16 1 +700f7 w P6 R3 2 +70106 w P4 1 +70117 b C8 1 +70120 w N12 M14 2 +70130 w B14 1 +70136 b B15 1 +70146 w S3 Q18 2 +701b1 b D6 1 +701c6 b T12 1 +701d7 b P15 1 +701e7 b F17 1 +701f6 b P2 1 +70220 w P12 1 +70257 b D7 1 +70267 w D15 1 +70281 b Q8 1 +70281 w D13 F12 2 +702a7 b P16 1 +702c7 b O15 1 +702e6 w N6 1 +702f7 w P7 1 +703b7 w B17 1 +703c6 b C2 1 +703d0 w B5 1 +703d7 b E12 1 +70400 w H14 1 +70406 w S17 1 +70417 w R2 1 +70430 w E7 1 +70446 b B17 1 +70456 b G4 1 +704c7 b P4 1 +704e0 b B3 1 +70527 b C6 F4 B5 3 +70530 w Q14 1 +70556 b Q11 1 +70560 b P7 1 +70587 b R18 1 +70590 w C14 1 +705d1 b G2 1 +705f7 w Q18 S16 2 +70646 w P5 Q5 2 +70666 w R15 1 +70691 b C4 1 +706a0 b O16 1 +706e7 w D16 1 +70717 b Q16 1 +70736 w F17 E16 2 +70771 b O16 1 +70786 w H4 1 +707a1 w E3 1 +707c0 b D8 1 +707e1 w S17 1 +70800 b N2 1 +70807 b S11 1 +70816 b O3 N4 2 +70876 w E16 1 +70896 b C3 1 +70897 w G17 1 +70906 w B11 1 +70931 b D2 1 +70940 w F12 1 +70986 b R7 1 +70987 w D13 1 +709b7 b F3 1 +709d0 w O13 1 +709d1 w C6 1 +709d7 b C5 1 +709e0 w O17 1 +709e7 b C13 1 +709f1 b S16 1 +70a16 w G17 1 +70a40 w F14 1 +70a60 b P17 1 +70a90 b O2 1 +70a91 b Q7 1 +70ac0 w S15 1 +70ae1 b O16 1 +70b11 w C16 1 +70b67 b D12 1 +70bc0 w B13 1 +70be6 b R4 1 +70c00 w M16 1 +70c06 w C14 1 +70c07 b J2 1 +70c31 b R9 R7 2 +70c31 w P3 P2 Q4 3 +70c37 w H16 1 +70c56 b R7 P2 2 +70cb7 b E9 1 +70d20 w O2 1 +70d21 b G17 1 +70d46 w D4 1 +70d51 w R14 1 +70d57 w M4 1 +70d71 b C4 1 +70da1 w G17 1 +70db6 b B8 1 +70dd7 b G16 1 +70e06 b O5 1 +70e20 w C7 C17 2 +70e46 b R2 1 +70e71 b O4 1 +70ea6 w R14 Q15 2 +70eb0 b Q11 1 +70ef6 b Q6 1 +70f17 b D13 1 +70f80 b M4 1 +70f80 w G15 1 +70fb7 w Q17 1 +70fc7 b B15 1 +70fe6 b E7 1 +71006 b O7 1 +71006 w C16 1 +71020 w D6 1 +71097 w R3 Q6 R6 M4 O5 5 +710c7 w G4 1 +710d0 b L4 1 +710e0 b D5 1 +710e0 w G5 1 +71111 w P3 J4 2 +71126 w R3 1 +71161 b R18 1 +711a7 w D4 1 +711b6 b P15 1 +711d6 w R8 1 +71200 w R17 1 +71230 b Q8 1 +71236 w H17 1 +71240 b F16 1 +71251 w D6 1 +71256 b P7 1 +71266 b R5 P6 2 +71270 w C5 1 +71287 b Q3 1 +712a7 b R1 1 +712b7 w E14 1 +712c6 b P18 1 +71300 b H3 1 +71301 b O17 Q16 2 +71301 w C18 1 +71360 b F1 1 +71397 w S3 1 +713a6 w C17 1 +713e6 w R15 1 +71401 w O17 O18 2 +71406 w C6 1 +71491 b E17 1 +714a7 w E13 1 +714c1 b D2 1 +714e7 w S5 1 +71540 b T6 1 +71546 b S5 1 +71556 b C18 1 +71597 w Q17 1 +715c6 w L17 1 +71610 w E8 1 +71620 w Q15 1 +71667 w P3 1 +71680 w P2 1 +71687 w R17 1 +71691 w D7 B6 2 +716a6 w D17 1 +716a7 w Q6 1 +71737 b R17 Q16 P16 O15 O16 5 +71737 w R16 1 +71741 b G3 1 +71746 b E15 1 +71771 b R15 1 +71776 w O5 1 +717d0 b F18 1 +717e0 w B15 C17 F16 F17 4 +717e6 b F15 C14 2 +71801 w D2 1 +71817 w Q14 1 +71827 b T5 1 +71877 b B14 1 +71881 w C16 1 +71886 b O5 1 +71897 b F16 E15 2 +718e7 b D18 1 +71901 b N16 1 +71901 w E5 1 +71951 b Q14 1 +719b1 b B2 1 +719b6 w C17 1 +71a00 b C8 1 +71a17 b R3 M4 2 +71a50 b D15 1 +71a57 w E17 F17 F16 E16 4 +71a87 b O2 1 +71a90 w J4 1 +71a97 b O14 S7 2 +71a97 w Q5 1 +71ad7 w E17 1 +71b01 b D13 1 +71b37 w S7 1 +71b56 b Q18 1 +71b67 w G3 1 +71ba7 w O18 1 +71bc0 b B16 1 +71bd7 b R3 1 +71c77 b C8 1 +71c87 b F5 1 +71c97 w C5 1 +71ca0 b N17 1 +71cd7 b M17 1 +71ce1 w O4 1 +71d01 w Q18 1 +71d27 b R5 Q4 2 +71d41 b D3 1 +71d41 w C3 D4 D5 E6 D6 5 +71d46 w D6 1 +71d50 b Q13 1 +71d51 w O17 1 +71d87 b G4 1 +71da0 w R14 1 +71dd1 w F2 1 +71dd6 b Q16 1 +71e36 b N4 1 +71e57 w B9 1 +71eb0 w D5 1 +71eb7 b N18 1 +71ec1 w O17 1 +71ee0 b E6 1 +71f31 b E4 1 +71f47 w B18 1 +71f66 b O4 1 +71fc1 w O4 1 +71fd7 b S7 P8 2 +72016 b S3 1 +72040 b R17 1 +72041 b O12 1 +72060 w B1 1 +72066 b B14 1 +72080 b Q7 1 +72081 w A18 1 +72096 w S5 1 +720a0 b N16 1 +720a6 b S15 1 +720d0 b F13 1 +720d6 w Q5 1 +72121 b R5 1 +72130 w O16 R15 P14 R12 4 +72181 b O14 N13 2 +72187 b B12 1 +72196 w C17 1 +721b0 w F17 1 +721c6 b B4 1 +721e0 b L3 1 +721f0 b D8 1 +72241 b F14 1 +72246 w C3 1 +72257 w R5 1 +72271 w R2 1 +722a0 w S17 1 +722a1 b E7 1 +722e6 w O18 1 +722f7 w P7 Q6 2 +72327 w D6 1 +72330 w P2 1 +72337 w C16 1 +72370 w D5 1 +72371 w R17 Q17 R14 3 +72377 w P2 1 +723a6 b Q2 1 +723e0 w R15 1 +72417 w C15 1 +72447 b Q6 1 +72461 b C5 1 +72471 b R18 1 +724a1 b P4 1 +724a6 w C4 1 +724f1 b D8 1 +72501 b Q5 1 +72531 b R5 1 +72536 b D15 1 +725b1 w B1 1 +725c1 w P6 1 +725e1 b E6 1 +725e6 b F16 1 +725e7 w E15 1 +725f6 w C15 C17 2 +72607 b O17 1 +72647 b A5 1 +72671 b E18 1 +72681 b S5 1 +726c1 w B3 1 +726c7 b P3 1 +726d6 w P3 1 +726d7 w O3 1 +726e0 w Q6 1 +72710 w D3 1 +72720 b O3 1 +72720 w O4 O3 N3 Q6 R8 Q8 Q9 7 +72760 b R14 1 +727c6 w C17 1 +72811 w Q5 1 +72820 w Q6 1 +72840 w N4 1 +72876 w B15 1 +72886 w E3 1 +728d7 b D6 1 +728f0 w E16 O5 Q6 3 +728f6 b O4 1 +72917 b S7 1 +72976 b L2 1 +729a0 b F15 1 +729e1 b E4 1 +72a26 b G5 1 +72a57 w L4 O17 2 +72a67 w O4 1 +72a77 b G4 R17 2 +72a86 b O4 1 +72a90 b C17 1 +72aa7 b R2 1 +72ab7 b C6 F3 D6 F4 D7 G4 C7 G3 C9 J3 10 +72ac1 b B9 1 +72ad6 w E4 1 +72b06 b H18 1 +72b41 w E14 1 +72b57 b B17 E17 F16 G17 G18 5 +72b70 w F16 1 +72be7 b O8 1 +72c50 b B6 1 +72c51 w Q17 1 +72c60 w Q5 1 +72c77 b Q4 1 +72cc7 b P17 1 +72ce0 w C17 1 +72d10 b R18 1 +72d36 w F14 1 +72da0 b E3 1 +72da0 w C9 1 +72de0 b N3 1 +72de1 w C15 1 +72e07 b E16 E15 D13 C17 4 +72e07 w D15 C17 2 +72e10 w Q3 1 +72e27 b Q9 1 +72e30 b Q3 1 +72e66 w N15 1 +72e91 w O12 1 +72e96 b R11 1 +72ea6 w C18 1 +72eb7 b F2 1 +72ed0 b Q8 1 +72ee1 b Q3 1 +72ef7 b R3 1 +72f07 w B11 1 +72f27 b S17 1 +72f31 w N3 1 +72f37 b O5 1 +72f57 w B6 1 +72f61 b Q14 1 +72f87 w Q14 1 +72f97 b C14 1 +72fa6 w B12 1 +72fb0 w O17 1 +72fb7 w D2 1 +72ff6 b S3 1 +73007 w O4 1 +73026 w E14 1 +730a1 b S3 1 +730c0 b O18 1 +730c7 b S7 1 +730f0 b Q12 1 +730f1 b Q6 1 +73136 w C2 1 +73141 b C16 1 +73150 w T14 1 +731b7 b C14 1 +731c6 b C6 D5 2 +731e6 w O6 1 +731f7 w Q14 1 +73217 w Q18 1 +73241 b R2 1 +73251 w C18 1 +73280 b E2 1 +73296 b G16 1 +732b6 w D7 1 +732c7 b E5 1 +73331 w S14 1 +73351 b S15 1 +73370 b Q13 T5 2 +73377 b C7 1 +733a1 w F6 1 +73446 w R2 1 +73470 w R11 1 +73476 w R13 1 +734f1 b G6 1 +73506 w L18 1 +73516 w C4 1 +73517 w H16 1 +73541 b S16 1 +73551 w Q5 1 +73560 b G16 C16 2 +73560 w B5 1 +73567 w E5 1 +73570 b R7 1 +73581 w P15 1 +735c1 b M14 1 +735d1 w O17 1 +735d6 w S2 1 +735f7 w Q14 1 +73607 w G18 1 +73611 w B6 1 +73631 w D16 1 +73651 w B3 1 +73671 b C14 1 +736b7 w R12 1 +736e1 w P6 1 +736f1 w R3 1 +73701 b P5 1 +73706 b R15 1 +73711 b C3 1 +73721 b O15 1 +73731 b B5 1 +73757 b P3 1 +73777 w P18 1 +737a0 b J4 1 +737c6 b J17 1 +737e0 w B16 1 +73850 w C12 1 +73891 w L16 E2 2 +73896 w D4 1 +738a0 w D12 1 +738a1 b R2 1 +738c7 b S16 1 +738d0 b F3 1 +738e0 w R17 1 +738f1 b P14 1 +73910 b P2 P4 2 +73946 w O17 1 +73990 b G5 1 +739a6 b D15 E16 2 +739e0 b S3 1 +739e0 w J16 1 +73a31 b B6 1 +73ac1 w T18 1 +73b16 b N14 1 +73b57 b G3 1 +73b70 b Q17 1 +73ba0 b E13 1 +73bb1 b O3 1 +73bb7 b R4 1 +73be6 b B14 1 +73bf6 b B17 1 +73c26 b E2 1 +73c57 w S4 R3 R4 M3 4 +73c60 w C12 1 +73c77 w G13 1 +73c96 w D15 1 +73cb1 b D16 1 +73cd0 b Q16 1 +73cd1 w C6 1 +73ce6 w P5 1 +73d36 b E7 1 +73d46 w F4 1 +73d47 w P7 1 +73d70 b Q16 1 +73d70 w F6 1 +73d71 w B7 1 +73dc0 w R5 R2 2 +73dd7 w R5 1 +73e06 w D3 D5 2 +73e07 w P7 1 +73e31 b R16 R17 2 +73e40 b C17 1 +73e40 w G3 1 +73e50 w Q5 R7 2 +73e96 w R17 1 +73ea6 w C11 1 +73f17 w B4 1 +73f26 w Q5 1 +73f46 b F5 1 +73f57 b Q15 1 +73f57 w E4 1 +73f61 w Q16 R17 2 +74017 w P3 F2 2 +74037 b Q4 1 +74040 w G4 1 +74056 w G2 1 +74066 b O6 1 +74071 b O4 E6 2 +74071 w N4 O4 2 +74087 b O17 1 +740e7 w O18 1 +740f1 w H18 1 +74100 b T16 1 +74146 w N3 1 +74157 w O5 1 +74180 w E17 1 +74181 w F3 1 +74187 w P3 1 +74196 w O5 1 +741f0 b M16 1 +74220 w G17 E18 2 +74226 w R14 1 +74227 b B17 1 +74237 w P2 1 +74271 b S16 R17 2 +74277 b B3 1 +742a0 b E14 1 +742b7 w S5 1 +742c0 w M15 1 +742c7 b C6 1 +742e7 b P2 1 +74317 w C18 1 +74347 w R17 1 +74351 b N4 1 +74366 w C16 D15 D11 3 +743a0 w O7 1 +743d0 b P7 1 +74410 b E18 1 +74416 w S5 1 +74427 b F18 1 +74436 b D5 1 +74437 b N6 1 +74437 w R19 1 +74447 w S13 1 +74466 w P15 1 +744a7 w E4 F5 2 +744b1 b E5 1 +744e0 w E5 1 +744e7 w E15 1 +74511 b Q16 1 +74536 b S4 1 +74540 b A4 1 +74556 w B5 B2 2 +74571 w A3 1 +745a6 b C2 1 +745b0 b H17 1 +745b1 w C14 1 +745d0 b G4 1 +74601 b D13 1 +74617 b E4 1 +74621 w B6 1 +74631 w D6 1 +74660 w D13 1 +74681 w H17 1 +74696 w S13 1 +746a1 b D7 1 +746b6 w C15 1 +746c0 w L4 1 +746c6 b N15 1 +746e1 b H17 1 +746e6 b D7 1 +746f1 w S4 1 +74710 b C8 1 +74721 b O18 1 +74721 w S14 1 +74727 b Q16 1 +747a0 b R14 1 +747b6 b D15 1 +747e6 b B7 1 +74831 w C12 1 +74847 b Q13 1 +74861 b O18 1 +748b6 w Q14 1 +748d1 b B3 1 +748e1 b E13 1 +74940 b E5 1 +74966 b C18 1 +74981 b Q4 1 +749a7 w H17 1 +749d6 b F7 1 +74a00 b B4 1 +74a01 w H17 1 +74a11 w D13 1 +74a20 b D12 1 +74a57 w F18 1 +74a60 w G6 1 +74a77 b G17 1 +74a80 b Q6 R6 R7 O4 M3 M4 L4 7 +74a80 w R6 1 +74ac0 w P5 1 +74ad0 w Q15 1 +74ae6 b P2 S2 2 +74b10 w C2 1 +74b20 w C15 F3 2 +74b57 b R13 1 +74b67 w M17 1 +74b77 w B17 1 +74b81 w S17 1 +74bb0 w C17 1 +74bb7 w Q12 1 +74bd1 b B5 1 +74c26 b P5 1 +74c60 w S6 1 +74c71 b N7 1 +74c71 w O8 1 +74c86 b E5 1 +74c91 b S4 1 +74c97 b M3 1 +74cc7 b O16 1 +74ce0 b C5 1 +74d07 w R18 1 +74d10 b O8 1 +74d20 b Q5 1 +74d30 w E6 1 +74d31 b Q13 1 +74d40 b E17 1 +74d66 b G6 1 +74da1 w F15 1 +74dc1 b S4 1 +74e10 b Q14 1 +74e30 w D3 1 +74e36 b C4 1 +74e37 w D7 1 +74e40 w F5 1 +74e61 w Q16 1 +74e76 b S5 1 +74e80 b B15 1 +74e86 b C16 1 +74ea7 b Q14 1 +74ee7 b S18 1 +74f20 w C4 1 +74fc7 w A17 1 +74fd7 w P3 1 +75007 w D7 Q7 2 +75031 b E2 1 +75050 b C4 1 +75091 b H4 1 +750b7 b P14 1 +750b7 w P17 1 +750f1 b D12 1 +75120 b S5 1 +75147 b S2 1 +75150 b R14 1 +75170 b R16 1 +75177 b E4 1 +751b6 w E17 F15 2 +75240 b Q8 1 +75251 w J16 1 +75267 w R17 S15 Q13 3 +75271 w E17 1 +752f6 w B14 1 +752f7 b C16 1 +75310 w G4 1 +75320 w O14 1 +75370 w M16 1 +753c6 b Q7 1 +753d1 w G17 1 +75406 w Q4 1 +75417 w F6 G7 2 +75446 w L3 1 +75457 b D6 1 +754f6 w Q15 1 +75510 b E6 1 +75560 b J3 1 +75596 w J3 1 +755b0 b G18 1 +755c7 w F2 1 +755d0 b Q6 1 +755e1 w N3 1 +75611 b Q14 1 +75627 w S3 1 +75656 w N15 1 +756a7 w E17 1 +756e1 b F6 1 +756f7 b Q4 R3 2 +75710 w J3 1 +75717 b O8 1 +75741 b G18 1 +75751 w R12 1 +75760 b D13 P15 2 +75796 b N16 1 +757e1 w O17 1 +75801 b M4 1 +75826 b E15 1 +75841 b D15 C17 C13 F16 H16 5 +75841 w S17 E16 D17 3 +75861 b Q6 1 +75880 w G16 1 +758b1 w Q5 P4 2 +758c1 b B19 1 +758d6 b D14 1 +75926 b P5 1 +75927 b E6 1 +75931 w D16 1 +75937 w E4 1 +75950 b G5 1 +75976 w E15 D15 2 +75980 b B2 1 +759c6 w O2 1 +75a21 w S2 1 +75a27 b E3 1 +75a77 b O6 1 +75a80 b R12 1 +75a90 w E5 1 +75ab6 w D16 1 +75ae7 w R18 1 +75b36 b D14 1 +75b40 b C4 D3 2 +75b80 b F16 D15 2 +75b91 w C7 1 +75bb1 b B15 1 +75bc6 b B17 1 +75bd1 w T18 1 +75bd7 w E5 1 +75be7 w R18 1 +75c30 w F4 1 +75cb0 w P17 1 +75ce6 w Q19 1 +75d07 w S7 1 +75d17 w F6 G7 2 +75d26 w C15 1 +75d27 b S14 1 +75d40 w Q17 1 +75d47 b H15 1 +75d86 b S4 1 +75d97 w R4 1 +75da0 w G15 1 +75db0 b D12 1 +75db0 w S5 1 +75db6 w R18 1 +75dc6 b D8 1 +75dc6 w Q13 1 +75dd7 w D5 1 +75de6 b F3 1 +75de6 w B15 1 +75de7 w G5 1 +75e00 b P8 1 +75e26 b C14 1 +75e40 b O16 1 +75e50 b S5 Q5 2 +75e60 b H7 1 +75e66 b D15 1 +75e77 b C16 1 +75ea1 w S19 1 +75eb7 w D17 1 +75ec0 w S4 1 +75ee7 w Q17 1 +75f06 w F17 D16 2 +75f11 w G4 1 +75f26 b O8 1 +75f37 b F5 1 +75f46 b Q3 1 +75f50 b C16 1 +75f70 w P18 1 +75f81 b B14 1 +75fa7 w H6 1 +75fb0 b B15 C17 F16 F17 4 +75fc7 w P2 1 +76031 w R15 1 +76050 b H5 1 +76061 w D14 1 +760a6 w D13 1 +760c6 b D19 1 +760d1 b S4 1 +76101 b G13 1 +76106 w Q2 1 +76111 b C2 1 +76127 b M17 1 +76146 w P3 1 +76171 w N4 1 +761a7 b O3 1 +761b1 b F16 1 +761c7 b F17 1 +761e7 b O3 1 +76226 b D15 1 +76226 w D15 D14 2 +76237 w R4 1 +76241 b P18 1 +762b6 b F14 1 +762b7 b P19 1 +762e1 w B8 1 +76381 w E5 F3 2 +763a0 w B2 1 +763a7 w O4 1 +763d0 w R12 1 +763d7 w F5 1 +76426 b O2 1 +76427 w D18 1 +76430 w E4 1 +76440 b F16 1 +76481 w D4 D6 2 +764d6 w M2 1 +764e6 b N5 1 +764f1 w Q3 1 +76511 w D6 1 +76521 w J16 1 +76531 w C6 1 +76560 b P18 1 +76590 b R5 1 +76590 w F16 C17 2 +76591 w B7 1 +765b7 b R6 1 +765c0 b G14 1 +765c0 w C18 1 +765d6 w C6 1 +765e7 b Q2 1 +76601 b O3 1 +76617 w M16 1 +76627 w F17 1 +76647 b R7 1 +76690 w Q15 1 +76697 w S3 1 +766c0 b O14 1 +766d7 b D17 1 +766f0 b M6 1 +76701 w P5 1 +76706 w E16 1 +76727 b Q15 O15 2 +76747 w S5 1 +76750 b N15 1 +76751 w G4 1 +76781 b Q16 1 +76791 w E4 1 +767c6 w B14 1 +76816 b C8 1 +76817 b L3 1 +76821 w N3 1 +76830 b B15 1 +76831 w D4 1 +76861 b C7 1 +76877 b L16 1 +768a7 w C16 1 +768b0 b M6 1 +768b7 b P3 P2 Q4 3 +768c6 b R15 Q16 2 +768e7 w B5 1 +76917 b N4 1 +76950 w P7 1 +76990 b C17 1 +769a1 b O6 1 +769b6 b F16 F17 C15 E15 C13 D13 D12 7 +769b6 w C17 E17 F16 G16 G17 5 +769d6 b O4 N4 2 +769e6 b R14 1 +769f7 b H7 1 +76a06 b O4 1 +76a06 w G16 1 +76a61 w D18 1 +76a76 b O2 1 +76a80 b J16 1 +76a86 w Q7 1 +76ac7 w S13 1 +76ae7 w D4 1 +76af1 b B7 1 +76b00 b C5 1 +76b06 w D5 1 +76b41 b E3 1 +76b87 w N2 1 +76ba7 b S4 1 +76be1 w R18 R17 2 +76be7 b C3 1 +76bf0 w N14 1 +76c30 b E6 1 +76c46 w R5 R3 2 +76c77 b R2 1 +76c87 b P17 1 +76cb0 b N3 1 +76cb7 w B17 1 +76d00 w O4 1 +76dc6 b P15 1 +76e10 b N17 1 +76e11 b R5 1 +76e37 w R2 1 +76e47 b S5 1 +76e71 w E15 1 +76ea6 w G16 1 +76f50 b E2 1 +76fa1 b J4 1 +76fb1 b Q14 O16 B17 3 +76fb6 w S14 1 +76fc7 b H15 1 +76fd0 b E18 1 +76ff0 b C2 1 +76ff6 b F16 1 +77027 b R6 1 +77040 b M3 1 +77046 b C15 1 +77056 w Q17 Q15 2 +77071 b C14 1 +77096 w C3 1 +77097 b S17 1 +770d1 b D15 1 +770f6 b F4 1 +770f6 w D3 1 +77111 b R16 1 +77111 w R17 Q16 P16 O15 O16 5 +77131 w B16 1 +77170 w S4 Q4 2 +77191 w L3 1 +771c7 w B4 1 +771d0 w O7 1 +77206 w B16 1 +77217 b J17 1 +77226 w H14 1 +772b1 b F18 1 +772d1 w L16 1 +772f7 w R2 1 +77300 b E15 1 +77351 w F17 1 +77367 b F8 1 +77380 b C15 1 +77380 w M17 1 +77391 b S6 1 +773e0 b R3 P3 2 +773e6 w G5 1 +77471 b Q17 1 +774a1 b P17 1 +774b0 w R4 1 +77501 b F7 1 +77501 w H4 1 +77520 b D14 1 +77526 w C1 1 +77530 b R5 1 +77537 b D13 F14 2 +77547 w N5 1 +77581 w B5 1 +775b0 w G4 1 +775b7 b J4 1 +775d1 b D15 1 +77611 w S18 1 +77630 b F4 1 +77657 w G18 1 +77666 w Q4 1 +776a1 w F4 1 +77716 b Q6 1 +77720 b Q5 1 +77731 b E2 B6 2 +77750 b C14 1 +77767 b D11 1 +77776 b S17 1 +77780 b Q15 1 +77787 w F6 1 +777a1 w D15 1 +777d0 b P4 1 +777d7 b R17 R16 O17 3 +777f7 w R16 1 +77847 w E11 1 +77850 w D7 1 +77851 w F2 1 +77876 b G3 E5 2 +778c7 b E3 F3 E4 D5 4 +778c7 w D4 1 +778e6 w E3 F5 2 +778f1 b Q18 1 +77910 b R16 Q16 R13 P15 R17 5 +77916 b Q7 1 +77940 w D6 R18 2 +77946 w R15 1 +77960 b F17 1 +779b7 w D3 1 +779d1 w D18 1 +779f7 w H14 1 +77a86 b P6 1 +77aa6 b S7 1 +77ac6 w Q5 1 +77ad1 w F15 1 +77af6 b Q3 1 +77b20 b D13 1 +77b21 w N16 O18 2 +77b56 w C6 1 +77b87 b B14 1 +77ba7 w F5 1 +77bd1 b S3 D6 2 +77be6 b Q17 1 +77bf7 w Q3 1 +77c11 w F2 1 +77c17 b R15 1 +77c27 b P3 1 +77c41 b B17 1 +77c46 b Q14 1 +77c60 b H17 1 +77c80 b P15 1 +77ce7 b C15 1 +77ce7 w C5 1 +77d07 w C4 1 +77d36 b N16 1 +77d56 w C3 1 +77d66 b D5 1 +77d67 w R17 1 +77d87 w D8 1 +77da1 w F17 1 +77db6 b D7 1 +77dc1 w O18 1 +77dd7 w E18 1 +77e11 b C16 1 +77e20 b C13 F17 2 +77e21 b B16 1 +77e27 b P5 1 +77e30 b G4 1 +77e40 w M16 S6 2 +77e50 b D14 1 +77e67 w J17 G17 2 +77e70 b N15 1 +77e76 w F16 1 +77e97 w D18 1 +77ee7 w F3 1 +77ef6 b R4 1 +77ef6 w C4 1 +77f11 w O5 1 +77f37 w O13 1 +77f47 b E3 1 +77f47 w G3 1 +77f51 w P17 1 +77f61 b C4 1 +77f76 b O17 1 +77f90 b O16 O17 2 +77fd0 w E16 1 +78007 b B13 1 +78036 b R6 1 +78046 b G16 1 +78061 w S3 1 +78087 w P17 1 +780a7 w R15 1 +780d0 w R17 1 +780e6 b D6 1 +780f7 w D17 1 +78146 w F17 1 +78150 w N18 1 +78166 b S5 1 +781e0 b B2 1 +781e1 w C17 1 +781f6 b F17 1 +78227 w R3 1 +78237 w F16 1 +78246 b P18 1 +78250 b Q14 P14 2 +782b7 w S15 1 +782e0 w C17 1 +782f0 w D5 1 +78321 b R7 1 +78340 w E18 1 +78341 b S5 1 +78361 w S5 1 +78391 b R2 1 +783c0 b M15 1 +783d0 w F4 F5 2 +78400 w P7 1 +78431 w P13 1 +78477 w D2 1 +78480 b C4 S17 2 +784a7 w C18 1 +784b6 w R3 1 +784c6 b F13 1 +784d0 b E2 1 +784e7 w B3 1 +78501 w D4 D6 2 +78521 b P3 1 +78560 w P5 1 +78577 w D3 1 +78580 b Q16 1 +78597 b R3 1 +785b6 b S15 1 +785c1 w E19 1 +785e1 b R18 1 +785f7 b G6 1 +78606 w E15 1 +78607 w D8 1 +78631 w E12 1 +78636 b D13 1 +78650 w E3 B3 2 +78670 w H4 1 +78671 b E18 1 +786a0 b O4 N4 2 +786a6 b B5 1 +786f0 b C13 1 +78711 w O16 1 +78731 b Q6 1 +78746 w F4 1 +78756 w F18 1 +78791 b Q17 1 +787b1 w E4 1 +787e0 w E16 1 +78816 w Q17 1 +78826 w R3 1 +78861 w B3 1 +78870 b C6 1 +78871 w F16 1 +788b7 w R15 1 +78900 w M4 1 +78956 b O15 1 +78970 b C16 1 +789a6 w M17 1 +789e0 w R7 N13 2 +78a01 b O4 S3 2 +78a07 b E15 1 +78a16 w C18 1 +78a36 w O16 1 +78a41 b Q14 R13 2 +78a70 b E17 1 +78a71 b F16 1 +78ab0 b B14 1 +78ab1 w R5 1 +78ac6 b Q2 1 +78ae0 b C16 1 +78b61 b R17 R18 2 +78b71 b M5 1 +78b80 b F6 1 +78ba6 w R2 1 +78bb0 w P8 1 +78bb6 w R3 1 +78bc7 b P16 O16 2 +78be6 w Q6 1 +78c10 w C7 1 +78c17 w N3 1 +78c20 b R12 1 +78c26 w P3 1 +78c40 w E2 1 +78c67 b G4 H6 2 +78c87 b P13 1 +78c91 w R9 1 +78cb0 b R14 1 +78cc7 w N2 1 +78cd1 w B5 1 +78d07 b P14 1 +78d41 w J17 1 +78d66 b F4 1 +78d80 b P18 1 +78dd6 b M18 1 +78dd6 w Q13 1 +78de0 b R19 1 +78df6 w Q6 1 +78e50 b C16 1 +78e61 w D6 1 +78e81 b M2 1 +78ec6 b E3 1 +78ed7 b Q6 1 +78ee7 b P4 1 +78ef0 w P6 P11 2 +78f16 b B6 1 +78f26 w D6 1 +78f31 w J15 C16 2 +78f36 b B15 1 +78f46 w O16 1 +78f56 w C9 1 +78f90 w E15 1 +78fb0 b Q5 1 +78fb6 b E5 1 +78fb6 w S16 1 +79081 b B15 1 +790d7 w D6 1 +790f0 w B18 1 +79100 b D12 1 +79126 b F5 1 +79167 w O14 1 +79197 b B14 1 +791c1 b F4 1 +791c6 w R7 1 +791f0 b D3 1 +79226 b Q4 1 +79227 b J18 1 +79236 w S3 1 +79247 b P8 1 +79277 b R17 1 +79300 b D4 1 +79311 w S14 1 +793d1 b P15 1 +79400 w G4 1 +79456 b N18 1 +79466 b Q5 1 +79471 b F3 1 +79480 b C15 1 +79490 b R15 1 +794a0 w E4 1 +794d7 w B5 1 +794f0 w F17 1 +794f7 b Q4 1 +79501 w N6 1 +79507 b D16 1 +79511 w S17 1 +79546 w P13 1 +79551 b D2 1 +79557 b R12 1 +79557 w B9 1 +79561 w F4 1 +79567 b M17 1 +79591 w S13 T2 2 +795b1 b R16 1 +795e1 b C18 1 +79600 w Q14 Q13 2 +79606 b O17 1 +79607 b F18 1 +79656 b S14 1 +79661 b D18 C17 D17 C12 4 +79670 w D12 1 +796a7 w B17 1 +796e7 b O17 1 +79707 b C9 1 +79727 w B11 1 +79747 w N3 1 +79756 w O17 1 +79757 w R2 R9 2 +79760 w C17 1 +79770 w P14 1 +797c1 b N18 1 +797c6 w R13 1 +797c7 w S6 1 +79801 w P17 1 +79830 w E6 1 +79851 b F4 1 +79856 w M4 1 +79876 b N4 1 +79890 b Q16 1 +798b7 b F4 1 +798d7 b Q13 1 +798f6 w O5 1 +79926 b C2 1 +79926 w B15 1 +79947 b F17 1 +79980 w Q6 1 +79987 w Q7 1 +799a6 w F13 1 +799c1 b F9 1 +799f1 b G4 1 +79a66 w A3 1 +79a77 w S15 1 +79a96 w D14 1 +79aa7 b N18 1 +79ab0 w T4 1 +79ab1 w C16 1 +79ab6 b R15 1 +79af7 w J4 1 +79b16 w B17 1 +79b47 w F13 1 +79b76 w E5 D4 2 +79b77 w C17 1 +79bc0 w Q14 1 +79bd7 w R1 1 +79c31 w F17 1 +79c47 w D14 1 +79c97 w D6 C2 2 +79cd7 b A3 1 +79d00 w O13 1 +79d21 w N8 1 +79d51 b M4 1 +79d86 b G3 1 +79dc1 w P8 1 +79de6 b R18 1 +79e27 b C6 1 +79e36 w C17 R3 2 +79e77 w S15 1 +79e80 b S5 1 +79ec1 b F16 1 +79f21 b P5 1 +79f36 b D2 1 +79f67 w R8 1 +79fa1 w E18 1 +79fc0 b Q5 1 +79fe0 w L3 1 +79ff6 b B14 1 +7a057 w B16 1 +7a060 b R18 1 +7a076 w C5 1 +7a077 b B16 1 +7a086 b Q14 1 +7a087 b S16 1 +7a087 w D3 1 +7a0f1 w G5 1 +7a140 w R6 1 +7a147 w Q17 Q15 2 +7a151 w C13 1 +7a171 w T15 1 +7a176 b S3 Q5 2 +7a187 b B5 1 +7a1e1 b F5 G4 E3 3 +7a207 w S17 1 +7a236 w E14 1 +7a257 b E2 1 +7a257 w E14 1 +7a266 b D19 1 +7a267 w D14 1 +7a281 w S4 P5 O3 3 +7a286 w P3 1 +7a290 b N18 1 +7a2a7 b F2 1 +7a2b6 b S4 1 +7a2c6 w E7 1 +7a2e1 w E18 1 +7a366 w G3 1 +7a390 w F15 D14 2 +7a3a1 b D6 1 +7a4a6 w Q3 P4 L4 3 +7a4e7 b B3 1 +7a506 b D8 1 +7a510 b P16 1 +7a590 w R3 1 +7a5a6 w B5 1 +7a5a7 b R6 1 +7a5c7 b F15 1 +7a5f1 b A7 1 +7a5f1 w O18 1 +7a610 b H17 1 +7a666 w C17 1 +7a680 b D16 1 +7a6e0 w N13 1 +7a6e7 b C14 1 +7a6f6 w P16 1 +7a700 w S7 1 +7a731 w S7 P8 2 +7a750 w R6 1 +7a751 b E17 1 +7a7b1 b P5 1 +7a7e7 b C17 C16 F17 3 +7a820 w F2 1 +7a827 w B13 1 +7a841 b S5 1 +7a867 b C3 1 +7a880 b C8 1 +7a8b7 w R14 1 +7a8d1 b R14 P18 2 +7a8d6 w B3 1 +7a8e6 w C16 1 +7a921 b F16 1 +7a940 b P7 1 +7a940 w N17 R14 2 +7a957 w D16 1 +7a971 b J17 E14 2 +7a9a6 b G17 1 +7a9b7 b D12 1 +7a9d1 w P13 1 +7aa21 b Q5 R3 R7 O4 M4 5 +7aa21 w P4 Q3 2 +7aa27 b D17 1 +7aa30 b R8 1 +7aa47 b F17 1 +7aa66 w H4 1 +7aa70 b S2 1 +7aa80 w D2 1 +7aac7 b S4 1 +7aad1 w P18 1 +7aae0 w N15 1 +7ab06 b D6 1 +7ab07 w F3 B5 2 +7ab91 b D14 1 +7aba6 w E5 1 +7abc7 w D13 1 +7abe0 b D8 1 +7abf1 b C3 1 +7ac10 b D13 1 +7ac67 w P3 1 +7ac90 w R17 1 +7ac91 w D16 1 +7ac96 b E6 1 +7ac96 w R4 1 +7ace1 w P6 1 +7ad06 b E5 1 +7ad16 w P15 1 +7ad60 b O5 1 +7ada0 b R17 1 +7ada0 w D5 1 +7ada6 w D17 E18 2 +7ada7 b N4 M6 2 +7adb7 w P15 1 +7adc0 b N3 1 +7add7 b F17 D15 2 +7ade1 b F14 1 +7adf0 w Q2 1 +7ae01 b P6 R3 2 +7ae30 w R3 1 +7ae51 b C16 1 +7ae80 w S4 1 +7aeb0 w Q5 1 +7aec0 b P3 1 +7aec7 w R4 1 +7aed1 b D18 1 +7af00 b C3 1 +7af07 w O4 1 +7af20 w L2 1 +7af31 b P14 1 +7af36 w B16 1 +7af86 b C2 1 +7af87 b O5 1 +7afc0 b D2 1 +7afe1 w Q18 1 +7aff6 b R11 P2 2 +7b020 b R5 R2 2 +7b030 b D6 1 +7b040 b O12 1 +7b066 w R3 1 +7b0b1 w R3 1 +7b0c7 b C6 1 +7b110 b R5 1 +7b130 b F14 1 +7b167 w B17 1 +7b187 b J16 1 +7b1a6 w F17 1 +7b1d0 w P16 B3 2 +7b1d7 w O2 1 +7b1e7 w F2 1 +7b1f7 b Q13 O12 2 +7b200 b E15 1 +7b207 w R18 1 +7b227 w E7 1 +7b237 w O15 1 +7b247 b B18 1 +7b251 b P18 1 +7b276 w C2 1 +7b277 b R3 1 +7b287 w E4 1 +7b2b0 b Q13 1 +7b2c1 w B15 1 +7b2c6 w S6 1 +7b2c7 w C4 Q18 2 +7b2d0 b M14 1 +7b301 b Q3 1 +7b307 w C17 1 +7b361 b G14 D16 2 +7b366 b O16 1 +7b3a0 b Q17 1 +7b3a0 w S5 1 +7b3a1 w O5 1 +7b3c1 w B12 1 +7b3d6 b H5 1 +7b3f1 b O3 1 +7b3f7 b S17 1 +7b416 b D16 1 +7b416 w G4 1 +7b430 w F3 1 +7b456 b R2 1 +7b4b1 w B2 1 +7b541 b O16 N17 2 +7b580 w F5 1 +7b5b1 b S17 1 +7b5b1 w C2 1 +7b5d1 b O4 1 +7b601 b D18 1 +7b626 w H17 1 +7b636 w B4 1 +7b687 w R18 1 +7b697 b Q5 P4 2 +7b6b6 w B4 1 +7b6c7 b R18 1 +7b6f0 b Q6 S1 2 +7b6f0 w F3 1 +7b727 b A2 1 +7b747 b E7 1 +7b761 w P2 1 +7b786 b C9 R3 2 +7b791 w O5 1 +7b7e1 w C3 1 +7b826 b B15 1 +7b830 w C12 1 +7b857 w O16 1 +7b860 w P17 1 +7b867 b E18 1 +7b890 b S17 1 +7b8c0 b E3 1 +7b917 w D9 1 +7b966 b F4 1 +7b9a1 b E15 1 +7b9c7 b E5 1 +7ba16 w S6 1 +7ba71 w G16 1 +7ba77 w O2 1 +7bac6 b R13 1 +7bad0 b O14 1 +7baf6 w E18 1 +7bb10 w O19 1 +7bb30 w R11 1 +7bb76 b S17 1 +7bb86 w C7 1 +7bbb0 w R4 1 +7bbb6 b B15 1 +7bc00 b P13 1 +7bc07 w P15 1 +7bc77 w J18 1 +7bc90 b R3 1 +7bcd6 w S15 1 +7bcf0 w O16 1 +7bcf1 w C12 1 +7bd06 b L3 1 +7bd07 b B3 1 +7bd07 w G4 D4 2 +7bd66 w C18 1 +7bd70 w O7 1 +7bdc1 w R18 1 +7bdf0 w M14 1 +7bdf7 b O3 1 +7be00 w Q2 1 +7be10 b D15 1 +7be61 b O4 1 +7be61 w Q11 1 +7be87 w Q13 Q4 2 +7bea6 b B1 1 +7bea6 w S7 1 +7bec6 b P14 1 +7bef0 w O16 1 +7bf20 w P5 1 +7bf51 b E2 1 +7bf67 w G14 1 +7bf71 b R16 1 +7bf90 b C4 1 +7bfa1 w N5 1 +7bfb7 b R8 1 +7bfc6 b C2 1 +7bfc7 b C15 1 +7bfd6 w F7 1 +7bfe1 b S16 1 +7c047 b C2 1 +7c071 b C4 E4 2 +7c0b7 b Q7 1 +7c0e6 b B3 1 +7c0f0 b B17 1 +7c116 w Q5 1 +7c127 w G16 1 +7c157 w O6 1 +7c166 w R17 1 +7c170 w F17 1 +7c190 b O5 1 +7c1b7 b N3 1 +7c1c7 w Q4 1 +7c1e1 w S13 1 +7c1e7 w S15 1 +7c1f1 b O5 1 +7c250 b C17 1 +7c276 b R2 1 +7c287 w O2 1 +7c296 w O4 1 +7c297 w J2 1 +7c2d0 b C15 1 +7c2e6 w P15 Q16 2 +7c317 b R11 1 +7c3d6 b D18 1 +7c426 w N4 1 +7c476 w Q15 D18 2 +7c486 w H6 1 +7c4a7 b N17 1 +7c4c0 w L16 1 +7c540 w E18 1 +7c571 w C15 G17 2 +7c586 w R3 1 +7c5d0 w N17 1 +7c5f1 w R16 1 +7c621 b F14 1 +7c6a0 w C5 1 +7c6a1 b D9 1 +7c6a6 w O17 1 +7c6f7 w E3 1 +7c701 b J2 1 +7c706 b D7 1 +7c746 b D6 1 +7c747 w T4 1 +7c751 b S15 1 +7c756 w M15 1 +7c780 w O4 1 +7c840 b D11 1 +7c860 w P18 1 +7c871 b E16 1 +7c886 b F17 1 +7c8a6 b F5 1 +7c8c6 b P3 1 +7c8e6 b E16 1 +7c8e7 w N15 1 +7c8f1 b O3 1 +7c8f1 w O4 P5 2 +7c906 w P15 1 +7c956 b G15 1 +7c967 b R5 1 +7c970 w H4 1 +7c9d0 b R7 1 +7c9f7 b R16 1 +7ca07 w R17 1 +7ca20 w E5 1 +7ca36 b F4 1 +7ca97 w B17 1 +7caa0 w P7 1 +7caa6 w E13 1 +7cab0 b F3 1 +7cb30 w D4 1 +7cb31 b D16 1 +7cb37 w O15 1 +7cb46 w B5 1 +7cb47 b O17 1 +7cb47 w P15 1 +7cb60 b F13 1 +7cb90 w R17 1 +7cbb1 b R3 R2 2 +7cbd7 w O15 1 +7cc10 b R14 1 +7cc31 b F5 1 +7cc31 w R17 1 +7cc71 b B5 1 +7cc91 w Q14 1 +7cca0 w R16 1 +7ccc1 w D14 E13 2 +7ccd7 b E5 1 +7cd01 w Q14 1 +7cd21 b R14 1 +7cd76 b J4 1 +7cd87 b Q16 1 +7ce01 b S3 1 +7ce10 b H4 1 +7ce11 b Q6 1 +7ce16 w S15 1 +7ce26 b E2 1 +7ce37 w R17 Q14 R14 M16 O15 5 +7ce41 w D14 1 +7ce56 b Q16 1 +7ce91 w G4 D4 2 +7ce97 b G15 1 +7cea0 w B3 1 +7cec6 w M18 1 +7cef1 w D7 F6 2 +7cef6 b E4 1 +7cf01 b Q15 1 +7cf06 b O16 1 +7cf06 w E15 1 +7cf46 b S13 1 +7cf66 w S17 1 +7cf71 b B14 1 +7cf77 w Q14 1 +7cf80 b C5 1 +7cf87 w A3 1 +7cf90 w D16 1 +7cfa6 b D6 1 +7d000 w J18 1 +7d001 b Q2 R3 Q3 R8 4 +7d010 b S14 1 +7d020 b F15 1 +7d040 w Q16 1 +7d047 b L4 1 +7d071 w Q16 1 +7d096 b C15 1 +7d097 w S4 1 +7d0c0 w Q14 S14 2 +7d0c1 b B13 1 +7d0f0 w R2 1 +7d101 b G15 1 +7d107 w B13 1 +7d126 w E16 1 +7d127 w Q15 1 +7d140 w O14 1 +7d156 w H16 1 +7d176 w O3 1 +7d1a7 b D9 1 +7d1c1 w R17 R16 L17 M17 P18 Q17 6 +7d1d1 b S5 1 +7d210 w Q15 1 +7d211 b R4 1 +7d220 b G7 1 +7d241 b D2 1 +7d270 b D8 1 +7d277 w R17 1 +7d286 b D17 1 +7d300 w Q7 1 +7d306 w E5 1 +7d307 b R6 Q5 2 +7d360 w Q17 P3 S3 3 +7d380 b C9 1 +7d386 w P18 1 +7d387 b P2 1 +7d387 w C13 1 +7d3c1 b B12 1 +7d3d6 b R18 1 +7d3e1 w S16 1 +7d401 w Q17 P16 R17 3 +7d481 b S15 1 +7d4a6 w Q14 1 +7d4c1 b F2 1 +7d4c6 b D5 E4 2 +7d581 b P16 1 +7d597 b F5 1 +7d597 w D4 1 +7d5a1 w S16 1 +7d5c6 w S17 1 +7d5e1 b C2 1 +7d5e6 w R1 1 +7d621 b J6 1 +7d627 w P7 1 +7d641 b F4 1 +7d656 b E7 1 +7d677 w N15 O16 2 +7d687 b F15 1 +7d697 b E16 1 +7d6d0 b M3 1 +7d710 b G3 1 +7d726 w E4 1 +7d767 w D6 1 +7d790 b R8 1 +7d7c0 w C6 1 +7d7c1 w P4 O4 2 +7d7e6 b N4 1 +7d820 b P18 1 +7d827 w D14 1 +7d850 b D14 C14 2 +7d887 b B16 1 +7d8d0 b S17 1 +7d936 b C5 1 +7d950 w D7 1 +7d970 b M4 1 +7d981 w E16 C13 2 +7d991 b T4 1 +7d9a7 b D3 1 +7d9c1 b R16 1 +7d9d0 b S2 1 +7da01 w B14 1 +7da21 b D16 1 +7da40 b Q15 1 +7da67 w O17 1 +7da77 b N4 O6 2 +7da77 w D4 1 +7db11 b R6 P2 2 +7db87 b R12 1 +7dba1 w P8 1 +7dbc6 b P4 1 +7dbd1 b G16 1 +7dbd6 b C5 E6 2 +7dbd6 w Q5 1 +7dbd7 w C13 1 +7dbe1 b S17 1 +7dbf6 w C3 1 +7dc11 w S14 1 +7dc20 w E14 1 +7dc40 w F4 F2 2 +7dc41 b N16 1 +7dc71 b O5 1 +7dc87 b O4 1 +7dc90 b R17 1 +7dc97 b R18 1 +7dcb1 w F17 1 +7dd01 b Q19 1 +7dd06 w G15 1 +7dd07 w C8 1 +7dd56 w D12 1 +7dd81 w F15 G3 D3 3 +7dd87 w H2 1 +7dd91 b G17 1 +7ddc1 w S14 1 +7ddc6 w E15 1 +7ddd7 b R6 1 +7de91 w R4 1 +7dea7 w G5 F4 2 +7df01 b S4 1 +7df26 w E15 D16 2 +7df46 w F4 1 +7df97 w M3 1 +7dfc6 b D6 1 +7dfe0 w C13 1 +7e010 b D14 1 +7e0b6 w E15 1 +7e0d7 b F15 1 +7e106 w E4 1 +7e120 b D5 1 +7e161 b D11 1 +7e161 w C4 1 +7e191 w N2 1 +7e1b1 w E3 1 +7e1c6 w S11 1 +7e216 w D18 1 +7e220 b Q7 Q3 2 +7e261 w S16 1 +7e276 b D3 E4 J4 3 +7e2c0 w R14 1 +7e2c7 b Q16 R17 2 +7e2e0 w C2 1 +7e311 w D4 1 +7e341 b G18 1 +7e3a0 b B17 1 +7e3a6 b F17 D16 2 +7e3b1 w Q13 R17 2 +7e3c6 w O6 1 +7e3d7 b D6 1 +7e3e1 b S4 1 +7e3e6 b Q12 1 +7e401 w Q6 1 +7e406 b C5 B3 2 +7e406 w N4 1 +7e426 b F4 1 +7e427 b B2 1 +7e430 b G3 E5 G4 B6 4 +7e451 b F17 1 +7e451 w F13 1 +7e497 b D2 1 +7e4f1 w C17 1 +7e511 b F8 1 +7e511 w E3 1 +7e551 b B15 1 +7e561 w O17 1 +7e571 w C9 1 +7e576 w Q15 1 +7e5c0 w R5 1 +7e5d1 b C5 G2 2 +7e5f0 b D15 1 +7e637 b Q5 1 +7e651 w N3 1 +7e670 w E13 G16 2 +7e6e0 b O17 1 +7e6e6 b P3 1 +7e6f1 b A16 1 +7e707 w Q2 1 +7e711 w D11 1 +7e757 w F4 1 +7e770 b N12 1 +7e7e6 b P6 O3 2 +7e7e6 w B15 1 +7e807 b N15 1 +7e860 w Q2 1 +7e8c1 b C17 E14 F17 F16 4 +7e8c6 b R14 1 +7e8e0 w E4 1 +7e900 w E19 1 +7e926 w P13 1 +7e930 w H17 1 +7e947 w H4 1 +7e950 b C3 1 +7e967 w S4 1 +7e970 b C12 1 +7e9e0 b Q13 Q16 P14 3 +7e9e1 b S16 C17 G16 3 +7ea90 b B5 1 +7eaa0 b C5 1 +7eac6 w Q17 1 +7ead6 w P4 1 +7eaf0 b C2 1 +7eaf0 w S7 1 +7eb06 b C5 1 +7eb20 b C8 1 +7eb21 w B9 1 +7eb27 w B4 1 +7eb30 w S3 1 +7eb77 w D4 1 +7eb96 b B11 1 +7ebb7 w E5 1 +7ebc6 w C8 1 +7ebd1 b R14 1 +7ec37 w D7 1 +7ec80 b Q5 1 +7ec86 w S7 1 +7ecb6 b O18 1 +7ecc7 b F6 1 +7ecf1 w O3 O2 2 +7ed01 w H6 1 +7ed17 w E18 1 +7ed40 b D7 1 +7ed51 w B5 1 +7ed56 w P8 1 +7ed66 b E17 1 +7ed91 w L5 1 +7ede1 w R9 1 +7ee01 b H18 1 +7ee31 b H3 1 +7ee67 w F4 1 +7ee90 b M15 1 +7ee96 b B14 1 +7eea1 b G7 1 +7eec1 w L3 1 +7eee1 w H3 1 +7eef6 w B12 1 +7eef7 b Q5 C5 2 +7eef7 w C12 1 +7ef16 b B15 1 +7ef46 b D13 1 +7ef96 b P17 1 +7efc6 b H16 1 +7eff6 w H17 1 +7f000 w P5 1 +7f001 w C15 1 +7f007 b P6 1 +7f036 w Q8 1 +7f097 b L16 1 +7f0a0 b Q2 1 +7f0d1 b Q2 1 +7f0f0 b O16 1 +7f107 w N18 1 +7f111 w R17 1 +7f180 w P16 1 +7f190 b L17 1 +7f196 b P15 1 +7f1a0 b E5 1 +7f1b1 b E4 1 +7f1d0 w J17 1 +7f1d6 b C6 1 +7f210 w N17 1 +7f231 w E15 1 +7f240 w C18 1 +7f261 w G3 1 +7f266 w O4 1 +7f287 w G7 1 +7f2d7 w R4 1 +7f2e6 b D13 1 +7f300 b Q14 1 +7f360 w L16 1 +7f381 b R7 1 +7f390 w O15 1 +7f3b0 w G3 1 +7f3b6 w S14 1 +7f3b7 w B17 1 +7f3c0 w N5 1 +7f3e1 b Q9 1 +7f426 w Q17 1 +7f427 b D13 D14 2 +7f427 w D14 1 +7f431 w G18 1 +7f470 w P12 1 +7f4a0 w S4 1 +7f4d1 w R15 1 +7f556 w B6 Q3 2 +7f567 b Q2 1 +7f567 w R2 1 +7f577 w E16 1 +7f580 b D13 R15 2 +7f5b0 w D13 1 +7f5b7 w P3 1 +7f5d6 w C9 1 +7f5f6 b E5 1 +7f676 w R4 1 +7f687 w N4 M3 L3 3 +7f696 b P5 1 +7f6a0 w D5 C7 2 +7f6a1 b G16 1 +7f6a1 w D4 1 +7f6b1 w F3 1 +7f6b6 w F2 1 +7f6d1 b B16 1 +7f6d6 b R6 1 +7f6d7 w E7 1 +7f711 b F18 1 +7f756 b F16 1 +7f767 b S18 1 +7f786 w B13 1 +7f791 b F16 1 +7f7a0 b E7 1 +7f7e7 b Q18 1 +7f7f0 w R7 1 +7f810 w Q14 1 +7f847 w Q6 1 +7f850 w M3 E2 2 +7f880 b T4 1 +7f880 w D18 1 +7f887 w D14 1 +7f8a0 w P16 1 +7f8b1 b R3 1 +7f8d0 b C13 1 +7f8d7 w G14 1 +7f8f7 b S2 1 +7f900 b R2 1 +7f950 w F6 1 +7f951 b P2 1 +7f9b1 w G17 1 +7f9c6 w O15 1 +7f9f0 b D9 1 +7fa10 b S6 1 +7fa16 w G5 1 +7fa47 b S16 1 +7fa80 w P6 1 +7fa86 w R3 1 +7fa90 b N16 1 +7faf1 b E18 1 +7faf7 w D18 1 +7fb01 w Q15 Q14 2 +7fb61 b C4 1 +7fb67 w S16 1 +7fb70 b S11 1 +7fb71 b O5 1 +7fb77 w G15 1 +7fbc0 w F13 1 +7fbd7 w C14 1 +7fbf7 w Q4 1 +7fc11 b F6 1 +7fc30 w G4 1 +7fc51 w B4 1 +7fc60 w E7 1 +7fc66 b P7 1 +7fc80 b E5 1 +7fca1 b O15 1 +7fce0 w F3 1 +7fce7 b L3 1 +7fd00 b N16 1 +7fd17 w Q6 1 +7fd20 b E15 1 +7fd51 b B4 1 +7fd66 b Q14 1 +7fd97 w F15 1 +7fda6 w C18 1 +7fde6 w Q17 1 +7fe21 b N17 1 +7fe56 b G16 1 +7fe90 w C6 1 +7fe96 b D3 1 +7ff21 w H15 1 +7ff37 w S15 1 +7ff40 w D15 1 +7ff71 b D17 1 +7ff77 w N4 1 +7ffb0 w S17 1 +7ffd6 w C5 1 +7ffe6 b O4 1 +7ffe6 w O7 1 +80020 w N16 1 +80060 b E2 1 +80067 b R14 1 +80081 b Q6 1 +800c6 w R7 P2 2 +800f7 b D2 1 +80110 b Q13 1 +80131 b R18 1 +80167 b R17 1 +801a0 w A15 1 +801a7 b M16 1 +80206 b C17 1 +80236 w C15 1 +80256 w R12 1 +80261 w F15 1 +80287 b R9 R7 2 +802a6 b H15 1 +802a7 b C9 1 +802d0 b J2 1 +802d1 b P18 1 +802e0 w C14 D16 C16 3 +802e7 b J3 1 +80311 b G16 1 +80361 w N16 1 +803a0 w H3 Q7 2 +803a1 w C11 1 +803c1 w C14 1 +803c6 w O15 1 +80411 w J16 1 +80450 b B3 1 +804c1 b F4 1 +804e6 b P15 1 +804e7 b C6 1 +80516 b R17 1 +80520 w C5 1 +80547 w E16 1 +80577 w Q9 1 +80590 b R8 1 +805c6 b M18 1 +805e6 b N18 1 +805f0 w O15 1 +805f6 b G4 1 +80607 w R5 1 +80636 b H3 1 +80640 w Q12 1 +80657 w B3 1 +806b0 w Q4 1 +806d6 w P2 1 +806e1 w S1 1 +80701 b J4 1 +80701 w R13 1 +80756 b C1 1 +80756 w P3 S3 2 +807a0 w B6 1 +807c0 w D6 1 +807d1 b R3 N4 2 +80801 w S4 1 +80811 w S17 1 +80816 w B5 1 +80830 w S7 1 +80857 b C6 1 +80886 w Q14 1 +80896 b P15 1 +80897 w R13 S12 2 +808a6 w E5 1 +808b0 b P5 1 +808c7 w O15 1 +808e0 b Q8 1 +808f1 b S16 1 +808f7 w S7 1 +80917 w R15 1 +80931 b E18 1 +80941 b B5 1 +80961 b M2 1 +80970 w M18 1 +80986 w E4 1 +80990 w Q7 1 +809a0 b R11 1 +809c1 b E17 1 +809f7 b P5 R5 2 +80a06 b Q4 1 +80a31 b N17 1 +80a37 b R4 1 +80a37 w Q3 1 +80a40 b P5 1 +80a76 w P15 Q15 2 +80a77 b C4 1 +80aa0 w D3 1 +80aa6 w D17 1 +80ab0 b D8 1 +80ab6 b B4 1 +80ab7 b F3 1 +80ac1 b C4 1 +80ac7 b P19 1 +80b07 b J5 1 +80b31 b C14 D16 2 +80b40 w P4 1 +80b57 w O5 1 +80ba0 w O15 1 +80bd6 b O13 1 +80be1 w C13 1 +80c36 w R5 1 +80c46 b L2 1 +80c86 w D6 1 +80c91 w R14 1 +80ca0 w E16 1 +80cd1 w C14 1 +80cf1 w O7 1 +80d06 b N9 1 +80d16 w E4 1 +80d91 w Q18 1 +80d97 w F4 1 +80dd1 b H2 1 +80e10 b Q4 1 +80e10 w E16 1 +80e11 b Q6 1 +80e56 b R6 Q7 2 +80e70 w H15 1 +80e76 w Q7 1 +80e96 b E5 1 +80ec6 w C17 1 +80ed6 b O17 N16 2 +80ee1 w O5 1 +80ef7 w E5 E6 C3 C7 D17 5 +80f07 b C4 1 +80f17 b R7 1 +80f26 w D3 1 +80f36 b S5 1 +80f40 b R3 Q7 2 +80f40 w H17 B7 2 +80f80 b L17 1 +80f91 b C14 1 +80fb7 b Q6 1 +80fc1 w R4 1 +80fc6 b Q6 1 +80fc6 w E16 1 +80ff1 b O3 1 +80ff6 b D17 1 +81006 w B11 1 +81030 b F16 F18 2 +810b6 b G3 1 +81100 w D2 D4 2 +81111 w P6 1 +81146 w O3 Q4 2 +81186 w D17 1 +811a0 b R7 1 +811a6 w P17 1 +811d0 w F2 1 +811e6 b N15 1 +811e6 w O18 1 +81200 w R4 Q3 2 +81217 w S4 1 +81220 b O6 1 +81230 b E4 1 +81241 b R15 1 +81250 b B5 1 +81280 b F7 1 +81287 b D17 1 +81290 b Q13 1 +812a1 b D15 1 +812a7 w P14 1 +812c0 b B16 1 +812d0 w O16 1 +812f0 b Q14 1 +812f0 w D15 C15 2 +812f1 w F12 1 +81360 w Q4 1 +81361 b P2 1 +81371 b D14 1 +81390 b Q7 1 +813b0 b R13 1 +813c1 b B5 1 +813f6 w R6 1 +81440 b G15 1 +81466 b Q15 1 +814d1 w G18 1 +814e0 b G17 1 +814f1 b R19 1 +81540 b N3 1 +81571 b Q4 1 +81571 w P3 O3 P4 Q5 4 +815a6 b R13 1 +815b0 w C3 1 +815d7 w S16 1 +81610 w E7 1 +81620 b C11 1 +81631 w T5 1 +81640 w O2 1 +81647 w F4 1 +81667 b S15 1 +81667 w E4 1 +816a6 w S15 1 +816b6 b B17 1 +81707 w Q16 1 +81731 b O15 1 +81741 b G18 1 +81776 b D5 1 +81781 b O2 1 +817b1 w G5 1 +817c7 b S7 1 +817d0 b O16 1 +817d6 w E3 1 +817f6 b C17 1 +81811 w E6 1 +81840 b D13 1 +818b0 b R3 1 +818b6 b R17 1 +818e0 w N16 1 +81920 b O4 1 +81951 w C2 1 +81971 b Q16 1 +81997 w Q18 1 +819b1 w N14 1 +819b7 w E5 1 +819c0 b L17 1 +819d0 w P4 1 +819f1 b M6 1 +81a80 b E4 1 +81a81 w P2 1 +81a97 b Q4 1 +81aa6 w Q7 1 +81ab1 b S5 1 +81b01 b P12 1 +81b10 b D13 1 +81b46 b Q13 1 +81b70 w O13 1 +81b91 b R18 1 +81b96 w C16 C15 B15 3 +81ba7 b P17 1 +81be0 w R18 1 +81bf0 w Q7 1 +81c06 w S11 1 +81c16 b C12 1 +81c51 b B3 1 +81c56 w R17 1 +81c57 b F3 F2 2 +81c76 w R7 1 +81c90 w D6 C3 2 +81ca0 b P4 P3 2 +81ca6 w C3 1 +81cb6 b D12 1 +81cb6 w E18 1 +81ce0 b C13 1 +81ce7 b C17 1 +81d17 w N2 1 +81d97 w P4 1 +81de7 b S7 1 +81e00 w R14 1 +81e17 w R5 1 +81e20 w S13 1 +81e41 b C17 C18 2 +81e71 w Q7 1 +81e77 b O16 1 +81e77 w C12 1 +81ea1 b D3 1 +81ec1 w C5 1 +81ef1 w C18 1 +81f41 w S16 1 +81f46 w D3 1 +81f61 b E3 1 +81f61 w Q16 Q14 2 +81f70 w R16 1 +81f77 w O17 1 +81fd6 b F15 1 +82007 b G16 1 +82011 w D9 1 +82026 b N18 1 +82037 b M7 1 +82046 w M5 B7 2 +82050 w O15 1 +82060 w G6 1 +82067 w R4 1 +82096 b F4 1 +820a7 w B3 1 +820b7 b N15 1 +820d7 w O5 F3 2 +82100 b E2 1 +82111 b Q2 1 +82120 b G18 1 +82156 b C5 D4 2 +82161 w F15 1 +82167 w G15 1 +82171 b N2 1 +82171 w E15 1 +82196 w P3 O14 2 +821c1 b E2 1 +821c6 b A4 1 +82231 w G17 1 +82261 b E15 1 +82297 b R3 C2 2 +822b1 w R6 1 +82346 b R5 1 +823c7 b F3 1 +823d0 b G5 1 +823e0 w E15 1 +823e1 w C13 1 +823e6 w E5 1 +823f0 w E16 1 +823f7 w D13 1 +82406 w M16 1 +82420 w Q16 1 +82461 b B3 1 +82480 b Q14 1 +824c0 b R17 R12 2 +824c0 w D14 1 +824c1 w Q18 1 +824e0 w R5 B16 2 +82507 w E17 1 +82517 b G18 1 +82526 w N2 1 +82547 b C4 1 +82571 w E4 E6 2 +825a1 b B17 B14 2 +825d0 b B2 1 +825f1 b O8 1 +82610 b P18 R17 Q14 R14 4 +82677 w R18 1 +82701 w R6 1 +82707 w C14 1 +82751 b D9 1 +82751 w C3 1 +82790 w F3 1 +82791 w H7 1 +82796 w Q6 1 +827e1 w Q14 R17 2 +82847 w Q18 1 +82867 b C7 1 +82881 b M17 1 +82897 w D17 1 +828a7 w E17 B13 2 +828d6 w F3 1 +82907 b B15 1 +82920 b Q5 1 +82946 b Q8 1 +82956 w Q2 1 +82976 w P6 1 +82990 b Q15 1 +829a0 w E15 1 +829b7 w C18 1 +829f6 b H4 1 +82a27 w Q18 1 +82a41 w F2 1 +82a56 b D18 1 +82a71 w D14 E18 2 +82a91 b C3 1 +82aa0 b D4 1 +82aa6 w D15 1 +82ab7 w E16 1 +82ad0 b M14 1 +82b20 w M5 1 +82b36 w F15 1 +82b46 b Q16 1 +82b91 b E5 1 +82bd0 b R2 1 +82bd0 w B2 1 +82be1 w D4 1 +82c11 w C18 1 +82c26 b H4 1 +82c50 w P13 1 +82c66 w O15 N16 P14 3 +82c76 b C17 1 +82cb1 w P12 1 +82cd6 w E5 1 +82d06 w Q16 1 +82d40 b P2 1 +82d67 b S5 1 +82da0 b O16 N16 2 +82db1 b E12 1 +82dc7 b O18 1 +82dd1 w P18 1 +82dd6 b J18 1 +82de7 w D14 1 +82df0 w D15 1 +82df7 w B6 1 +82e07 b E4 1 +82e20 b S5 1 +82e36 b C6 D6 2 +82e37 w M3 1 +82e40 b Q6 1 +82e76 b C3 C5 D6 D7 C7 5 +82e76 w D6 C6 E3 E5 G3 G4 H4 7 +82e86 b C15 1 +82ea0 b Q12 1 +82ec0 w C11 1 +82ed0 b F2 1 +82ee0 b G8 H6 2 +82f40 w H18 1 +82f46 w F4 1 +82f91 b P13 1 +82f91 w D15 1 +82f96 w S7 1 +82fa0 b S15 1 +82fd6 b S17 1 +82fe0 w E5 1 +83020 b P17 1 +83026 b Q15 1 +83086 w G14 1 +830a6 w F17 1 +830a7 b R15 1 +830b7 w O17 Q16 2 +830c0 b G8 1 +830f1 b P7 1 +83116 w D14 1 +83141 w O5 1 +83151 b G3 1 +83170 w D13 1 +831a0 w D15 1 +831a6 b E16 1 +831a7 w C7 1 +831e1 b R15 1 +831f1 b S3 1 +83220 b Q15 1 +83220 w S8 1 +83221 w R15 1 +83241 b S5 1 +83256 w D5 E4 2 +83257 w M18 1 +83280 b D15 1 +83287 b C4 1 +832a0 b Q3 1 +832a1 b A4 1 +83300 b R15 1 +83317 b R4 1 +83331 w E15 E17 2 +83337 w S2 1 +83371 w M2 1 +83386 b D7 1 +833f6 w S6 1 +83400 w P2 D13 2 +83411 b E14 1 +83411 w Q12 1 +83430 b D6 E3 F5 H3 4 +83436 w O6 1 +83440 b O16 1 +834c0 b R13 O17 2 +834f6 b F15 1 +83531 b E7 1 +83561 w O4 1 +83587 b D13 1 +83597 b R16 Q15 R17 3 +835c0 b R2 1 +835c7 w D18 1 +835f0 w P7 1 +83617 b R5 1 +83626 b C4 1 +83661 b R17 1 +83671 b C16 1 +83690 w R3 M3 2 +836d1 b R15 1 +836f1 b E7 1 +83710 b P17 1 +83711 b B4 1 +83740 b G6 1 +83747 w D13 1 +83751 b M3 1 +83766 b P5 1 +83776 w F16 1 +83791 w D2 1 +837a6 w H3 1 +837a7 w E5 1 +837b7 b B14 1 +837e0 b C5 1 +83807 w O13 1 +83841 w S7 1 +83866 b O4 1 +83866 w O3 P4 2 +83897 b F6 1 +838b0 w B16 1 +838f0 w O12 1 +83911 b C16 1 +83950 w Q4 1 +83991 b M17 1 +839b6 b D6 1 +839b7 w F4 1 +83a37 w Q13 1 +83a51 w Q6 1 +83a56 b O5 1 +83a71 b E14 1 +83a80 w R5 1 +83ab0 w P3 1 +83af0 b F7 1 +83b10 w E14 1 +83b37 w N16 1 +83b47 w B15 1 +83b66 b O15 1 +83bc1 w P3 1 +83bd6 b C12 1 +83be1 b R2 1 +83bf0 b R4 1 +83c11 w S16 1 +83c31 w S14 1 +83c57 b D6 1 +83cd1 w C4 1 +83cf0 w S3 1 +83d26 w O2 1 +83d36 b C14 1 +83d51 b G5 1 +83d56 w R14 1 +83d67 w E15 1 +83d71 w E5 1 +83d80 b M2 1 +83dc0 b G17 H3 2 +83e70 w D15 1 +83e77 w B5 1 +83ea6 b P5 1 +83eb1 b S16 1 +83eb7 b O16 1 +83ed6 w Q3 1 +83ee6 b C14 1 +83ef1 b F12 1 +83f11 b D6 1 +83f40 w O4 1 +83f76 b M3 1 +83f77 w D17 1 +83f90 b S16 1 +83fa6 b E18 1 +83fb6 b P4 1 +83fd1 b Q15 1 +84020 w D11 1 +84031 b P18 1 +84040 w G15 1 +84067 b E3 1 +840a7 w Q2 1 +840b1 b N15 1 +840e7 w C2 1 +840f6 b S18 1 +840f6 w E14 D13 F15 3 +840f7 b D5 1 +84126 w F17 1 +84141 b C9 C7 2 +84190 w P18 1 +84191 w D4 C3 2 +841c1 b H17 Q2 2 +84216 b B17 1 +84251 b S3 1 +84256 b Q17 1 +84256 w R15 1 +84261 w O5 Q6 2 +84267 w H16 1 +84277 b F2 1 +84287 w R6 1 +842c0 b C2 1 +842c1 b G5 1 +842d6 w R17 Q16 2 +84301 w R2 1 +84307 b P3 1 +84317 b R12 1 +84337 b J4 1 +84351 b O15 1 +843a7 b L16 1 +843c1 b N5 1 +843e0 b E7 1 +843e1 b D15 E14 2 +843e1 w C3 1 +843e7 b R8 1 +843f7 b S14 1 +84431 b E16 1 +84451 b S4 1 +844a0 w R3 1 +844e7 b R8 1 +844f0 w Q5 1 +844f7 w C3 1 +84506 w E6 1 +84511 b C2 1 +84536 w D15 1 +84540 w R16 Q17 2 +84551 b P14 R17 2 +84566 b O4 1 +84597 w J3 G3 2 +845a0 w O17 1 +845a6 b B5 1 +845d0 w C6 1 +845f7 w F14 O5 2 +84621 b G2 1 +84637 b R17 O16 Q16 Q15 P14 P15 N15 N16 R14 R13 Q13 11 +84637 w Q17 1 +84680 b D9 1 +846b6 b A16 1 +846c1 b O3 S5 2 +84716 w P16 Q14 2 +84720 b Q16 1 +84740 b Q13 1 +84787 w J3 E6 2 +84797 b D8 1 +847a0 b G16 1 +847a1 w D3 1 +847e7 w E6 1 +84811 b C3 1 +84850 b O3 1 +84857 b R14 S14 2 +84861 b T19 1 +848a0 b D4 1 +848b0 b F2 1 +848c7 w S18 1 +848d7 b M16 1 +848f0 b D7 1 +848f0 w D14 C17 2 +848f6 w R17 1 +84956 b E15 1 +849a7 b O2 1 +849c6 b E4 D5 2 +849d1 w R16 1 +849e6 w O4 1 +849e7 b D3 1 +84a01 w R8 1 +84a11 b S17 1 +84a11 w B8 1 +84a30 w F15 1 +84a36 w C7 1 +84a46 w S15 1 +84a57 w Q2 1 +84a86 b S4 1 +84a97 w S5 1 +84aa7 b M17 1 +84ab7 b Q16 1 +84ad6 b O3 1 +84ae1 b D17 1 +84af6 w S14 1 +84b17 w Q6 R7 2 +84b31 w P18 1 +84b71 b N16 1 +84b87 w S14 1 +84bf0 w S17 1 +84c27 b S4 1 +84c40 w R16 1 +84c80 w S15 1 +84cb1 w O18 1 +84cc1 b M16 1 +84cc7 w C17 1 +84d01 w S7 1 +84d07 w S3 1 +84d46 w S17 1 +84d71 b P4 1 +84d86 w R14 1 +84d90 b N5 1 +84dc7 w P7 1 +84de0 b P5 1 +84df0 w S15 1 +84e00 w E13 1 +84e16 w O16 1 +84e27 b F3 1 +84e37 b F3 1 +84e46 b P5 1 +84e57 w N5 1 +84e90 b C15 1 +84eb7 b O6 1 +84ec1 b S6 1 +84ed6 w H17 1 +84ed7 w D17 1 +84f06 w G14 1 +84f07 b F13 1 +84f27 b G5 1 +84fd7 b N17 1 +85001 b F2 1 +85010 w R6 1 +85026 w Q12 1 +85037 w Q14 1 +85050 w R16 1 +85067 w R17 S17 2 +85077 b D16 1 +850a1 b N16 1 +850f6 b F18 1 +85107 b B2 1 +85130 b C17 1 +851a6 b E16 1 +851b1 b G18 R16 2 +851b1 w N16 1 +851c0 b P3 1 +851c6 w C15 C18 2 +85200 w C15 1 +85231 w Q17 1 +85237 b P16 1 +85240 w P15 1 +852c1 b P18 1 +852c1 w P2 1 +852e7 w B4 1 +85321 w P4 1 +85341 w O15 P4 2 +85351 b E6 1 +85351 w D18 1 +85370 w G15 1 +85386 w R9 1 +85390 b F6 1 +853b6 b D6 1 +853d6 b Q5 1 +85437 w B14 1 +85441 b S3 1 +85487 w B17 1 +85496 w O11 1 +854a6 b R16 1 +85520 b Q17 1 +85556 w E14 R17 2 +85576 b C17 1 +85581 b S4 1 +855b7 w R3 1 +855d0 w B5 1 +855f0 w O13 1 +855f1 w O13 1 +85606 b G17 1 +85647 b D14 1 +85651 b B16 1 +85656 b C8 1 +85677 w D18 1 +85696 b F3 F4 2 +856c6 b S6 1 +85706 w E4 D6 2 +85747 w C18 1 +85751 w E18 1 +85766 b E14 1 +85766 w S16 1 +85770 b S18 1 +85777 b Q7 1 +85777 w C5 1 +85780 w R16 Q16 R13 P15 R17 5 +85797 w D18 1 +857a1 b D18 1 +857a6 b E3 B3 2 +857c0 w P6 1 +857c6 b Q13 1 +857f7 w Q18 1 +85806 b R15 S15 2 +85891 b P18 1 +858b7 b E12 1 +85900 b R11 1 +85911 w D18 1 +85926 w R17 1 +85927 b R14 P16 2 +85931 b G18 1 +85947 w D7 1 +85956 b N5 1 +85990 b E4 1 +859a6 b Q3 P4 L4 3 +859e0 b Q18 1 +859f0 w R2 1 +85a07 w E7 1 +85a10 b R17 1 +85a20 w H3 1 +85a27 b J15 1 +85aa6 b R4 1 +85ac0 b B19 1 +85ac6 w S12 1 +85ad7 w E18 1 +85b40 w O17 1 +85b86 b M13 1 +85b91 w B6 1 +85b96 w E18 1 +85bc0 b P15 1 +85bc0 w O18 1 +85c36 w R17 1 +85c47 w R7 1 +85c87 b C4 1 +85ca0 w D7 D3 2 +85cb0 w A4 1 +85d17 b B6 1 +85d20 b F5 1 +85d20 w D15 1 +85d36 w O3 1 +85d41 b D7 1 +85dc6 w R12 1 +85de1 b D17 1 +85e10 w Q7 1 +85e27 b E19 1 +85e77 b D4 1 +85e97 b R17 1 +85f06 b F16 1 +85f91 b S6 1 +85fa1 w N12 1 +85fe0 b B5 1 +85ff1 b R13 1 +86021 w Q15 O15 2 +86031 w D4 1 +86040 b E2 1 +86050 b D4 1 +86061 b N6 1 +86090 w E4 1 +860d0 w B16 1 +860f1 b E14 D14 2 +860f7 w F14 1 +86120 w O6 1 +86127 b R15 1 +86147 w E18 1 +86186 w D5 1 +861a1 w Q16 O16 2 +861a6 w R2 M3 2 +861d6 b F3 1 +861e0 w O15 1 +86226 b O14 1 +86231 w E12 1 +86236 b C3 1 +86251 b R2 1 +86261 w C3 1 +86286 w M17 1 +862b7 b R7 1 +862e1 b C2 1 +86321 w P14 1 +86360 w F3 1 +86377 b C5 1 +86396 w P5 1 +863e1 b C7 1 +86400 b D14 1 +86407 b P17 1 +86410 b P13 1 +86437 b D11 1 +86467 w C14 1 +864b6 w C16 1 +864d1 b D5 1 +864e6 b C18 1 +86500 b F4 F5 2 +86507 w Q7 1 +86526 w F3 1 +86530 b J3 1 +86536 w G17 1 +86537 b E14 1 +86570 b D3 1 +86581 w R3 1 +865b6 w H17 1 +865c0 w B2 1 +865f6 w C17 1 +86600 b O11 1 +86601 w E5 1 +86611 w E2 1 +86627 b C14 1 +86650 w Q15 1 +86657 b R6 1 +86677 w E3 1 +866b1 b B14 1 +866b1 w C16 1 +866d7 b P7 1 +866f6 b M17 1 +86716 w S16 1 +867b6 b F6 1 +867f6 w D13 1 +86846 b Q3 P2 O4 3 +86861 w P15 1 +86870 b Q6 1 +86880 w O3 1 +868c6 w B15 1 +868e0 w S5 1 +868e7 b M5 S16 2 +868f0 w O4 1 +86900 w C16 D17 2 +86980 w J17 1 +869a6 w C13 J17 2 +869c0 b C18 1 +869d0 b E14 1 +869e1 w Q15 1 +869e6 w O3 1 +86a16 b L13 1 +86a21 b M4 1 +86a26 w F15 C14 2 +86a41 b B17 1 +86a41 w R17 1 +86a57 w Q12 M16 2 +86a67 w S16 1 +86aa0 b D17 1 +86aa7 b M13 1 +86ab7 w J14 1 +86ac7 w M4 Q8 2 +86b10 w Q15 1 +86b40 w R6 1 +86b47 b S7 1 +86b67 b R3 1 +86ba1 w J2 1 +86bb7 b R13 1 +86c06 w N5 1 +86c50 w E5 1 +86c57 w P2 1 +86c61 w T5 1 +86c66 b P14 1 +86c87 w G17 1 +86ca0 w O15 Q14 2 +86ca1 b E2 1 +86cf1 b R5 1 +86d46 w L3 R17 2 +86d50 b C17 1 +86d67 b P17 1 +86d70 b O6 1 +86db7 w C4 1 +86e40 b N5 1 +86e41 w O15 1 +86e80 w N12 1 +86eb0 w E18 E16 2 +86ec6 b P4 Q6 2 +86ee7 b C6 1 +86ee7 w H16 1 +86f00 w R4 1 +86f01 w C8 1 +86f07 b R3 1 +86f10 w C7 1 +86f26 b D4 1 +86f31 b Q6 R2 2 +86f36 w F15 G18 2 +86f41 b C17 1 +86f80 w R16 Q17 2 +86fc0 w P4 1 +86fe7 b S3 1 +86ff0 w Q5 1 +87006 w S15 1 +87011 w P1 1 +87040 b P2 1 +87057 b Q4 1 +87066 w E4 1 +87070 w O16 R17 2 +87087 w R6 1 +870b0 b Q15 1 +870b1 b C2 F2 2 +870c1 w P18 1 +870e0 w N4 1 +870e1 w H16 1 +870f0 b Q12 1 +870f7 w C3 1 +87107 b C9 1 +87111 w P5 1 +87161 b F16 1 +87197 w D9 1 +871a0 b R14 1 +871d0 w G3 1 +871e1 w R16 1 +87207 w P6 1 +87216 b Q1 1 +87220 w D9 1 +87227 b H17 1 +87237 b Q11 1 +87257 w C4 1 +87277 b E6 1 +87277 w C3 1 +872b1 w D5 1 +872c1 w C18 1 +87311 w O16 N17 2 +87327 w R14 1 +87340 w Q8 1 +87366 b T4 1 +87377 b C12 1 +87386 b R3 1 +873a0 w O13 1 +873d1 w J17 1 +873e1 b B5 1 +873f0 b Q7 1 +873f6 b E14 1 +87400 b O14 1 +87401 b S7 1 +87491 b P2 1 +874a7 b C14 1 +874c6 w R6 Q4 2 +87527 w R18 R11 2 +875c0 b O16 R15 P14 R12 4 +875d7 w D16 1 +875f0 b O16 1 +87630 w C12 1 +87640 w F17 1 +87651 b E17 1 +87671 w O17 1 +87676 w S4 1 +87677 w S7 1 +87680 b O2 1 +876c0 b D12 1 +876f1 b G3 1 +87720 w D14 D13 2 +87771 w R3 1 +87780 b E4 1 +87786 w N3 1 +877d1 w G16 1 +877e1 b C3 1 +87820 w D5 1 +87867 b S17 1 +878c1 w F7 1 +878e6 w F6 1 +878f0 b G3 1 +87911 b A19 1 +87930 w Q5 1 +87961 w E17 1 +87966 w E3 E2 2 +87987 b R16 1 +87a10 w R18 1 +87a11 b Q5 1 +87a26 b F16 1 +87a50 w Q17 1 +87a87 w E6 1 +87ac6 b S4 1 +87b01 w E15 P7 2 +87b07 b F16 B15 2 +87b41 w O14 1 +87b50 b E7 1 +87b97 b Q13 Q14 2 +87b97 w Q14 1 +87bd1 b C18 1 +87be1 b E2 1 +87bf0 b B8 1 +87c00 w C13 P4 2 +87c27 b D7 D4 Q6 P5 4 +87c27 w R6 1 +87c31 w D2 1 +87ce7 w C2 1 +87d17 b O15 1 +87d27 w R19 1 +87d41 b B4 C3 2 +87d67 w Q18 1 +87d70 b P4 1 +87d71 w G2 1 +87d76 b P16 Q15 P17 3 +87da7 b C7 1 +87dc7 b B18 1 +87dd1 w E16 1 +87e00 w R4 1 +87e07 b R16 1 +87e07 w S4 1 +87e21 w R3 Q8 2 +87e30 w T18 1 +87ea0 w Q15 R15 2 +87ea7 w H17 C4 2 +87ef1 w D16 1 +87f01 w R13 1 +87f10 b Q5 1 +87f21 w R8 1 +87f41 b P14 1 +87f41 w S6 1 +87f71 b F3 N18 2 +87f90 b Q6 R3 2 +87f96 w H16 1 +87fa0 b F14 1 +87fb6 w Q18 1 +88017 w D17 1 +88031 w F5 F6 C5 C6 4 +88041 w M18 1 +88057 w R16 1 +88066 w R2 1 +88077 w D18 1 +880a1 b E17 1 +880b1 w G17 1 +880b6 w E18 1 +880d7 b S15 1 +88136 w R8 1 +88176 b B16 1 +88177 w D5 D6 E4 C5 4 +88180 b F4 F2 2 +88186 b J3 1 +88187 w C4 1 +88197 b C6 1 +881b1 w D18 1 +881c0 b E3 1 +881c7 w F16 1 +881f6 w A18 1 +88230 b H4 R16 2 +88241 b R3 1 +88257 w S3 1 +88261 b C3 1 +88261 w S13 1 +88270 b D7 D4 E6 3 +882b6 b Q7 1 +882d6 b E7 1 +88306 w F4 1 +88321 b P15 1 +88340 w H5 1 +88341 w S13 1 +88347 w F2 1 +88376 w R2 1 +88390 w O8 1 +883a0 w S5 1 +883f7 w D15 1 +88446 b Q6 1 +88447 b C3 1 +884b1 w D7 1 +884b6 w Q6 1 +88540 w R3 1 +88577 b F4 1 +885c0 b N13 1 +885d0 b O5 1 +88646 w B15 1 +88651 b P15 1 +88657 w C2 1 +88667 w O16 1 +88690 b C6 1 +886b0 b C14 1 +886b0 w Q16 1 +886c6 b C4 1 +88701 b H18 1 +88716 b Q17 1 +88736 w F3 1 +88760 b R3 1 +887a7 w M4 1 +887b6 w Q15 1 +887f0 w N5 1 +88816 b E8 1 +88816 w P14 1 +88850 w F2 1 +88860 w F15 1 +88871 b S8 1 +88890 w D4 1 +888b6 w P18 G14 2 +888c6 w P15 1 +888e6 w F4 1 +888f1 b D8 1 +88901 b P18 1 +88950 w E14 1 +88956 b G16 1 +88980 w Q8 1 +88996 b D15 1 +889b1 w Q16 1 +889d6 b G5 1 +88a16 w C2 1 +88a40 w G15 1 +88a50 w C8 1 +88a70 b R4 1 +88a76 w S5 1 +88aa6 w D14 1 +88b26 b N17 P15 2 +88b30 w O6 1 +88b47 w R5 1 +88b76 b N17 1 +88ba1 w G4 1 +88bd7 b O3 1 +88c07 b Q8 1 +88c07 w R15 1 +88c50 b P4 1 +88c57 b Q17 P16 R17 3 +88c61 w S18 1 +88c76 b O17 1 +88c96 w D4 1 +88cd7 w S5 1 +88ce7 b P18 1 +88d21 w O7 1 +88d31 b D2 1 +88d61 w E14 1 +88d67 w H16 1 +88d71 b E15 1 +88d71 w C18 1 +88d77 b Q9 1 +88d77 w M2 1 +88da6 b N16 1 +88da6 w C15 1 +88de0 b D14 E16 2 +88de1 b Q7 1 +88e01 b B16 1 +88e31 b S14 1 +88e50 b C8 1 +88e51 w E2 1 +88e86 w C4 1 +88eb0 w P15 1 +88ed6 w E16 1 +88f01 b E2 1 +88f01 w C7 1 +88f37 w D17 D15 2 +88f46 w Q7 1 +88f76 b B2 1 +88f81 b G15 1 +88fa6 w N5 1 +88fc0 b N16 1 +89011 b C5 C6 D6 D5 4 +89066 b Q15 1 +890b6 b B11 1 +890f1 b R4 1 +89107 w P15 1 +89111 w F19 1 +89131 w B17 1 +89137 w S15 1 +89147 b D16 C17 2 +89161 w G16 C17 2 +89186 b N3 S5 2 +89190 w O6 1 +89196 w G16 R2 2 +891a7 b D4 1 +89210 b D6 1 +89240 b S14 1 +892a1 b P8 1 +892d1 w O15 1 +89300 w R13 S15 2 +89320 b P14 1 +89341 b F18 1 +89351 w R17 1 +89381 b F15 C17 2 +89386 w O2 1 +89391 b C3 1 +893a1 w N2 1 +893d1 w E17 1 +893e0 w C14 1 +893e6 b S4 1 +893e6 w C15 1 +89430 b S16 1 +89440 w R5 1 +89467 b C2 1 +894c7 w C17 1 +894e0 b C14 F15 2 +894e6 b F6 1 +89517 b Q15 R16 2 +89517 w P16 R17 N17 Q14 Q12 5 +89527 w D16 1 +89557 b G2 1 +895b0 w E6 1 +895e0 b G6 1 +895f0 b E14 1 +89631 b D16 1 +89636 b R18 1 +89657 w B13 1 +89677 b Q4 1 +89690 w O4 O5 2 +89696 w C6 D7 2 +89707 b M2 1 +89711 w J16 1 +89721 b O17 1 +89737 w O7 1 +89796 b S4 1 +897b1 w D15 1 +897c7 b S7 1 +897d0 w D5 C5 2 +89851 b F15 1 +89857 b D15 1 +89877 w Q16 1 +89881 b S14 1 +89881 w S9 1 +898c0 b N17 1 +89910 b F15 1 +89957 b T15 1 +89957 w Q18 1 +899c7 w N15 1 +899e7 b B18 1 +89a01 w S7 C16 2 +89a07 b S4 1 +89a11 w P17 1 +89a16 w D7 1 +89a20 b R6 1 +89a27 w R3 1 +89a81 w F14 1 +89a87 w Q4 1 +89ab1 b P16 1 +89ac0 b F4 1 +89ae0 w F4 1 +89b27 b P14 1 +89b36 w P4 1 +89b37 w D4 1 +89b80 w R15 1 +89b87 w Q18 R17 Q17 R12 4 +89bd7 b T18 1 +89be6 w C2 1 +89c00 b C7 1 +89c47 b P8 1 +89c50 w O18 1 +89c91 b F16 1 +89c91 w G16 F16 2 +89c96 w Q17 1 +89cd0 w S14 1 +89ce6 b O3 1 +89d07 b E16 1 +89d31 b B7 1 +89d40 b J16 1 +89d47 b O2 1 +89d47 w P13 1 +89d56 b R3 1 +89de1 w P18 1 +89df0 b E6 1 +89e00 w A17 1 +89e37 b B14 1 +89e37 w S16 1 +89e41 b R12 1 +89e57 b M15 1 +89e81 b G4 1 +89ea7 b P6 1 +89f36 b Q5 1 +89f40 b C16 1 +89f71 w N6 1 +89fc6 w C8 1 +89fd6 b F3 1 +8a000 b D16 1 +8a040 w D3 1 +8a060 w G2 1 +8a070 b E7 E17 2 +8a077 w S4 1 +8a087 w F15 1 +8a0a0 w B18 1 +8a0a1 b R17 1 +8a0a6 b E5 1 +8a0e7 w Q12 1 +8a100 w R4 1 +8a117 b O5 1 +8a181 b B15 1 +8a1b6 b A17 1 +8a1d7 w O15 1 +8a227 b O16 1 +8a251 b Q18 R17 2 +8a286 w N17 1 +8a2a7 b L18 1 +8a2c7 w D7 1 +8a2f6 w P3 1 +8a307 b Q17 1 +8a310 w B4 1 +8a311 w D5 G3 2 +8a326 b O4 G15 2 +8a347 w D12 1 +8a391 w E15 1 +8a3a1 w R3 1 +8a427 w C5 1 +8a430 w R3 1 +8a441 b S3 1 +8a447 b C2 1 +8a447 w D18 1 +8a470 w F6 1 +8a481 b N13 1 +8a4e0 w O3 1 +8a4f0 w E4 1 +8a547 b B17 1 +8a550 b O18 1 +8a556 b C5 1 +8a556 w E14 1 +8a580 b R9 1 +8a5c0 w B15 1 +8a600 b Q18 1 +8a601 b S4 1 +8a611 b F14 1 +8a631 b R5 1 +8a661 b D2 1 +8a680 b P13 1 +8a687 b C5 1 +8a6a1 w M4 1 +8a6b6 w Q2 1 +8a6d0 b P18 1 +8a6d0 w R16 1 +8a6d1 b Q4 1 +8a716 b H15 1 +8a736 b D17 1 +8a750 w G16 1 +8a757 b P13 1 +8a786 w D14 1 +8a797 b B5 R14 2 +8a7f0 b S15 1 +8a7f6 b E15 1 +8a820 w C3 1 +8a836 b Q17 1 +8a841 b C11 1 +8a877 b P16 1 +8a8b1 b O16 1 +8a8c6 w E3 1 +8a8d0 b R18 1 +8a8f7 b O4 R3 2 +8a907 b Q16 1 +8a910 b E17 1 +8a916 b N15 1 +8a927 b Q16 P17 R15 O16 Q14 5 +8a947 b P3 1 +8a950 w G4 1 +8a956 b Q15 1 +8a961 w D11 1 +8a966 w Q14 1 +8a987 b C12 1 +8a9d7 w G14 1 +8a9e7 b Q4 1 +8a9f1 w P15 1 +8a9f6 w Q14 1 +8aa47 w Q2 1 +8aa67 w R16 1 +8aa70 b R18 1 +8aa87 w S6 1 +8aab1 w R5 1 +8aae1 b B5 1 +8ab20 b D5 1 +8ab30 b C14 1 +8ab30 w C2 D2 2 +8ab50 b M13 1 +8ab50 w B15 1 +8ab90 b E18 1 +8ac66 b Q17 Q15 2 +8aca7 w F18 1 +8acb6 w G6 1 +8acc6 b T17 1 +8acd1 w B16 1 +8acd7 b H4 1 +8ad01 w R15 1 +8ad51 b Q14 R18 2 +8ad61 b C3 1 +8ada0 w P15 1 +8adf7 b F18 1 +8ae17 w F8 1 +8ae81 b E4 F4 D5 E3 4 +8aea1 w D7 1 +8aeb6 b H16 1 +8aee0 b B2 1 +8aee6 w D5 F4 2 +8af01 b G13 1 +8af06 w B4 1 +8af07 b S15 1 +8af21 b D14 1 +8af80 b Q6 1 +8afa1 b Q15 1 +8b040 w O3 1 +8b050 b F17 1 +8b097 b Q16 1 +8b0a1 w D16 1 +8b0f0 b R15 1 +8b0f1 w R3 1 +8b170 w G5 1 +8b1b1 w J4 1 +8b1d1 w M18 1 +8b1e0 w R11 1 +8b1f1 b B16 C17 2 +8b227 b S4 1 +8b241 w E19 1 +8b247 w D2 1 +8b280 b P15 1 +8b290 w D16 1 +8b297 b N17 F2 2 +8b2b1 w N18 1 +8b2d1 b B3 1 +8b347 b C6 1 +8b351 b B6 1 +8b360 w O18 1 +8b3a0 w D2 1 +8b3a6 w R18 1 +8b3a7 w N16 Q16 2 +8b3b1 w E2 1 +8b3b7 w B14 1 +8b410 b D5 1 +8b417 b G15 1 +8b420 b N17 1 +8b430 b O14 1 +8b461 b Q11 1 +8b470 w A14 1 +8b481 w N4 R3 2 +8b491 w Q17 1 +8b4b1 w J16 S16 2 +8b507 w L16 1 +8b511 b M2 1 +8b541 b P13 1 +8b551 b F16 1 +8b581 b O5 1 +8b591 b C3 1 +8b597 b D6 1 +8b5a7 b C16 1 +8b5b1 w C6 1 +8b5b7 b D4 1 +8b5d1 w B16 1 +8b5e1 w S1 1 +8b5f1 w H4 1 +8b5f6 w R16 1 +8b5f7 b H4 1 +8b600 b O2 C8 B4 3 +8b607 w D2 1 +8b631 b Q16 1 +8b650 w P3 1 +8b671 w B4 1 +8b686 w R18 1 +8b6c6 w S17 1 +8b6c7 b R9 1 +8b717 w B3 1 +8b727 w F16 1 +8b736 b P7 1 +8b747 w C2 1 +8b756 b F9 1 +8b761 b P17 1 +8b777 w S3 1 +8b780 w F14 1 +8b781 b R2 1 +8b891 w O2 1 +8b8c7 b P3 1 +8b8c7 w R14 1 +8b8d6 w R18 1 +8b8e6 w M4 1 +8b8f1 w D4 1 +8b947 w O4 1 +8b971 b F4 1 +8b980 b E5 1 +8b9e0 b E14 1 +8b9f0 b E15 1 +8b9f0 w O3 1 +8ba20 b P17 1 +8bae6 w R15 1 +8bb21 w Q4 1 +8bb27 w G6 1 +8bbb7 w C3 1 +8bbe7 w D15 1 +8bc06 w Q2 1 +8bc16 w C18 1 +8bc40 w S17 1 +8bc96 w N15 1 +8bca6 b B8 1 +8bcd1 b S17 1 +8bcd6 b C8 1 +8bd07 b D18 1 +8bd70 w N16 1 +8bda1 w R16 1 +8be01 w P3 1 +8be37 b Q16 1 +8beb6 w D18 1 +8bec6 w Q1 1 +8bef0 w D3 1 +8bef6 w T17 1 +8bf47 b E2 1 +8bf57 b F15 1 +8bf80 w E4 1 +8bf96 b F15 1 +8bfb1 w C11 1 +8bfc6 b D6 1 +8bfd6 b D14 C14 E17 E15 G17 G16 H16 7 +8bfd6 w C17 C15 D14 D13 C13 5 +8c010 b B6 1 +8c017 w E14 1 +8c047 b C17 1 +8c0a0 w N5 1 +8c0a7 b Q13 1 +8c0c0 b S16 1 +8c0f1 b O7 1 +8c120 w S18 C5 2 +8c147 w S5 1 +8c150 w J2 1 +8c1c1 w C14 E16 2 +8c1f7 w Q8 1 +8c267 b P4 1 +8c287 b F18 1 +8c2b7 b O15 1 +8c2b7 w D4 1 +8c2c1 b S5 1 +8c2c6 w D6 1 +8c307 b Q4 1 +8c336 b R3 1 +8c356 w B6 1 +8c376 w S5 1 +8c387 b R17 1 +8c3b1 w S18 1 +8c3d1 b F4 1 +8c416 w Q13 1 +8c436 w O5 1 +8c4a1 w M2 1 +8c4b7 b P13 1 +8c4d1 b P6 1 +8c511 b S15 1 +8c520 b E15 1 +8c540 b Q5 1 +8c556 b C11 1 +8c560 b C16 D17 2 +8c5e7 b D4 1 +8c5f1 b P2 1 +8c626 b S15 1 +8c691 b D5 1 +8c6b0 b D8 1 +8c6c7 b M6 1 +8c6d1 w Q16 1 +8c707 b C5 1 +8c761 b O16 1 +8c767 w S12 1 +8c7c7 w O6 1 +8c801 b B15 1 +8c810 b C13 1 +8c827 w R16 1 +8c831 w C17 1 +8c856 b P7 1 +8c861 b M14 1 +8c8b1 b Q13 1 +8c8c0 w O16 1 +8c8d0 b Q6 1 +8c8e7 b S6 1 +8c910 w Q7 1 +8c957 b D2 B3 2 +8c966 w Q6 1 +8c977 b F5 1 +8c980 b N13 1 +8c981 b B5 C14 2 +8c990 w G3 E2 2 +8c997 w H16 1 +8c9a7 w B15 1 +8ca17 w P2 1 +8ca20 b R16 1 +8ca27 w Q3 1 +8ca50 b B5 1 +8ca56 b E5 E4 2 +8ca77 w A1 1 +8ca80 b Q6 P4 C12 3 +8ca86 b S15 1 +8cae7 w O4 1 +8caf0 b O14 1 +8caf6 b C4 E4 2 +8cb01 w R15 1 +8cb27 w M16 1 +8cb30 b P17 1 +8cb41 w S14 1 +8cb80 w J17 1 +8cbe1 b D4 1 +8cbe6 b Q15 1 +8cbf7 w D16 1 +8cc00 w P4 1 +8cc70 w C6 1 +8cc81 w M17 1 +8cc96 b H3 1 +8cca0 b F3 1 +8cca0 w M17 1 +8cd16 b P15 1 +8cd30 b O16 1 +8cd31 b R4 1 +8cd50 b Q5 1 +8cd70 w P3 1 +8cd76 w Q15 1 +8cd81 b O6 1 +8cd86 w G2 1 +8cd96 b R17 1 +8cda0 w R9 1 +8cdb6 w Q14 1 +8cdd6 w C16 1 +8cde6 w C11 1 +8cdf7 b Q3 1 +8ce11 b R6 1 +8ce26 b D6 1 +8ce26 w R18 1 +8ce47 b C3 1 +8ce51 w D16 1 +8ce71 w O14 1 +8ce77 b R3 1 +8cea7 b E5 C6 2 +8ceb1 b A17 1 +8ceb7 b F2 1 +8cec6 w Q5 1 +8cee6 b N6 1 +8cf20 w P15 1 +8cf46 b D6 1 +8cf67 w E2 1 +8cfb6 w D13 1 +8cfd7 b R14 1 +8cfe6 b R12 1 +8cfe6 w E14 1 +8d036 w B4 1 +8d047 w C15 1 +8d050 w E16 1 +8d090 w P12 1 +8d091 w B14 1 +8d0b6 w E16 1 +8d0c7 w F4 1 +8d126 b C16 1 +8d131 w B7 1 +8d187 b Q4 1 +8d1a1 b H2 1 +8d1b6 w S17 1 +8d217 w F5 G4 E3 3 +8d231 b N18 1 +8d276 b D14 1 +8d297 b B3 1 +8d2c6 b F4 1 +8d2e1 w R4 1 +8d2e6 b N17 R11 2 +8d300 b C6 1 +8d310 b Q6 1 +8d336 b D17 1 +8d337 w Q5 1 +8d361 b F3 B5 2 +8d366 w O13 1 +8d376 b Q16 1 +8d391 w C14 1 +8d3c1 b D2 1 +8d3d0 w Q8 1 +8d3d1 w S3 1 +8d3e0 w P14 1 +8d3f0 b E18 1 +8d410 b E5 1 +8d461 b P17 1 +8d466 b D4 1 +8d476 b D18 1 +8d4f0 w C6 1 +8d500 b C11 1 +8d506 w C3 D4 2 +8d517 b E3 1 +8d537 b A5 1 +8d541 b M5 1 +8d576 w G15 1 +8d5a7 b P5 1 +8d5b1 b Q6 1 +8d600 w D5 1 +8d606 b E3 1 +8d620 w J17 1 +8d626 b E6 D7 F5 3 +8d626 w P18 1 +8d656 w E2 1 +8d657 b C5 1 +8d657 w R18 1 +8d666 b C5 1 +8d696 w Q5 1 +8d706 w C17 1 +8d726 w D6 1 +8d730 w S5 1 +8d766 b Q18 1 +8d771 b N5 1 +8d776 b C18 B14 2 +8d797 b P1 1 +8d7b7 b C6 1 +8d7c1 b E3 F2 E2 3 +8d7d0 b E5 1 +8d7e6 b R13 1 +8d816 b D5 1 +8d846 b F3 C7 2 +8d8b6 w P7 1 +8d8e6 w F16 1 +8d8f0 w R3 1 +8d917 b B14 1 +8d927 w Q11 1 +8d947 b O2 1 +8d957 w Q7 1 +8d967 b C2 1 +8d980 w L17 1 +8da10 b O17 1 +8da51 b D16 1 +8da77 b Q2 1 +8da90 w R17 1 +8dab6 b R16 1 +8dae0 b P7 Q3 R4 3 +8daf6 b H17 1 +8db00 w E17 1 +8db07 b G3 1 +8db27 w O18 1 +8db37 b F17 1 +8db51 b Q2 S4 2 +8db51 w Q17 1 +8db81 b E16 1 +8dba7 w C13 1 +8dc00 b C3 1 +8dc07 w H3 1 +8dc57 w D5 1 +8dc60 b P13 1 +8dc77 b C8 1 +8dcf7 b F18 1 +8dd00 b O2 1 +8dd20 b D6 1 +8dd50 w R13 1 +8dd57 b S3 1 +8dd61 b P14 1 +8dd71 w R8 1 +8dd77 w S5 1 +8ddb6 b P5 1 +8dde6 b D17 1 +8de07 w E13 1 +8de10 b E14 1 +8de30 b E4 1 +8de30 w B5 1 +8de40 b Q14 1 +8de60 w D3 1 +8deb6 b C3 1 +8deb6 w C2 1 +8deb7 w Q6 1 +8dee1 b C17 D16 D15 E14 D14 D13 6 +8dee1 w D17 1 +8df06 w N17 1 +8df30 b G9 1 +8df36 w R6 1 +8df76 b N16 1 +8df86 w D15 1 +8df96 w A12 1 +8dfd6 b G5 1 +8dfd7 b G16 1 +8dfe0 b Q13 1 +8e030 b O8 1 +8e031 b Q12 1 +8e060 w E14 1 +8e061 w Q5 1 +8e066 w P7 1 +8e067 w R7 1 +8e090 b D4 1 +8e097 b E5 1 +8e0a7 w B5 1 +8e0b1 b R14 1 +8e170 b C14 1 +8e191 w D18 1 +8e1a6 w E16 1 +8e211 b S15 O18 2 +8e246 b D3 1 +8e260 b F17 1 +8e260 w F18 1 +8e2b1 w P17 1 +8e2b7 b E17 1 +8e2d1 w E15 R4 2 +8e2d7 w J16 1 +8e2f6 b C13 1 +8e301 w R3 1 +8e316 w D6 1 +8e337 w D15 1 +8e366 b F15 1 +8e3a0 b O12 1 +8e3d1 b B6 1 +8e3d6 b F16 1 +8e3d7 b S14 1 +8e407 w Q3 1 +8e437 w P17 1 +8e460 w O17 1 +8e4b6 b C8 1 +8e4e1 b C18 1 +8e506 w C4 1 +8e581 b P4 1 +8e581 w Q5 P5 N4 R3 4 +8e591 b N17 1 +8e5a6 w O4 1 +8e5b1 b G17 1 +8e5d0 w D5 1 +8e5d1 w L17 J2 2 +8e616 b D17 1 +8e620 b P7 1 +8e626 w E4 1 +8e657 w G3 1 +8e676 w N5 1 +8e690 b G4 1 +8e696 b L17 1 +8e6e0 w R11 1 +8e6e7 b E16 1 +8e6f0 b N15 1 +8e776 w C15 1 +8e797 w C11 1 +8e7a6 w F17 1 +8e7c0 w J6 1 +8e7d1 w M15 1 +8e7f1 b C3 1 +8e800 w F4 1 +8e8b0 b D13 1 +8e8b0 w O5 1 +8e921 b S16 1 +8e946 b M17 1 +8e957 b Q17 1 +8e9b6 b P14 1 +8e9c1 w E5 1 +8e9d0 b P18 1 +8e9d0 w Q5 1 +8e9e7 w F16 1 +8ea20 w P17 1 +8ea21 b B5 1 +8ea41 w C3 1 +8ea50 w J4 1 +8ea57 b S3 1 +8ea60 w E17 1 +8ea61 w D13 1 +8ea67 b F7 1 +8ea70 w C2 1 +8ea71 w B18 1 +8ea87 w G16 1 +8eb26 b C11 1 +8eb47 b Q7 1 +8eb57 w B13 1 +8eb61 w C12 1 +8eb86 b R9 1 +8eb91 w S13 1 +8ebb6 w C3 1 +8ebd0 w O2 1 +8ebe7 w R4 1 +8ebf7 b E14 F16 2 +8ec41 w R17 1 +8ec47 b R2 1 +8ec56 w H18 1 +8ec61 b T1 1 +8ec77 w B3 1 +8eca1 w N17 1 +8ecc7 w F16 1 +8ed01 b Q14 1 +8ed40 b C2 R13 2 +8ed46 b E14 1 +8ed77 b B19 1 +8edc7 w C14 1 +8ede6 b S16 1 +8edf6 b B16 1 +8edf6 w F2 1 +8ee07 w P14 1 +8ee17 w B6 1 +8ee37 w F18 1 +8ee57 w S7 1 +8ee81 w D18 1 +8eea0 b F3 1 +8eed1 b Q6 1 +8eed6 w D7 C7 C3 3 +8ef20 w D11 1 +8ef80 w C11 1 +8ef91 w F13 1 +8ef96 b G3 1 +8efd1 b N13 1 +8eff0 w D2 E3 2 +8eff1 b R7 1 +8f006 w P5 1 +8f011 b M16 1 +8f017 b D16 C17 2 +8f021 w M5 1 +8f037 w G14 1 +8f057 w P6 1 +8f0b7 b R3 1 +8f0c6 w O18 1 +8f0e1 w P8 1 +8f0e6 b M2 1 +8f150 w S18 1 +8f186 w F4 1 +8f1a7 w E5 1 +8f230 w P18 1 +8f256 w H3 1 +8f261 w O3 R14 2 +8f266 b G3 1 +8f271 w O15 1 +8f281 b B17 1 +8f297 w S15 1 +8f2b1 b Q4 1 +8f2d0 w R14 1 +8f2e0 w N2 N5 2 +8f2e1 w D4 1 +8f301 w G18 1 +8f320 b Q15 O7 2 +8f350 b R7 1 +8f360 b R14 1 +8f397 b E4 D3 2 +8f397 w D5 C3 C7 F4 H4 R3 R2 7 +8f3a6 b E18 1 +8f3e7 b N2 1 +8f400 b B18 1 +8f416 b B17 1 +8f427 b D4 1 +8f447 b R5 1 +8f467 b F3 1 +8f486 b O14 1 +8f487 w P18 1 +8f490 w E4 1 +8f507 b D13 1 +8f520 b P14 1 +8f536 b R5 1 +8f556 b S12 1 +8f576 w S3 1 +8f581 b R5 1 +8f5e0 w R9 1 +8f5e7 b C8 1 +8f601 w O7 1 +8f620 b R4 1 +8f630 w S2 1 +8f651 w P16 Q15 2 +8f6a0 b O5 1 +8f6d7 w S8 1 +8f6f1 w F15 C2 2 +8f6f7 w N17 1 +8f700 w Q16 1 +8f737 w N3 1 +8f747 b E11 1 +8f770 w N16 1 +8f787 b Q18 S17 2 +8f7a0 b Q3 1 +8f7c1 w B3 1 +8f7d0 b F5 1 +8f7d7 w E5 1 +8f7e7 b R17 1 +8f7f6 w B2 1 +8f820 w D16 1 +8f836 w R3 1 +8f897 b S4 1 +8f8a0 w C16 1 +8f8a1 w N6 1 +8f8b6 w D3 1 +8f8c1 b S3 1 +8f8f7 w Q17 1 +8f917 w D16 1 +8f950 w D5 1 +8f981 w P6 1 +8f997 b R17 Q16 Q15 P14 Q14 5 +8f997 w Q17 1 +8f9f1 b E14 1 +8fa50 b S15 1 +8fab6 w B3 1 +8fac0 b D6 B6 2 +8fac6 w F18 1 +8fad1 w E16 1 +8fad7 b B3 1 +8fae7 w B3 1 +8faf0 w R11 1 +8fb00 b P4 1 +8fb26 b E4 1 +8fb77 w P14 1 +8fb80 w N16 Q15 2 +8fbc6 b C16 1 +8fbc7 w C6 1 +8fbf0 b F6 1 +8fc66 w C6 1 +8fc77 w C15 1 +8fc86 b R2 1 +8fc90 b E13 1 +8fc96 b R3 1 +8fc97 w R8 1 +8fca1 b C2 1 +8fcb0 b E6 1 +8fcb7 w F4 1 +8fce6 w N3 1 +8fd00 b R18 1 +8fd10 b F7 1 +8fd37 w R7 1 +8fdc0 b D16 1 +8fdc7 w B16 M3 2 +8fdd0 b H16 1 +8fdd7 w E18 1 +8fde6 b D14 1 +8fdf6 w O3 1 +8fe36 w E3 1 +8fe46 w O4 1 +8fe57 w L3 1 +8feb7 b D7 1 +8feb7 w M15 1 +8fed0 w C3 1 +8fee0 b Q4 1 +8fee0 w N15 1 +8fee6 w N17 1 +8ff20 w Q1 1 +8ff76 b P5 1 +8ff87 b D18 1 +8ffb1 b D14 1 +8ffd0 b Q16 1 +8fff6 b R4 Q3 2 +90026 b E2 1 +90041 w R4 1 +90050 w P13 1 +90071 w B2 1 +90076 w C17 1 +900e0 b Q5 1 +90126 w F4 1 +90156 w E13 1 +90157 b G3 1 +90157 w R13 1 +90166 w B7 1 +90177 w G18 1 +90186 b E4 1 +90187 w F4 1 +901f0 b E16 1 +901f1 b Q2 1 +90206 b C16 1 +902a6 w P2 1 +902a7 w R3 1 +902c7 b F13 1 +902e0 b P4 1 +90327 b E4 D5 2 +90347 w E4 1 +90356 b C9 1 +90360 w C3 1 +90387 b S8 1 +903b6 w F14 1 +903d1 w F3 1 +903e1 w E3 1 +903f0 w N5 1 +90406 b J17 1 +90421 b G3 1 +90440 w F4 1 +90447 w R8 1 +90457 b R14 1 +90471 w P14 1 +904b1 b S7 1 +904e1 w H17 1 +904f1 w E16 1 +90526 b D7 1 +90530 w R14 1 +90551 w P17 1 +90570 w J16 1 +90591 b N3 1 +905b0 w Q5 1 +905b7 b Q6 1 +905f1 b E17 F18 E18 R8 4 +90606 w O16 1 +90607 b P16 P15 Q13 R17 4 +90607 w Q15 1 +90616 b E17 E18 2 +90631 w O3 1 +90666 w E3 1 +90676 w C5 1 +90690 w E3 1 +906c1 b E15 1 +906d6 w D18 1 +90786 w O16 1 +907b0 w C5 1 +907d1 b S6 1 +907e0 b R7 O3 2 +907f1 b E15 1 +90806 w G5 1 +90826 b O4 1 +90847 w O2 Q14 2 +90861 b P3 1 +90866 w C5 D4 2 +90881 b S16 1 +90881 w G17 1 +90890 b S13 P13 2 +908b1 b S5 1 +908b6 w J2 1 +908c6 w Q18 1 +90906 w A16 1 +90916 b O15 1 +90920 b D14 1 +90940 b G2 1 +90961 w O2 1 +909b0 b D5 1 +909d6 b Q2 1 +909d6 w Q18 1 +909e6 b C17 1 +90a01 w D4 1 +90a30 b C7 1 +90a36 b S17 1 +90a37 w C15 1 +90b31 b B17 1 +90ba6 b D14 1 +90ba7 b E16 1 +90c07 w D14 1 +90c40 b O7 1 +90c81 w D6 C3 2 +90ca0 b F7 C4 2 +90cc0 w S3 1 +90cc7 w H17 1 +90cd7 w O13 1 +90ce0 w Q17 Q16 N17 P15 R17 5 +90ce7 w A4 1 +90d16 w Q15 1 +90d20 b F17 1 +90d30 b D14 1 +90d61 b D12 1 +90d81 w R9 1 +90d90 w H17 1 +90d97 w Q2 1 +90db7 b E2 1 +90e30 b E4 1 +90e41 w D3 1 +90e61 b S6 1 +90e71 w F3 E4 2 +90e90 b Q14 R14 2 +90eb7 b R16 1 +90ed1 w R15 1 +90f00 b B5 1 +90f46 w C4 1 +90f76 b C13 1 +90f77 w D11 1 +90f80 w C5 1 +90f87 w O3 1 +90fc6 w M2 1 +90ff0 w P16 Q9 2 +91000 b E15 1 +91007 b E5 C5 2 +91066 b G3 1 +91067 w Q15 1 +910a1 w R8 1 +910d6 b E16 1 +910d7 b G16 C17 2 +910f0 b E12 E13 C15 E15 4 +91111 b P15 1 +91130 w B15 E13 2 +91141 b S17 1 +91196 b R18 1 +911b0 b P16 1 +91221 b C17 1 +91240 b P4 1 +91257 w D4 1 +91286 w F4 1 +912d7 w R15 1 +912f6 w P14 R14 2 +91310 w Q6 1 +91317 b L3 1 +91317 w F16 G16 2 +91327 w R15 1 +91336 w D4 1 +91337 b M16 1 +913c6 b C17 1 +913e6 w S14 1 +913f6 b Q6 1 +91491 b C2 1 +914a0 b P14 1 +914b1 w C2 1 +914e7 w D17 1 +914f0 b N2 N5 2 +91507 b B3 E3 F4 G3 G2 5 +91517 b F14 1 +91546 b G17 1 +91587 b D5 1 +91597 b G5 1 +91626 w O17 P16 2 +91636 b F18 1 +91641 b D2 1 +91646 b Q2 1 +91667 b B8 B18 2 +91681 b F3 1 +91681 w F4 E5 2 +916a6 w O16 1 +916f7 b N5 1 +91706 w S16 1 +91720 w D14 E17 F15 H17 4 +91727 b N4 O4 2 +91727 w O4 1 +91741 b N3 1 +91747 b N2 1 +91761 w D16 D14 2 +91771 w R8 1 +917a6 b M17 1 +917b7 w C17 D14 C14 H16 F15 5 +91806 b S13 1 +91811 b N18 1 +91847 b L16 1 +91857 b O13 1 +91867 b H17 1 +91876 w E17 E18 2 +91890 b G3 1 +91896 b M4 1 +918a0 w D13 1 +918a6 b Q5 1 +918a6 w O17 1 +918c0 b G6 1 +918d7 w E6 1 +91910 b P13 1 +91926 w O7 1 +91927 b Q4 R3 2 +91946 w B17 1 +91947 b D15 1 +91956 b D17 1 +91956 w Q17 1 +91971 b Q8 1 +91981 w Q5 1 +919c0 w R16 1 +919d1 b F17 1 +91a06 w M17 1 +91a11 b D3 1 +91a37 w C2 1 +91a60 w P14 1 +91a76 b R4 1 +91ac0 w P18 1 +91ac1 b O3 1 +91ad0 b C14 1 +91b36 b B18 1 +91b50 b B4 1 +91b56 b R17 1 +91b66 b C16 E16 2 +91c00 w C18 1 +91c56 b D3 1 +91c61 b L4 1 +91c90 b Q5 R5 2 +91c96 b E2 P17 O4 3 +91ce0 w D15 1 +91cf7 w E3 1 +91d31 b O18 1 +91d36 b S6 1 +91d66 w P5 1 +91d80 w O4 1 +91d81 b Q15 1 +91da1 b Q3 1 +91da6 w E5 1 +91dd6 b F4 E4 2 +91dd7 w Q18 1 +91de6 w E16 1 +91df1 b E7 1 +91e11 w N4 1 +91e57 b N15 1 +91e57 w Q17 1 +91e96 b S11 1 +91e97 w Q12 1 +91ea1 w Q7 1 +91eb1 w P17 1 +91f21 b S1 1 +91f56 b G2 1 +91f57 b D13 1 +91f76 w P16 1 +91fb6 w P15 1 +92010 b B4 C5 2 +92020 b S15 1 +92026 w S15 1 +92041 b D6 1 +920d6 b H17 1 +92100 b E16 1 +92100 w C8 1 +92120 b M4 1 +92131 w B17 1 +921a0 w G5 1 +921d0 b S14 1 +92210 b T4 1 +92240 b G14 E7 2 +92286 w Q6 1 +922a1 w R16 1 +922d1 b F18 1 +922f1 b R17 1 +92310 b C3 1 +92340 b B18 1 +92377 w Q11 1 +92391 b Q16 1 +92397 w Q13 1 +923b1 b R4 1 +923b6 w R16 1 +923c6 w C1 1 +92400 b P15 1 +92427 w S6 1 +92436 w E13 1 +92461 w M6 1 +92467 w P4 Q5 2 +92480 b G2 1 +92487 b N16 1 +924a1 b Q17 1 +924a7 b S14 1 +924c1 b N5 O4 2 +924e1 w O5 1 +92520 w E4 1 +92526 b C16 1 +92536 w E13 1 +92540 b S4 1 +92546 w Q6 1 +92556 w Q18 1 +92576 w Q18 1 +92581 w E4 1 +92590 w C2 1 +925c1 w J17 E14 E13 3 +925d1 w P3 1 +925d7 b P3 1 +92617 b Q4 1 +92621 b D7 1 +92670 w S16 1 +92671 w F15 1 +92680 b O7 R4 2 +92681 w Q6 1 +926c1 b E5 1 +926d0 w G2 1 +926d1 w L16 1 +92707 w F6 1 +92736 b M4 1 +92761 w H3 1 +92766 b H2 1 +92781 w E3 1 +927a6 b R5 1 +927b7 w B15 1 +927e7 w E4 1 +92800 w E5 1 +92807 w E2 1 +92820 w C16 1 +92836 b O2 1 +92846 b N15 1 +92866 w C17 1 +928c0 b E18 C17 D14 C14 4 +928c0 w N18 1 +928d0 w D14 1 +928e0 w Q5 1 +928f0 w D9 1 +92906 b O3 1 +92910 w B18 1 +92930 b B3 1 +92931 w E19 1 +92936 w E12 1 +92967 w O14 1 +92980 w P3 1 +92990 b R15 1 +929a1 w F7 1 +929a6 w G4 1 +929a7 w F4 1 +929b6 b R13 1 +929c0 b Q15 1 +929c7 w F15 1 +929d0 w S5 1 +92a06 b C3 1 +92a07 w S16 1 +92a26 w Q18 1 +92a77 b E14 1 +92ab0 w M7 1 +92af6 b B19 1 +92b06 w C2 1 +92b41 b E3 1 +92b51 b S13 1 +92b51 w H17 1 +92b57 b Q17 1 +92b80 w P14 1 +92b86 b F2 1 +92b96 w C5 1 +92b97 b B13 1 +92ba7 b H4 1 +92bb0 w E7 1 +92bb1 w Q14 R13 2 +92bd6 b B3 1 +92bf1 w O8 1 +92c10 w Q7 1 +92c66 w Q17 1 +92c90 b M3 1 +92cc7 w R13 1 +92cd0 b Q16 1 +92ce1 b P4 1 +92ce7 b D11 1 +92ce7 w G17 1 +92d80 w D3 D4 2 +92d97 b F3 1 +92db6 b P5 1 +92dc1 w R6 1 +92e06 b Q16 1 +92e30 w Q5 1 +92ec6 b D6 1 +92ed0 b H5 1 +92ed6 w C4 1 +92ed7 b E17 1 +92ee6 b F4 1 +92ef6 w C5 1 +92ef7 w B18 1 +92f01 b D18 1 +92f07 w R7 1 +92f16 b F4 1 +92f50 b F16 R5 2 +92f51 w P4 1 +92f77 w P6 1 +92f80 b S17 1 +92f91 b Q3 1 +92fa1 b R16 1 +92fc6 w G2 1 +92fd0 w R18 1 +92fd6 b G6 1 +93017 w F5 1 +93051 b B14 1 +93086 b C13 1 +93090 b Q17 1 +930a0 w D15 1 +930f1 b D7 1 +930f7 b C11 1 +93111 b R18 1 +93177 b G3 1 +93180 w C4 1 +93197 b C3 1 +931a0 b C15 1 +931e1 w G16 1 +93237 w R3 O5 R6 Q6 4 +93240 b R6 1 +93247 w R2 1 +932a0 w M3 1 +932a1 b B5 1 +932c1 b C17 1 +932e1 w Q4 P3 R5 O4 Q6 5 +93326 b Q12 1 +93327 b T5 1 +93336 w F7 1 +93337 w E13 1 +93351 w C17 1 +93360 b G3 1 +93376 b Q18 1 +93381 b N4 1 +933b7 w F19 1 +933f7 b B4 1 +93427 w C16 1 +93430 w P3 1 +934b0 w D13 1 +934b7 b C11 1 +934e7 b H17 1 +93507 b C3 1 +93540 w C8 1 +93551 b S6 1 +93551 w J5 1 +93557 b G17 F16 2 +935b1 b N17 1 +935f0 b Q8 1 +93607 b O14 1 +93610 w Q15 1 +93616 w C6 E16 2 +93620 b R5 1 +93651 b P18 1 +93691 w B7 1 +936c0 b E16 1 +936c7 b E3 1 +936d0 w S14 1 +936d7 w Q5 1 +93761 b O5 1 +93767 b O16 1 +93767 w S6 1 +937d6 b P5 1 +937d7 w P2 1 +93820 w G16 1 +93827 b M4 1 +93861 w G5 1 +93871 b D17 1 +93896 b P15 S15 2 +938a6 b D18 1 +938d7 b Q5 1 +93981 w Q7 1 +93990 b G15 1 +939a1 w D9 1 +939c6 w P17 1 +939e1 b E3 1 +939f6 w D2 1 +93a56 b D16 1 +93a61 w F15 1 +93b10 b F15 1 +93b50 w P5 1 +93b51 w H16 1 +93b60 b B7 1 +93b61 w S15 1 +93b86 b F16 1 +93b90 w C4 1 +93ba7 w R6 1 +93bd6 w R3 1 +93bd7 w Q17 1 +93c40 b M5 1 +93c47 w Q9 1 +93c66 b M19 1 +93c96 w G16 1 +93cb6 b C16 D17 2 +93d11 w C16 N15 2 +93d21 w C4 1 +93d26 w G16 1 +93d61 w L16 1 +93d96 b R15 1 +93db1 w P5 1 +93e01 w G18 1 +93e11 b S4 1 +93e57 w E14 1 +93ea0 w E13 1 +93ed0 w F6 1 +93ed1 b R15 1 +93ed7 w C4 1 +93ee0 b R16 1 +93f06 b Q6 1 +93f47 b R2 1 +93f60 w B15 1 +93f71 b M16 1 +93fd7 w B3 1 +93fe1 w S18 1 +93ff7 w M17 1 +94047 w B2 1 +94056 b E17 1 +94066 w B17 1 +94080 b P3 O6 2 +94096 b C14 1 +940d1 b O16 1 +94117 b F13 1 +94131 w F15 1 +94147 w B3 1 +94187 b G3 1 +941a7 w Q14 1 +941d1 b Q3 1 +94206 w P6 1 +94210 b Q18 1 +94237 b B18 1 +94270 w D5 1 +942b0 w C17 1 +942e1 b S17 1 +94311 w S6 1 +94361 b P7 1 +94366 b S17 1 +94381 w C3 1 +94401 b P15 1 +94411 b F18 1 +94416 b C6 1 +94437 b M5 1 +94441 b N16 1 +94467 b O18 1 +944a7 b G17 1 +944c0 b Q4 1 +94506 w B14 1 +94531 b F14 1 +94540 b P19 1 +94551 b F12 1 +945a7 w O5 1 +945d7 b R3 1 +945f1 b P3 1 +94610 w J16 1 +94621 w F15 1 +94670 w P16 1 +94707 w R15 1 +94711 b C3 1 +94711 w R17 1 +94770 b O3 1 +94776 w D8 1 +947c7 b F5 1 +947f0 w O15 1 +947f1 b D3 1 +94800 w N4 1 +94801 b S8 1 +94830 b P8 1 +94851 w R15 1 +94866 w R11 1 +94877 w B6 1 +948a0 b E2 1 +948a1 b R4 P4 2 +948c7 w H14 1 +948f1 w Q17 1 +94900 b D16 1 +94931 w R13 1 +94990 b O4 R4 2 +949e6 w Q7 1 +949f0 w B5 1 +949f7 w M3 1 +94a00 w O3 1 +94a21 b L14 1 +94a30 b B17 1 +94a80 w R15 1 +94ab7 b C6 1 +94ae1 b Q16 1 +94af6 b B15 1 +94b00 w T4 1 +94b10 w R15 1 +94b31 b R18 1 +94b36 b F3 G4 2 +94b50 w C14 1 +94b90 w C17 1 +94ba1 w C9 R17 2 +94ba7 b P17 1 +94bd7 w Q14 1 +94c27 b C2 C5 D6 C7 B7 5 +94c41 w R5 1 +94c86 w R18 1 +94c91 w Q3 1 +94cf1 b M16 1 +94cf6 w M17 1 +94d36 b E6 C6 2 +94d77 w C3 1 +94dc7 b B2 1 +94e00 w D6 E4 2 +94e40 w P1 1 +94eb1 b S3 1 +94f10 w R7 1 +94f47 w D14 1 +94f57 w F16 1 +94f67 b E1 1 +94f97 b P16 1 +94fa1 b F15 G16 E17 3 +94fe7 w D2 1 +94ff1 b O16 1 +95050 b Q2 Q4 2 +95070 w C17 1 +95097 b O18 1 +950a0 b A6 1 +950a6 b B17 1 +95107 w S3 1 +95116 w Q3 1 +95140 w S5 1 +95180 b Q14 1 +951a7 b L3 1 +951b1 w P3 1 +951b6 b O18 1 +95206 b B9 1 +95220 w D6 1 +95221 w Q4 O4 2 +95231 w P5 1 +95277 b M3 1 +95280 b S17 G18 2 +95287 b C3 1 +952b6 w S4 1 +952c0 w E16 1 +952e1 b H5 1 +95347 w E18 E17 E3 3 +95386 b C18 1 +953a1 w S16 1 +953e0 w D18 1 +953f0 w P2 1 +953f6 b R16 1 +95420 b F11 1 +95427 w S3 1 +95436 w R8 1 +954a6 w C11 1 +954a7 w S6 1 +954b1 w P18 1 +954c6 b E13 1 +954f1 w C8 B5 B4 3 +95506 b Q6 1 +95546 w E7 1 +955c6 w P18 1 +955e0 w D12 1 +955e7 w C14 E18 2 +95696 w P6 1 +956b1 b F5 1 +956c7 b O8 1 +956d7 b Q17 1 +95740 w F16 1 +95747 b P18 1 +95751 b E17 1 +95780 w A16 1 +95781 w N14 1 +95790 b R5 1 +95806 w D5 1 +95817 b E18 1 +95830 w E12 1 +95837 w Q13 P17 2 +95857 w H17 1 +95877 w P3 1 +95891 w Q9 1 +958d1 w E18 1 +958d7 w R11 1 +95906 b G6 1 +95920 b G4 1 +95937 w S2 1 +95950 w O8 1 +959a0 w P4 1 +959e1 b H16 1 +95a10 b M15 M14 2 +95a26 b D2 1 +95a61 w D17 1 +95a66 b C6 G3 2 +95a66 w F18 1 +95a67 w B4 1 +95a70 w P4 1 +95af1 b C17 1 +95b06 w E2 1 +95b51 b P17 R14 2 +95b51 w S15 1 +95b57 b C3 1 +95b81 w D11 1 +95bb0 w B6 1 +95be0 w P4 1 +95c11 b D6 1 +95c36 b D2 1 +95c46 b P18 1 +95c50 b R17 R15 2 +95c61 b O6 1 +95c81 w M14 1 +95cc0 w F4 1 +95cf0 b P3 1 +95d27 b B17 1 +95d30 w F3 1 +95d46 w C6 1 +95d60 w Q6 1 +95d90 w F17 1 +95da1 b O16 1 +95db0 b N16 1 +95dc7 w F18 1 +95dd6 w E15 1 +95dd7 b R17 1 +95e06 b R5 1 +95e11 w D6 1 +95e16 b B16 1 +95e46 b R17 1 +95e46 w O4 B17 2 +95e50 b S7 1 +95e61 w E2 1 +95e80 w O6 1 +95e86 w F16 1 +95ef0 b N8 1 +95f30 w J3 1 +95f56 w O3 1 +95f66 b S18 1 +95f67 b J17 1 +95f91 b H15 1 +95fb0 b N16 1 +95fc1 b H2 1 +96017 b L2 1 +96057 b R5 1 +96060 b P16 1 +960a6 b S5 1 +960b1 b T3 1 +960b6 b R8 1 +960d0 w R14 1 +960e0 w A18 1 +96147 b R12 1 +96166 w H3 1 +96180 b D15 1 +96180 w H15 1 +96181 w S12 1 +961c1 w F6 1 +96200 b S15 1 +96207 b C8 1 +96220 w E17 1 +96247 w F14 1 +962f7 w S15 1 +96316 w D5 1 +96331 w R5 1 +96361 b D17 1 +96387 b E16 1 +96391 w D18 1 +96397 b G16 1 +963a6 w E18 1 +963b6 w D2 1 +963b7 w E3 1 +963c1 w O18 1 +963d0 b R15 1 +963e0 w D8 1 +96440 b S14 1 +96457 b L16 1 +964e6 b E17 1 +96546 b D6 D7 2 +96547 b S5 1 +96571 w Q11 1 +96580 w R15 1 +965f0 b R6 1 +965f6 b O14 1 +96607 w E17 1 +96610 b B3 1 +96647 b R11 1 +96661 b R4 E12 2 +96670 b O18 1 +96686 w B4 E16 2 +96696 b R6 1 +966c1 w R17 1 +966f6 b C14 1 +96700 w C16 1 +96730 w N8 1 +96747 b G8 1 +96777 b S14 1 +96780 w R8 1 +967b0 w G5 1 +96816 w B17 O14 2 +96820 w N17 P18 2 +96840 w H14 1 +96867 w O7 1 +968c0 w O16 1 +968f0 b H14 1 +96900 b Q7 1 +96900 w P15 1 +96910 b E7 1 +96937 b E18 1 +96937 w O4 1 +96960 w D15 1 +96970 w O5 1 +96987 b R15 1 +96991 b C11 C13 2 +969b0 b R14 1 +969b1 w O18 1 +969d1 w F7 1 +969d6 w R8 1 +969f6 b C15 1 +96a11 b D5 E4 2 +96a17 w C15 1 +96a81 b P17 1 +96aa0 b D15 1 +96aa1 b Q14 1 +96ab0 b Q14 1 +96ab1 b E13 1 +96af0 b M5 1 +96b10 b C15 1 +96b16 b R14 1 +96b27 b D3 B15 2 +96b31 b P5 P6 R3 R7 4 +96b51 b C18 1 +96b61 b D14 1 +96b67 w Q2 S4 2 +96b86 w E6 1 +96b91 b Q16 1 +96bd1 w D3 1 +96be1 b O2 1 +96be1 w C14 1 +96c27 b Q18 1 +96c56 b O2 1 +96c76 w C18 1 +96c87 w O3 S5 2 +96cb0 b C3 1 +96cc1 w D4 1 +96cf7 b D3 1 +96d37 b C3 1 +96d60 w G17 1 +96de7 w B3 1 +96df7 w C17 1 +96e01 w R9 1 +96e51 b B12 1 +96e61 w R14 O17 Q14 O16 Q13 N16 R13 N17 R11 L17 10 +96e66 b F13 1 +96ea1 b C5 1 +96f01 w M17 1 +96f07 b R6 1 +96f10 w E13 1 +96f80 w C13 1 +96fb0 w D17 1 +96fc1 b R16 1 +96ff1 b D16 1 +97031 b O14 1 +97040 w Q11 1 +970c0 b H4 1 +970c7 b G5 1 +970d1 w P2 1 +970d7 w B6 1 +970f0 b H6 1 +97111 b B3 1 +97116 b B17 1 +97116 w R17 1 +97120 b O4 O5 2 +97151 w C4 1 +97156 w B4 1 +971a1 b E6 1 +971a1 w H3 1 +971c6 w Q14 Q13 2 +971d1 b N2 1 +971f6 w D6 1 +97237 w D12 1 +972a1 w P14 1 +972e0 b L16 1 +972e7 w E14 1 +97327 w B16 1 +97346 b C16 1 +97347 w D2 1 +973a0 b Q2 1 +973b7 w S6 1 +973c6 b C18 1 +973e6 w D3 1 +973e7 b D11 1 +973f0 w S2 1 +97457 w P4 1 +974d1 b P8 1 +974e6 w Q15 1 +97510 w C5 C2 2 +97521 w H3 1 +97526 b E4 1 +97537 b C5 1 +97557 w C15 1 +97571 w R11 1 +975c0 b O6 1 +975d1 w D17 1 +97606 b R2 1 +97620 b R7 P5 Q7 P6 4 +97636 b P4 1 +97667 w G17 1 +97687 w Q2 1 +976a6 w D16 1 +976b0 w B17 1 +976b7 b P2 1 +976b7 w E6 1 +976c7 b N3 D7 C3 3 +97746 w C15 E14 2 +97767 b P3 1 +97770 b C7 1 +97771 w C16 1 +977d0 w C8 1 +977e0 b R17 1 +977e1 b P17 1 +977f0 w O5 1 +97817 b H13 1 +97817 w R5 1 +97827 w B7 1 +97836 b Q1 1 +97837 b R7 1 +97840 w D11 1 +97860 w R6 1 +97880 b E18 1 +97897 b D4 1 +978c7 b S5 1 +97907 b E8 1 +979f1 w F3 1 +97a06 w D3 1 +97a07 w Q9 1 +97a20 w P4 1 +97a27 b F3 D5 2 +97a60 w B11 1 +97a76 b E14 C14 2 +97a86 w N18 1 +97ab7 w L4 1 +97ac0 w E15 1 +97ad6 b O4 1 +97b41 b Q15 1 +97b47 b D16 1 +97b50 b R14 1 +97bb6 b D3 1 +97bb7 b F14 1 +97bd0 w O17 P14 2 +97be1 b G16 1 +97c30 w C5 1 +97c40 b D4 1 +97c51 w H18 1 +97c66 b P3 1 +97c70 b R15 1 +97c71 b G5 1 +97c71 w S8 1 +97c87 w L5 1 +97cf7 b E4 1 +97d00 w Q5 1 +97d36 b B3 1 +97d61 w R12 1 +97d81 w R17 1 +97df7 w S16 1 +97e07 w Q8 1 +97e31 b B4 1 +97e50 b G3 E5 2 +97ec6 w S6 1 +97f30 b D7 1 +97f41 w R14 1 +97f51 b S16 1 +97f56 b O16 1 +97f96 w S14 1 +97fa0 w N3 1 +97fc1 b P4 1 +97fc1 w B18 1 +97ff6 w P7 1 +98031 w L18 1 +98077 w R2 1 +98087 w O14 1 +980b0 w R13 1 +980b7 b D16 1 +980f6 w D2 1 +98116 w C8 1 +98157 w E6 1 +98161 w C15 1 +98171 b R15 1 +98176 b E5 1 +981c0 w R15 1 +98230 w C7 1 +98241 b S4 1 +98267 w R17 P14 O17 O16 4 +98287 w N18 1 +982a6 b R3 1 +982a6 w S18 1 +982d6 b L14 1 +982f0 w N16 Q16 O15 3 +98301 w P15 1 +98331 b C17 1 +98390 b H14 1 +98391 w M5 1 +983c1 b E6 1 +983d7 w G17 1 +98420 w E6 1 +98430 b R3 1 +98436 w E3 1 +98451 w P15 P17 2 +98457 b D14 1 +98461 w N5 1 +98486 b G6 1 +98491 b F4 1 +984b0 w P16 1 +984c6 w F12 1 +984d0 b R14 1 +984e1 w Q17 1 +984f0 w H5 1 +98506 b R4 1 +98591 w C15 D16 M4 3 +985c1 b E3 1 +985e7 w G15 1 +98607 b P3 1 +98646 b D14 1 +986b7 w E3 1 +986c1 b G16 1 +98721 w O16 1 +98750 b P6 O4 2 +98761 w B7 1 +98787 b D4 1 +98790 w S4 R6 2 +987b6 w C16 B15 2 +98801 b G16 D16 P2 3 +98801 w D18 1 +98836 w P6 1 +98840 b R12 1 +98846 b C13 1 +98870 b P16 1 +98896 b C14 1 +988a6 w P5 1 +988d1 b Q18 R17 Q17 R12 4 +98910 b G18 1 +98930 w C3 1 +98947 b B2 1 +98950 w Q8 1 +989a1 w D4 1 +989f7 b Q7 1 +98a07 b C4 1 +98a07 w D14 1 +98a21 b N4 1 +98a96 w B18 1 +98aa1 b F4 1 +98ab6 b G4 1 +98ac7 b F3 1 +98af1 b S14 1 +98af6 w Q6 1 +98b01 w E13 1 +98b11 b R15 1 +98b41 w D9 1 +98b66 w P5 1 +98b70 w E14 1 +98bb6 w D7 1 +98bc7 w O4 1 +98bf7 b Q18 1 +98c10 w F5 1 +98c27 b O17 1 +98c40 b Q5 1 +98c51 b G4 D4 2 +98c97 b C16 1 +98ca1 b C2 1 +98ce0 w J3 1 +98d01 b S15 1 +98d17 b E5 1 +98d37 b R3 1 +98d66 b F4 1 +98d66 w G5 1 +98d67 b D3 1 +98dc0 b E17 1 +98df1 w D17 1 +98e20 w D17 1 +98e30 b C7 1 +98e60 w C12 1 +98e66 w C3 1 +98e87 b C6 1 +98e97 b B17 1 +98eb1 b D7 1 +98eb6 w G5 G6 2 +98ed1 w B17 1 +98ee6 b C4 1 +98fd6 w H17 1 +98fe0 w N15 1 +98fe1 w C6 1 +99017 w E18 1 +99021 b P17 1 +99061 b E16 1 +99066 w D3 1 +99071 w S16 1 +99081 b O7 1 +990b1 b B5 1 +990b6 b P7 1 +990b6 w F18 1 +990c0 w F5 1 +990d6 w S8 1 +990f1 b D5 1 +99120 b F14 1 +99127 b B6 1 +991d6 b R12 1 +991e0 b O7 1 +99220 w B6 1 +99231 w Q16 1 +99250 b R7 P5 S3 3 +99267 w R2 1 +99287 w F3 1 +99290 w Q9 1 +992a1 w Q18 1 +992b6 b D7 1 +992c0 w R9 1 +992d7 b B15 1 +99301 b O15 1 +99301 w P5 1 +99326 w B16 1 +99370 b E14 1 +99376 w D4 1 +993c0 b Q12 1 +993c6 w E5 E4 2 +99411 w C15 B6 2 +99421 w O2 1 +99426 w Q15 P16 2 +99430 b B18 1 +99460 w P5 1 +99471 b O2 1 +994b6 w Q3 1 +994c0 w G4 1 +994c1 w F16 1 +99506 w D5 1 +99510 w G4 1 +99531 w Q2 1 +99551 w E3 1 +99560 w P2 1 +99567 b C17 1 +99576 w R15 1 +995e6 w O2 1 +99640 b B4 1 +99641 w R4 1 +99650 w C15 1 +99667 w D3 1 +99670 b D1 1 +99670 w Q13 1 +996c0 w B18 1 +996d7 w D5 1 +996e6 b J3 1 +99716 b P8 1 +99720 b R17 1 +99757 b H16 1 +99796 b H5 1 +99817 w Q18 H2 2 +998c6 b D13 1 +998d0 w F15 1 +998f7 b Q17 1 +99921 w E14 1 +99936 w S3 1 +99957 b C2 1 +99960 b F14 1 +999a7 w E5 1 +999e1 w N14 1 +999e6 b S7 1 +999f7 b P2 1 +99a56 b Q13 R13 R17 3 +99a57 b Q17 1 +99a57 w S3 1 +99ab7 b D15 1 +99ac1 b E13 1 +99ae1 w R1 1 +99ae7 w E12 1 +99b11 w G2 H5 2 +99b57 w S17 1 +99ba0 b G3 1 +99ba7 b C4 1 +99be6 w E18 1 +99bf0 w P16 1 +99c21 b C16 1 +99c37 w O16 1 +99c86 b S12 1 +99c91 b D14 1 +99cd7 b R4 1 +99cd7 w R3 Q4 P4 O5 O4 N4 6 +99ce6 b B3 1 +99cf0 w R11 1 +99d16 w C12 1 +99d17 w S15 1 +99d70 w N4 1 +99d97 b M17 1 +99de1 w R2 1 +99e11 b B17 1 +99e37 w B8 1 +99e47 b C15 D16 2 +99e50 b E15 1 +99e50 w M3 1 +99e66 w F6 1 +99e71 b D3 1 +99e81 w C18 1 +99e97 b B5 1 +99ec6 b F17 1 +99ec7 w F17 D16 2 +99ee1 b D18 1 +99ee6 b C6 D4 2 +99ee6 w D5 1 +99ef0 w S15 1 +99ef6 w G18 1 +99f26 w D18 1 +99fb1 b P4 1 +99fc7 w C13 1 +9a010 w D14 1 +9a027 b N17 1 +9a046 w F2 1 +9a080 b O15 1 +9a0b7 b G2 1 +9a0c0 w F2 1 +9a0d1 b O4 1 +9a0e1 b B6 1 +9a0f6 w F18 1 +9a100 b L17 1 +9a100 w H15 1 +9a180 b Q18 1 +9a180 w Q15 1 +9a196 w M3 1 +9a1d1 b C3 1 +9a251 w G2 1 +9a257 b G14 1 +9a257 w D3 1 +9a2c0 b C17 1 +9a2c1 b Q13 1 +9a2f1 b E2 1 +9a2f1 w B4 1 +9a300 b D3 1 +9a301 b T16 1 +9a307 w C6 1 +9a330 b F14 1 +9a340 w R15 1 +9a350 b C7 E5 2 +9a356 w E18 1 +9a377 b P14 1 +9a3d7 b D13 1 +9a401 b R17 1 +9a406 w F16 1 +9a420 b B15 1 +9a441 w A18 1 +9a451 b S13 1 +9a460 b R5 1 +9a481 w P15 1 +9a490 b D2 1 +9a4a6 b E15 1 +9a4b7 b C17 D12 2 +9a506 w O16 1 +9a507 w B4 1 +9a511 w D13 D16 2 +9a517 b H17 1 +9a537 w E8 1 +9a540 b E14 1 +9a547 b R6 1 +9a551 w C14 1 +9a5b0 w N15 1 +9a5d0 w Q18 1 +9a5f7 b B4 1 +9a626 b E4 1 +9a671 b Q3 1 +9a680 b O16 1 +9a691 b O3 1 +9a696 w R4 1 +9a6a0 b P17 1 +9a6c7 w P18 S14 2 +9a6f6 b C14 D14 2 +9a731 b Q6 1 +9a766 b T17 1 +9a767 b E18 1 +9a770 b P6 1 +9a790 w P6 1 +9a7b1 b N18 1 +9a7c1 b B3 1 +9a800 b M4 1 +9a801 w C6 1 +9a837 b C17 1 +9a841 b R4 1 +9a877 b P3 1 +9a890 w F17 1 +9a896 b Q6 1 +9a8c6 w D17 1 +9a8f1 w E15 1 +9a910 b O12 1 +9a921 b P14 1 +9a936 b D6 1 +9a946 b E2 1 +9a996 w B9 1 +9a9b6 w R15 1 +9a9c1 b S5 1 +9aa01 w G2 1 +9aa20 w O19 1 +9aa86 w O16 1 +9aac7 b B9 1 +9aad6 w C12 R8 2 +9ab16 w F6 1 +9ab20 w E7 1 +9ab27 b D17 1 +9ab56 b R15 1 +9ab57 w C15 1 +9ab70 w P5 1 +9ab86 w F5 1 +9abd7 b R3 1 +9abf7 b O5 1 +9ac06 b Q8 1 +9ac10 b G3 1 +9ac51 b R4 1 +9ac51 w Q6 1 +9ac81 w D14 1 +9acc6 w B6 1 +9ad07 w Q17 1 +9ad17 b S2 1 +9ad27 b Q3 1 +9ad30 w Q3 1 +9ad37 w O4 1 +9ad47 b D18 1 +9ad80 w M15 1 +9ada0 b F5 1 +9ade0 b Q17 1 +9ade6 b D6 1 +9adf6 b C3 1 +9ae36 b B3 1 +9ae60 b O6 1 +9ae70 w P12 1 +9ae76 w O18 1 +9ae80 w O8 1 +9ae81 b P3 D4 2 +9ae81 w E3 F3 E4 D5 4 +9ae87 w C15 C14 D14 D15 4 +9aeb7 w J16 1 +9aec1 w N17 1 +9aee0 b B5 1 +9af66 b C14 1 +9af97 w M6 1 +9afe1 w E12 1 +9b011 w C5 1 +9b021 w F4 1 +9b040 b R4 1 +9b041 w D17 1 +9b071 w F15 1 +9b086 w C15 1 +9b090 b B13 1 +9b0a1 b F15 1 +9b0f7 b O4 P5 2 +9b101 b E3 1 +9b110 w G11 D6 2 +9b116 b Q15 1 +9b121 b C6 1 +9b121 w D6 E5 2 +9b131 b P2 1 +9b147 w G2 1 +9b186 w O17 1 +9b1b1 w B14 1 +9b1b6 w Q6 1 +9b1d0 w M3 1 +9b1d7 b D2 1 +9b1f1 w R6 1 +9b206 b S8 1 +9b220 w A16 1 +9b246 b C16 1 +9b261 w C13 1 +9b271 w E4 1 +9b280 b D6 1 +9b291 b N15 1 +9b2a0 w Q5 1 +9b2d6 w P16 1 +9b337 w R14 Q16 2 +9b346 w N14 1 +9b371 b C12 1 +9b396 w S9 1 +9b3b0 w F5 1 +9b3e1 b Q13 1 +9b3f1 w R13 1 +9b411 w B3 1 +9b426 b D7 1 +9b491 w F3 1 +9b4a0 b D4 1 +9b4a6 b E3 Q2 2 +9b4c7 w R17 1 +9b4e1 w B16 1 +9b4f0 w H17 1 +9b4f6 w S8 1 +9b501 b Q4 1 +9b531 w C6 1 +9b556 w F15 1 +9b576 b S4 1 +9b581 b D17 1 +9b586 b P14 1 +9b590 w H13 F12 2 +9b631 b C15 1 +9b676 w E5 1 +9b691 w Q9 1 +9b6a0 w P17 1 +9b6a7 b R18 1 +9b6b1 b R12 1 +9b6b7 b O6 1 +9b6c0 w D18 1 +9b6c6 w E5 1 +9b6e6 b D13 1 +9b6f7 w M4 1 +9b721 b Q14 1 +9b730 b R3 R5 2 +9b736 w P6 R6 2 +9b786 w P15 1 +9b871 b B7 1 +9b897 b G3 1 +9b8c0 w Q3 1 +9b907 w R13 1 +9b927 w S13 1 +9b940 w B4 1 +9b947 b E3 1 +9b947 w D6 1 +9b951 b D4 1 +9b961 w P18 1 +9b967 w E14 1 +9b976 w R14 1 +9b9d1 b R18 R4 2 +9b9f1 b P6 1 +9ba00 w P4 1 +9ba31 b Q5 1 +9ba31 w P4 P5 Q7 R3 4 +9ba41 b P2 1 +9ba77 b R17 1 +9ba97 w N15 1 +9baa1 b Q13 1 +9bab1 w B14 1 +9bac6 b B3 1 +9bae7 w Q18 1 +9baf7 b B7 1 +9bb50 b B5 C4 2 +9bb70 b M7 O8 2 +9bb71 w R16 1 +9bb76 b Q3 1 +9bb81 w E5 1 +9bba7 w S4 1 +9bbe0 w C16 1 +9bc16 w T2 1 +9bc80 b P6 1 +9bcb6 w D15 1 +9bcc7 b C16 D15 C17 3 +9bd11 b F18 1 +9bd20 b O4 1 +9bd20 w S18 1 +9bd30 w C7 E5 D7 3 +9bd57 w Q4 1 +9bd60 w E1 1 +9bd91 b N5 1 +9bda0 w Q19 1 +9bdc1 b S8 1 +9bdd1 b E4 D5 2 +9be16 w C8 1 +9bec1 b B3 1 +9bef6 b E4 1 +9bef7 b R8 1 +9bf40 w D6 1 +9bf67 w Q11 1 +9bf97 b O15 1 +9bfe1 b S3 1 +9c006 w C12 1 +9c031 b J17 G17 2 +9c037 b E4 1 +9c051 w G18 1 +9c056 b R9 1 +9c066 b F15 1 +9c077 b P4 1 +9c081 b S16 1 +9c090 b D6 1 +9c090 w H17 B2 2 +9c0b7 w O1 1 +9c0c1 w E16 1 +9c0d6 b R16 1 +9c0e0 w R15 R13 2 +9c116 b C18 1 +9c190 w J4 1 +9c1e7 w Q17 1 +9c1f7 w P17 1 +9c240 b Q4 1 +9c276 b R2 1 +9c287 b C14 1 +9c2a1 b S2 1 +9c2a1 w G17 1 +9c2d1 b R18 1 +9c2e7 w R3 1 +9c2f7 w R7 1 +9c311 b C18 1 +9c316 b C8 1 +9c321 w P14 1 +9c327 w S14 1 +9c337 w D3 1 +9c361 b F6 G7 2 +9c397 b D16 1 +9c3d7 b F4 G5 2 +9c410 w P4 1 +9c431 b B15 1 +9c451 b M18 1 +9c477 w L4 1 +9c480 b B17 1 +9c496 w O16 P16 2 +9c4d7 b D4 1 +9c4e0 w R4 Q4 R7 P5 R3 5 +9c4f0 w B2 1 +9c4f6 w N17 1 +9c500 b P3 1 +9c506 b C7 1 +9c507 b S9 1 +9c510 w Q6 1 +9c520 b C13 1 +9c520 w R13 1 +9c540 w Q13 1 +9c546 b Q4 1 +9c556 b C14 1 +9c5a7 w C14 1 +9c5c7 w F16 1 +9c5d6 w E15 1 +9c5e0 w F13 1 +9c5e1 b Q9 1 +9c5e1 w C15 1 +9c616 b B3 1 +9c647 w R17 1 +9c670 b P17 1 +9c670 w C6 1 +9c696 w C3 1 +9c6c7 w P6 1 +9c6d1 b O16 1 +9c740 w O6 1 +9c760 b G18 1 +9c7b0 b S5 1 +9c7b7 w D6 1 +9c7d0 b S18 1 +9c801 w D17 1 +9c807 w L3 N3 2 +9c817 b C17 1 +9c827 w C16 B2 2 +9c837 w S15 1 +9c8b0 b R6 1 +9c8b0 w Q6 R6 R7 O4 M3 M4 L4 7 +9c8c0 b R16 1 +9c8e1 b R4 1 +9c8f6 w R13 1 +9c906 b D8 1 +9c9a7 w Q6 1 +9c9c6 w N17 1 +9c9d0 w C15 1 +9c9e1 w F5 1 +9c9e6 w Q14 1 +9c9f6 w Q17 1 +9ca36 w J18 1 +9ca37 w P6 1 +9ca77 w N17 1 +9ca86 w Q2 1 +9caa7 b S12 1 +9cad6 w D7 1 +9cb00 w B4 Q18 2 +9cb06 b T17 1 +9cb40 w P15 1 +9cb41 b R7 1 +9cb47 w C1 1 +9cb57 w C6 1 +9cb60 w Q3 1 +9cb81 w R2 1 +9cba7 b Q5 1 +9cbc6 w M16 1 +9cc11 b F3 1 +9cc60 b Q17 1 +9cc71 b C14 1 +9cc81 b R13 1 +9cc91 w D3 1 +9ccc7 w S16 1 +9ccd0 b G5 1 +9cd10 b D14 1 +9cd30 b P18 1 +9cd61 w C4 1 +9cdb6 b E14 1 +9cdd1 b F4 1 +9cdd6 b E17 1 +9cde6 w C13 1 +9ce00 b D14 1 +9ce01 w G12 1 +9ce37 b R13 1 +9ce50 w P17 1 +9ce96 b O3 1 +9cea0 b R17 1 +9ceb7 b M4 1 +9ceb7 w O7 1 +9cec0 w D3 B15 2 +9cf27 b O16 1 +9cf50 b P3 1 +9cf67 b N18 1 +9cfb6 b C14 1 +9cfd7 w N7 1 +9cfe6 w O16 N16 2 +9cff0 b E18 1 +9d001 w P2 1 +9d040 w Q12 1 +9d041 w F16 1 +9d057 b E18 1 +9d060 b S18 1 +9d067 b H2 1 +9d071 b E7 1 +9d077 w R4 1 +9d080 b E14 1 +9d0a1 b Q18 1 +9d0b6 w B3 1 +9d0c1 w Q13 1 +9d0d0 w S17 1 +9d0f0 w C14 1 +9d126 b G6 1 +9d157 w F7 1 +9d160 w E3 1 +9d180 w D3 1 +9d1a1 w R17 Q17 R11 R12 S15 R16 6 +9d1b0 b B6 1 +9d1b0 w Q12 1 +9d1b1 w Q7 O8 2 +9d1d6 b Q13 1 +9d1e7 b R16 1 +9d1f7 b E3 1 +9d246 b S3 1 +9d257 b C8 1 +9d280 w R16 1 +9d2a1 b S13 1 +9d2b6 b Q17 1 +9d2b7 w N16 1 +9d2d7 w R4 R3 2 +9d300 b G17 1 +9d301 w O16 1 +9d331 b S17 1 +9d371 b Q12 1 +9d396 w P18 1 +9d397 w B5 1 +9d3a7 b Q13 R17 2 +9d3f0 w P4 1 +9d430 w G2 1 +9d450 w F8 1 +9d451 w D4 E3 C5 F4 D6 5 +9d497 w E17 1 +9d4c1 w E2 1 +9d4e7 b F15 E14 2 +9d4f0 w C8 1 +9d501 w P2 1 +9d540 b P5 1 +9d547 b Q17 1 +9d547 w R17 Q16 Q15 P14 Q14 Q13 6 +9d551 w L17 P14 P13 3 +9d560 b R4 1 +9d566 b C2 1 +9d571 w E12 1 +9d5a6 b J2 1 +9d5d0 b P15 C11 2 +9d641 w D4 1 +9d697 b B18 1 +9d6b1 w O17 1 +9d6b6 b E15 1 +9d6c6 w E15 1 +9d711 b F5 1 +9d766 b B12 1 +9d770 b O3 1 +9d770 w Q6 1 +9d7a1 b P7 1 +9d7c0 b S15 1 +9d816 w H17 1 +9d841 b Q3 1 +9d8b0 b C14 1 +9d8d1 b R13 1 +9d8d7 b M7 1 +9d8f0 b C17 H17 2 +9d900 b D13 D17 2 +9d920 b Q14 1 +9d990 w O16 1 +9d9a7 w N4 1 +9d9b6 w C16 1 +9da16 b P15 1 +9da51 b S15 1 +9daa1 w C18 C15 D14 C13 B13 5 +9dab7 w O4 1 +9dac0 b H7 1 +9dac7 w R6 1 +9dad1 b M16 M5 B4 3 +9db11 w G6 1 +9db30 w F15 1 +9db37 b D11 1 +9db40 b Q6 P18 2 +9db77 b D14 B1 2 +9db86 w O6 1 +9dbd1 w C17 1 +9dbd6 w F17 1 +9dbf7 w Q19 1 +9dc00 w Q16 1 +9dc20 w C13 1 +9dc51 b Q3 1 +9dc76 b O17 1 +9dc80 b C15 1 +9dca6 w M17 1 +9dca7 b N2 1 +9dcc7 b E16 1 +9dd31 b C18 1 +9dd56 b G16 1 +9dd60 b C17 1 +9dd71 w L15 1 +9dd80 b B15 1 +9dda6 b R7 1 +9ddb7 b D16 1 +9ddd6 b O16 C8 2 +9dde0 w N16 1 +9de10 b D13 1 +9de26 b Q18 1 +9de37 b R18 1 +9de56 b P2 1 +9de77 b O3 1 +9df16 b R18 1 +9df20 w F13 1 +9df31 b N16 M17 L17 3 +9df41 b P3 1 +9df86 b M3 1 +9dfa0 b L17 1 +9dfa1 b O16 1 +9dfa1 w P4 1 +9dfa7 w P14 1 +9dfc1 w C14 1 +9dfe0 b D13 1 +9e030 b F3 D4 D3 3 +9e041 w M3 1 +9e076 w S2 1 +9e090 w B17 1 +9e0d6 b F17 1 +9e0d6 w O4 1 +9e0f0 b P12 1 +9e0f7 b R3 1 +9e110 w O7 1 +9e116 b Q15 P16 2 +9e120 b R3 1 +9e157 w E13 1 +9e167 w F6 1 +9e1a7 w R3 1 +9e1b6 b P5 Q5 2 +9e201 w Q3 1 +9e246 w P4 1 +9e261 w N4 1 +9e267 b M3 1 +9e291 b R14 1 +9e291 w R17 Q14 P15 3 +9e2b6 w P16 1 +9e2c0 b G16 D15 2 +9e2f6 b Q18 1 +9e307 w T19 1 +9e310 b P19 1 +9e320 w P7 1 +9e336 b Q6 1 +9e3a7 w C5 1 +9e3b7 b B6 1 +9e401 b S15 1 +9e4b6 w H3 1 +9e4c0 b F16 1 +9e4c0 w R12 1 +9e4c1 w M3 1 +9e4c6 b R5 1 +9e4c7 w R18 1 +9e4e1 w L15 1 +9e501 b E7 1 +9e567 b B2 1 +9e567 w Q16 1 +9e570 b C17 1 +9e5a7 b Q14 1 +9e5b1 b H5 1 +9e5c1 b R18 1 +9e5c7 b F17 1 +9e607 w P7 1 +9e660 b C15 F14 2 +9e676 b H17 1 +9e6a7 b Q15 1 +9e6f0 b T3 O5 2 +9e6f1 b S16 1 +9e716 w E7 1 +9e726 w E17 1 +9e736 b Q2 1 +9e747 w F14 1 +9e760 w D7 1 +9e7c1 w N6 1 +9e816 b S7 1 +9e841 w E5 1 +9e871 w Q6 1 +9e891 b D11 1 +9e8a7 w F17 1 +9e8d0 w P14 1 +9e8e1 b D15 1 +9e920 b E17 1 +9e930 b C16 1 +9e936 b D16 1 +9e960 b H3 1 +9e986 w Q7 C14 D16 3 +9e996 b Q2 1 +9e9a1 w D5 1 +9e9b1 w C3 1 +9e9b6 w M16 O17 2 +9e9d7 w J4 1 +9ea07 w E15 E14 C17 C13 4 +9ea10 b Q17 1 +9ea20 b F12 1 +9ea37 w R4 1 +9eaa0 w D14 1 +9ead1 w J4 1 +9eb30 w C15 1 +9eb50 w S6 1 +9eb57 b N17 1 +9eb66 b Q3 1 +9ebd6 b Q14 1 +9ebe7 b D16 1 +9ec10 w S6 1 +9ec27 w B3 1 +9ec31 b P14 1 +9ec47 w E2 1 +9ec76 w R14 1 +9ec97 w O14 1 +9eca6 w N3 R9 2 +9eca7 b R12 1 +9ecd7 w P2 1 +9ed36 w N4 1 +9ed47 b D15 1 +9ed67 w O2 1 +9ed80 w B4 1 +9ede0 b D16 1 +9ee00 w C2 1 +9ee07 w C2 1 +9ee10 w C3 1 +9ee27 b H15 1 +9ee36 w Q6 1 +9ee57 b P15 1 +9eec7 w C3 1 +9ef41 w D11 C4 2 +9ef51 b E15 1 +9ef56 w C3 1 +9ef61 b E9 1 +9ef87 b E8 1 +9efb6 w S6 1 +9efc6 b F17 C13 2 +9f007 w F8 1 +9f021 w F17 1 +9f026 w H4 1 +9f081 w D5 E4 2 +9f090 w O7 1 +9f0c6 w P3 S3 2 +9f127 b S5 1 +9f156 b P16 1 +9f157 w O3 H5 2 +9f181 w B16 1 +9f190 b F19 1 +9f1f1 b J16 1 +9f206 b N5 1 +9f260 w E16 1 +9f270 b B18 1 +9f291 b S18 1 +9f2b7 w Q16 1 +9f2f1 b G2 1 +9f311 b G5 1 +9f311 w Q17 1 +9f316 w C14 1 +9f321 b E18 1 +9f331 b B2 1 +9f3a0 b R6 Q4 R4 3 +9f436 b S6 1 +9f447 b P7 1 +9f467 b F15 1 +9f480 w G16 1 +9f487 w R7 1 +9f4a6 b C17 1 +9f4b7 w R2 1 +9f4d0 b C6 F5 2 +9f510 b C5 1 +9f526 b E3 1 +9f541 b C18 1 +9f556 w Q15 1 +9f5a7 w E18 1 +9f5d6 w H4 1 +9f5e6 w B5 1 +9f611 w P18 1 +9f630 w C12 1 +9f647 w Q8 1 +9f650 w N8 M6 2 +9f686 b F3 1 +9f687 w G13 1 +9f6f1 w B13 1 +9f741 w D16 1 +9f751 w C6 E4 2 +9f796 b J3 1 +9f7a0 b R13 1 +9f7d6 w C18 1 +9f7d7 b Q4 O4 2 +9f7f0 b E8 1 +9f817 w O4 1 +9f820 w E13 1 +9f846 w R5 1 +9f877 b C3 1 +9f8c0 w R4 1 +9f8d7 w E8 1 +9f8e7 b D16 1 +9f900 b Q17 1 +9f947 w T3 1 +9f950 w P16 1 +9f980 w Q13 1 +9f9c0 b E15 1 +9fa31 w D13 1 +9fa61 w P3 1 +9fa70 b N4 1 +9fa90 w J15 1 +9faa7 b D3 1 +9fad7 b P5 1 +9fae6 w E15 E16 2 +9fb06 b P17 S17 2 +9fb06 w R16 1 +9fb10 w F12 1 +9fb36 w C17 1 +9fb40 w P17 J3 2 +9fb56 b R3 1 +9fb80 w J17 1 +9fb91 w R12 1 +9fba7 b C14 1 +9fbc7 b N4 1 +9fbd0 w C11 1 +9fbd6 w H17 1 +9fbd7 b Q17 1 +9fc16 b C5 1 +9fc40 b C18 1 +9fc60 w N17 1 +9fc70 w E5 1 +9fca0 b B16 1 +9fcb0 b F1 1 +9fcc7 w C3 D7 2 +9fce6 w N16 1 +9fcf1 b B18 1 +9fd37 w H4 1 +9fd80 b D15 1 +9fda0 w M13 1 +9fdc1 w P15 1 +9fdf0 w R17 1 +9fe20 w C15 C18 2 +9fe86 w R6 1 +9feb0 b D19 B16 C15 3 +9fec7 w O4 1 +9fed1 b B14 1 +9fee7 w E7 1 +9ff06 w D14 1 +9ff10 b M4 1 +9ff30 b D3 1 +9ff56 b Q5 1 +9ff61 w R5 1 +9ff80 w D16 1 +9ff81 w R4 1 +9ffa0 w D12 1 +9ffa7 b E6 1 +9ffa7 w G3 1 +9ffb6 b P16 1 +9ffb6 w S15 1 +9ffd1 b O2 1 +9ffd1 w B18 1 +9ffd6 w Q13 1 +9ffe6 b C4 1 +a0000 w E14 1 +a0001 b P2 1 +a0060 w D16 1 +a00e6 b E6 1 +a00f7 b D16 F16 2 +a0101 b S15 1 +a0116 w F5 1 +a0131 b D18 1 +a0177 b G4 F2 2 +a0180 b R14 1 +a01b6 b M5 1 +a0227 w S5 1 +a0247 b M17 1 +a0257 b S17 1 +a0257 w O6 1 +a0280 w L17 1 +a02a7 w C16 1 +a02e6 b C6 1 +a0311 w O19 1 +a0317 b C8 1 +a0321 w P15 1 +a0377 b R12 1 +a03b1 w R17 1 +a03d1 b F15 1 +a03d1 w P12 1 +a0417 w C3 1 +a0430 w L6 1 +a0460 b Q12 1 +a0466 b C17 1 +a04a1 b C4 1 +a04a7 w H4 1 +a04d6 b R18 1 +a04f0 b R3 1 +a04f7 w B12 1 +a0510 b N5 1 +a0516 w G4 1 +a0527 w D12 1 +a0567 b R14 1 +a0576 w P3 1 +a0587 b M14 1 +a0590 b R11 1 +a0590 w Q6 R3 2 +a0596 b C18 1 +a05a0 w P13 1 +a05a6 w Q6 1 +a05b1 b D17 1 +a05e6 b Q15 1 +a05e6 w Q15 1 +a05f1 w P14 1 +a0656 b E18 1 +a0690 b R6 1 +a06a6 w S18 1 +a06b0 w R12 1 +a06b1 b E18 1 +a06b1 w D2 1 +a0717 b J4 1 +a0721 b Q4 1 +a0726 b O15 1 +a0737 b B4 1 +a0760 w R12 1 +a0786 b D14 1 +a0791 w S17 1 +a07a7 w H16 1 +a0826 b Q15 1 +a0827 b E15 1 +a0847 b P8 1 +a0867 w E2 1 +a08b6 w R3 1 +a08c1 b O16 1 +a08c1 w N16 O16 2 +a08e7 w N18 1 +a08f0 w M17 1 +a0921 b P16 1 +a0946 w C5 1 +a0967 b B15 1 +a0981 w Q7 1 +a0987 w F2 1 +a0990 w H3 1 +a09a0 w P15 1 +a09f0 b P5 1 +a09f6 w R7 1 +a0a01 b S3 1 +a0a01 w O16 1 +a0a41 b M18 1 +a0a70 w R3 1 +a0a80 b Q18 G17 2 +a0aa1 b C18 1 +a0ab1 b F14 1 +a0ac0 b C8 1 +a0ad0 b S3 1 +a0ae0 b Q6 P3 O5 M3 4 +a0b10 w F6 1 +a0b31 w G18 1 +a0b36 w R16 1 +a0b50 b E12 1 +a0b61 w H5 1 +a0b96 b Q3 1 +a0ba6 w Q14 1 +a0bc1 b Q11 1 +a0be1 b Q15 1 +a0be1 w P16 P15 Q13 R17 4 +a0c06 b O6 1 +a0c27 b C7 1 +a0c46 w R3 1 +a0c57 b S14 1 +a0c80 b G15 1 +a0c97 b E3 1 +a0d41 w G3 1 +a0d60 w R6 O5 2 +a0d66 b Q13 1 +a0d70 w P18 1 +a0d90 w Q17 1 +a0db6 w Q16 1 +a0e31 w M13 1 +a0e41 b Q11 1 +a0e67 w C18 1 +a0e77 b O4 1 +a0eb0 b P16 1 +a0ec1 b N15 1 +a0ec7 w S5 1 +a0ed7 b R16 1 +a0f31 b B7 1 +a0f76 w C3 1 +a0fa1 b Q14 1 +a0fc6 w C17 1 +a0fe0 w L16 1 +a1020 b R11 1 +a1076 w Q6 1 +a1080 w R5 1 +a10c0 b G15 1 +a10d1 b D2 1 +a10e7 w B17 1 +a10f0 w R2 1 +a1106 b E4 1 +a1106 w E4 1 +a1117 w E14 B6 2 +a1127 w B16 1 +a1130 w E3 1 +a1136 w D4 1 +a1167 w Q6 1 +a1170 w O15 1 +a1177 b Q4 1 +a11c0 w H3 1 +a11c1 w Q18 1 +a11d7 w O9 1 +a1206 w C18 1 +a1241 b Q7 1 +a1260 b Q15 1 +a12b0 w D8 1 +a12c0 w P2 1 +a12f1 w B12 1 +a1306 w D8 1 +a1337 w R3 1 +a1347 b E6 1 +a1381 b F15 1 +a1397 b D5 1 +a13d1 b D2 1 +a13f0 w N16 1 +a1406 b P17 P18 D13 3 +a1407 b D16 E5 2 +a1430 b S17 1 +a1431 w N15 1 +a1471 w B18 1 +a1476 w B2 1 +a1477 w F18 1 +a1480 w Q14 1 +a14b1 b C5 1 +a14c6 b D16 1 +a14d6 b F16 1 +a14f1 b D17 1 +a1507 w D11 1 +a1510 w P14 1 +a1526 b P15 1 +a1570 w O11 1 +a1577 b R17 Q17 R14 3 +a1587 b O4 S5 2 +a15b1 w E15 1 +a15b6 w E15 1 +a15f1 w B5 1 +a15f7 b D17 1 +a1640 b R3 G17 2 +a1660 w C1 1 +a1686 b N18 1 +a1690 b A2 1 +a1691 w Q16 1 +a16c1 b L18 1 +a16d0 w J17 1 +a16e1 w S17 1 +a1706 b P3 1 +a1720 w R18 1 +a17d0 b F5 1 +a17d7 b D17 1 +a17f1 b C5 1 +a17f7 b P18 1 +a1826 b R12 1 +a18a7 w H5 1 +a18b6 b G5 1 +a18d0 b Q14 1 +a18d1 w J16 1 +a18e0 w C8 P4 2 +a18e6 b F4 P13 2 +a1907 b R2 1 +a1920 b C17 1 +a1966 b G18 1 +a1967 w Q11 1 +a1980 b B4 1 +a1996 b C14 1 +a19c7 b C8 1 +a19d0 b B5 1 +a19f0 b G15 1 +a1a01 w C12 B15 B16 3 +a1a11 b S3 1 +a1a26 w S11 1 +a1a27 b R16 1 +a1a77 b N16 1 +a1aa0 w P15 1 +a1ac7 w L4 1 +a1af6 w P17 1 +a1b10 w C14 1 +a1b11 w S5 1 +a1b16 w E14 1 +a1b27 w H15 1 +a1b50 w O11 1 +a1b60 w S15 1 +a1ba0 w O18 1 +a1be6 w E2 1 +a1c07 b C5 1 +a1c37 b F15 1 +a1c51 b P5 1 +a1c60 b D5 1 +a1cb7 b O3 1 +a1cc1 b T4 1 +a1cf6 b C3 1 +a1d16 w T3 1 +a1d36 w Q15 1 +a1de1 b M17 1 +a1e10 w D9 1 +a1e37 b O5 1 +a1eb1 w C16 1 +a1f01 w C7 1 +a1f11 b G15 1 +a1f31 w F16 G17 2 +a1f40 w R6 Q4 R4 3 +a1f67 b O4 1 +a1f71 w R5 1 +a1f77 b M13 1 +a1f81 b R4 1 +a1f97 b H3 1 +a1fb1 w S18 1 +a2061 w C14 1 +a20a0 b P17 C9 2 +a20a1 w B16 C18 2 +a20b6 w F6 1 +a20d1 b B3 1 +a20d1 w R5 1 +a20f0 b N3 P5 2 +a20f6 b J3 1 +a2107 w B15 1 +a2111 w D6 1 +a2131 b D12 1 +a2180 w Q14 1 +a2186 b D7 1 +a21c7 b P7 1 +a2231 w R5 1 +a2247 b F17 1 +a2251 b B14 1 +a2291 w A18 1 +a22a6 w O16 1 +a22b6 b B7 1 +a22c7 w O14 1 +a22d6 b E14 1 +a22d7 b C16 D18 2 +a2311 b F5 1 +a2347 b O3 1 +a2371 b O3 1 +a2390 b Q15 1 +a2397 b S4 1 +a23c0 b P3 S3 2 +a23d7 w P5 1 +a2460 b T14 F3 2 +a2470 b F7 1 +a2476 w R5 1 +a2477 b B17 1 +a24a6 b C5 1 +a24d0 w C7 1 +a24e7 b P18 1 +a24e7 w R15 O17 2 +a24f0 b N3 1 +a24f6 b F4 1 +a2501 w S13 1 +a2507 w O4 1 +a2537 b P1 1 +a25b0 w R3 1 +a25d6 b O6 1 +a25d7 w P18 1 +a2621 b N15 1 +a2626 b C6 1 +a2626 w A8 1 +a2631 b Q17 1 +a2631 w R17 Q16 Q15 P14 Q14 5 +a2637 b C18 1 +a2641 b S3 1 +a2657 w C17 1 +a2677 b C12 1 +a2680 b Q5 1 +a26b6 w G17 1 +a26f0 b D5 1 +a2700 w L17 1 +a2711 b R3 1 +a2721 b B17 1 +a2747 w L4 1 +a2756 b D8 1 +a2761 w D16 1 +a2767 w F18 1 +a2791 w B2 1 +a2797 w F2 1 +a27b0 b C17 1 +a27d1 w J18 R16 2 +a2801 w B14 1 +a2807 w P6 1 +a2850 b G2 1 +a2870 w S18 1 +a2876 w E15 1 +a2891 b D13 1 +a2936 b D5 F4 2 +a29a0 b G13 1 +a29a7 b Q17 1 +a29c6 w O3 1 +a29f0 b Q14 1 +a29f1 b Q2 1 +a2a00 b D17 1 +a2a07 b Q15 1 +a2a10 w E12 F12 2 +a2a16 b E16 1 +a2a26 b S3 1 +a2a36 w P13 1 +a2a41 w B3 1 +a2a47 b R2 1 +a2a47 w R15 1 +a2a57 b B2 1 +a2a60 w F15 1 +a2a71 w Q2 1 +a2a80 b D15 C15 2 +a2a90 w R8 1 +a2a97 w B4 1 +a2ac1 w M7 1 +a2ad0 w Q11 1 +a2af0 b D5 1 +a2b07 b G16 1 +a2b07 w N17 1 +a2b17 b D8 1 +a2b31 w Q4 1 +a2b47 w G15 1 +a2b70 w N17 1 +a2b77 w R15 1 +a2b81 w Q18 1 +a2b97 w R7 1 +a2bb7 b O15 1 +a2bd6 b M3 1 +a2c20 w C17 E17 2 +a2c26 b C17 1 +a2c30 w E15 1 +a2c50 b D6 1 +a2c70 w D3 C4 2 +a2c90 w H14 1 +a2cc0 w P6 1 +a2cd1 w P5 R5 2 +a2d20 b N4 1 +a2d20 w E4 1 +a2d47 b N3 1 +a2d66 b N16 1 +a2d71 b D11 1 +a2d90 b P2 1 +a2d96 w P3 P2 2 +a2df6 w R6 1 +a2e07 w F18 1 +a2ec0 w P2 1 +a2ed1 w S2 1 +a2f67 w G3 1 +a2f71 b L4 1 +a2f76 w O16 1 +a2f81 b E17 1 +a2fe7 b P4 P3 2 +a3090 b M16 1 +a3090 w R14 1 +a30d6 w F7 S16 2 +a30d7 w S17 1 +a30e0 w D7 1 +a30e7 w E2 1 +a3101 b N13 1 +a3106 w Q13 1 +a3120 b P17 1 +a3127 w B12 1 +a3196 b Q1 1 +a31a7 w R2 1 +a31c0 w R12 1 +a31d0 w E8 1 +a31e6 b S16 1 +a3206 w B7 1 +a3217 b C9 1 +a3237 b H15 1 +a3241 w S3 P3 O4 N3 N2 5 +a3256 w Q6 1 +a3271 w Q4 1 +a3281 w M15 1 +a3291 w M17 1 +a32b0 b H5 1 +a32b0 w G15 1 +a32c0 w P6 1 +a32e7 w L4 1 +a3337 b P14 1 +a3376 b O3 1 +a3380 b Q3 Q4 N3 P5 R3 5 +a33a0 b Q13 1 +a33c0 b R2 1 +a33c7 b B7 1 +a3401 b B5 1 +a3410 b M16 1 +a3426 w S7 1 +a3476 w D15 1 +a3481 b R14 1 +a3486 b N5 1 +a3496 b O16 1 +a34a0 w Q15 1 +a34a6 b F3 1 +a3506 w F14 1 +a3526 b E17 1 +a3547 b Q4 1 +a3556 w D15 1 +a3596 w Q6 Q7 2 +a35d0 w R5 1 +a35d7 b N4 O2 2 +a35f1 w D14 1 +a35f6 b R15 1 +a35f7 b C5 1 +a3620 b H3 1 +a3666 b D12 1 +a3670 b F18 1 +a36a0 w R5 1 +a36a7 w B6 1 +a36f0 b R6 1 +a3700 w D17 D16 E5 3 +a3726 w B14 1 +a3761 b D18 1 +a3770 b D15 1 +a3770 w D2 1 +a3786 b D6 1 +a3786 w P16 1 +a37a7 b C3 1 +a37c1 w D4 F4 2 +a37f6 w R15 1 +a3810 w P3 1 +a3816 w N16 1 +a3831 b O3 Q4 2 +a3836 b C3 1 +a38c6 b T18 1 +a38f0 w D16 1 +a38f1 w B11 1 +a3941 w S14 1 +a3966 w B6 1 +a3981 b P3 1 +a39b0 b E5 1 +a39d0 b T16 1 +a39e0 b B7 1 +a39f7 w D2 1 +a3a20 b N18 1 +a3a30 w R16 1 +a3a37 w N2 1 +a3a51 b D4 1 +a3a61 b C4 1 +a3ac0 w E7 1 +a3ac1 w P2 1 +a3ac7 b Q4 1 +a3ad0 b T14 1 +a3ad6 w Q15 1 +a3ae6 w P5 1 +a3af1 w F8 1 +a3b00 b Q4 1 +a3b11 w R16 1 +a3b26 w P18 1 +a3b30 b Q16 1 +a3b30 w F14 1 +a3b51 b C5 1 +a3b57 b R18 1 +a3b60 w T3 1 +a3ba6 b J3 1 +a3c01 w Q3 1 +a3c16 b B18 1 +a3c30 b O13 R16 2 +a3c56 w E14 1 +a3c57 b Q5 1 +a3c66 b D14 1 +a3cc1 b G15 1 +a3ce7 w O3 1 +a3d20 b Q9 1 +a3d30 b P15 1 +a3d80 b S17 1 +a3d91 w P5 1 +a3dc6 b Q4 1 +a3dc6 w G17 1 +a3e20 b P4 R9 O3 N3 M3 N4 O4 7 +a3e20 w P4 O4 R7 R8 R9 Q7 Q8 Q9 8 +a3e27 w D19 1 +a3e31 b P16 1 +a3e56 w R11 1 +a3e60 w P18 1 +a3e61 b G3 1 +a3e70 b D14 1 +a3eb7 w G5 E14 D14 3 +a3ec0 b E14 1 +a3ee0 w P2 1 +a3f30 w S18 1 +a3f71 w C17 1 +a3f97 b E2 1 +a3fa6 w R14 1 +a3fc7 w G3 1 +a3fd0 w F3 D4 D3 3 +a3fe1 b O19 1 +a3fe6 b R6 1 +a3ff0 w P19 1 +a4020 w E16 1 +a4027 w B14 1 +a4087 b P16 1 +a40b6 w D13 1 +a40f1 b F17 1 +a4117 w T3 1 +a4141 w G4 H6 2 +a4157 b P6 1 +a41a1 b N16 1 +a41c1 w E7 1 +a41e6 w E7 1 +a41f6 b B6 1 +a4207 w S7 1 +a4210 w S5 1 +a4211 b P2 1 +a4227 w C2 1 +a4260 w J3 D9 2 +a4266 b R17 1 +a4276 b H7 1 +a42b7 w H16 1 +a42c6 w T17 1 +a42f7 w F3 1 +a4316 w R5 R2 2 +a4317 w R9 1 +a4321 b N17 M18 2 +a4330 w C18 1 +a4340 b E6 1 +a4346 w G2 1 +a4356 b R4 1 +a4357 b O17 1 +a4360 w N16 1 +a4376 b E4 1 +a4386 b O14 1 +a43c0 w H16 1 +a43c6 w L17 1 +a43d1 b O5 O4 2 +a4417 b H6 1 +a4421 w E5 C5 2 +a4427 w F2 1 +a4461 w S5 1 +a4466 b C17 1 +a44b6 b F16 1 +a44c0 w F7 1 +a44c7 w R18 1 +a44d1 b B15 Q5 2 +a44f1 w E6 1 +a4507 b G5 1 +a4570 b E7 P3 2 +a4570 w B14 1 +a4577 b S17 1 +a4591 w F17 F18 2 +a4596 b O6 1 +a45c1 w H15 1 +a45d0 w R15 1 +a45d6 w P2 1 +a4621 b S16 1 +a4630 b P7 1 +a4636 b R4 1 +a4681 b B12 1 +a4690 b Q17 1 +a46b6 w C2 1 +a46e0 b S17 1 +a4726 w P15 1 +a4731 b R16 1 +a4746 b O18 1 +a4781 b C6 1 +a4786 w F7 1 +a47c0 b F3 1 +a47c6 w G17 1 +a47c7 w E5 1 +a47d7 b P15 1 +a47e1 b D6 1 +a4810 w S16 1 +a4851 b C17 B17 2 +a4880 b O18 1 +a48b1 w C3 C4 J3 H3 E2 D3 6 +a48d6 w P6 1 +a48e0 w D5 1 +a48f0 b P3 1 +a4926 w C4 1 +a4937 b S17 1 +a4966 b E15 1 +a4970 b B3 1 +a4980 b C14 1 +a4986 b R17 1 +a4997 b B16 1 +a49f6 w C5 M3 2 +a4a00 b P12 1 +a4a11 b S15 1 +a4a17 b D11 1 +a4a26 b S17 1 +a4a31 b D19 1 +a4a60 w C6 1 +a4aa6 b A12 1 +a4ab0 b H4 1 +a4ac6 b O16 1 +a4ad6 w O5 1 +a4ad7 b H4 1 +a4ad7 w F7 1 +a4af1 b C3 1 +a4af6 b Q18 1 +a4b30 w F2 1 +a4b87 w D7 D4 2 +a4b96 w B17 1 +a4bb0 b P6 1 +a4bc0 w O5 1 +a4c07 w D1 1 +a4c17 b C4 D5 C3 3 +a4c77 w F16 1 +a4ca1 w E5 C6 2 +a4cf6 b R6 1 +a4cf7 b Q16 1 +a4d10 b R3 1 +a4d66 b P16 1 +a4d80 b M4 1 +a4da7 b F16 1 +a4db7 b E17 1 +a4dc0 b R15 1 +a4dc0 w C3 1 +a4e56 b Q5 O4 2 +a4e71 w F16 B15 2 +a4eb7 b E8 1 +a4ec1 w D18 1 +a4ee0 b E8 1 +a4ee6 w F17 1 +a4ef0 w C16 1 +a4f00 w B18 1 +a4f01 w P18 1 +a4f36 w N15 1 +a4f37 b F3 1 +a4f60 w O18 1 +a4fa7 w S15 B15 2 +a4fd1 b D6 1 +a4fe1 w O18 1 +a4ff1 b R4 1 +a5000 w F2 1 +a5010 b H17 1 +a5056 b C17 E17 F16 G16 G17 5 +a5056 w F16 F17 C15 E15 C13 D13 D12 7 +a5057 b F2 1 +a5071 b E3 F3 F4 E4 4 +a50b0 b N15 1 +a50d7 w Q4 1 +a50f7 w Q4 1 +a5116 b F15 1 +a5120 w B5 1 +a5131 b R1 1 +a5167 w D6 1 +a5177 b B6 1 +a5191 w S16 1 +a51f1 b P3 1 +a5200 b F17 1 +a5211 b M17 1 +a5217 b J16 1 +a5246 b F6 1 +a5287 b O15 1 +a52a6 b R4 1 +a5336 w D1 1 +a5341 w B13 1 +a5367 w E2 1 +a5381 w P17 1 +a5396 w S7 1 +a53d0 b F18 1 +a53e6 b E18 1 +a5430 b Q5 1 +a5460 b O5 1 +a5466 w Q8 1 +a5491 w R6 1 +a5496 b C3 1 +a54c7 b R3 1 +a5500 b G2 1 +a5540 w E17 1 +a5570 b P4 O4 R7 R8 R9 Q7 Q8 Q9 8 +a5570 w P4 R9 O3 N3 M3 N4 O4 7 +a55b6 w C2 1 +a55e0 w D13 D17 2 +a5610 b C2 1 +a5630 w P8 1 +a5641 b C18 1 +a5656 b F16 1 +a5657 w M2 1 +a5671 b Q18 1 +a56a7 w R3 1 +a56c0 w H3 1 +a56c6 w S18 1 +a56d0 w M16 1 +a56f1 b E13 1 +a56f1 w G4 F6 2 +a5711 b S17 B6 2 +a5730 b B5 1 +a5746 w E7 1 +a5771 b Q12 1 +a5776 b S17 1 +a5827 w Q16 1 +a5830 b R14 1 +a5830 w E4 1 +a5857 w R2 1 +a5887 b R12 1 +a58a6 w C14 1 +a5910 b A16 1 +a5931 b Q15 1 +a5946 b R16 Q17 2 +a5951 b E14 1 +a5957 b R5 B4 2 +a5960 b Q16 1 +a5971 b C15 1 +a5986 b S16 1 +a59a0 b P5 1 +a59a1 w C17 1 +a59e6 w F6 1 +a59e7 w D16 1 +a5a07 w Q15 1 +a5a17 b S18 1 +a5a17 w A1 1 +a5a66 w E5 D5 2 +a5a70 b F3 1 +a5a80 w P6 1 +a5ab1 w S19 1 +a5b47 w E12 1 +a5bb0 w C11 1 +a5bb1 w C5 1 +a5bc0 w P2 1 +a5bd1 w B5 1 +a5c00 w C5 1 +a5c10 b S3 1 +a5c26 b Q4 1 +a5c46 w B14 1 +a5c50 w E15 1 +a5c56 b M5 1 +a5c56 w F17 C13 2 +a5c91 b F16 1 +a5ca0 w G5 1 +a5cb6 w N6 1 +a5cc7 b P5 1 +a5cf6 b C17 1 +a5d26 b C15 1 +a5d37 w P14 1 +a5d61 b P13 1 +a5dc1 w O2 1 +a5e01 b F15 1 +a5e20 b F2 1 +a5e50 b R15 1 +a5e76 b D17 1 +a5e80 w S14 1 +a5e81 b N3 1 +a5ec0 w S9 1 +a5ed7 w N16 1 +a5ee1 b O5 1 +a5f06 b D7 1 +a5f26 b R3 1 +a5f57 w D4 1 +a5f81 b P4 1 +a5f97 b N6 1 +a5fa6 b B12 1 +a5fb7 w S6 1 +a5fd6 b C6 1 +a6006 b F7 1 +a6011 w Q2 1 +a60d6 w B14 1 +a60e1 w C14 1 +a60e7 w P7 1 +a6126 b G4 G3 C3 3 +a6187 w Q4 1 +a6191 b P16 1 +a61a1 b R15 1 +a61a1 w S4 1 +a61b1 b D3 1 +a61c1 w S6 1 +a61e1 w C18 1 +a61e6 b S3 1 +a61e7 b T2 1 +a6207 b O17 1 +a6261 b C9 F5 2 +a6286 b E2 1 +a62f6 b R17 1 +a6300 w N6 1 +a6321 b R16 1 +a6360 w G14 1 +a6386 b S15 1 +a63a0 w B14 1 +a63d7 w P3 1 +a63f1 w C3 1 +a6437 b B15 1 +a6437 w R4 P4 2 +a6440 b N4 1 +a6447 b R9 1 +a6451 b P15 1 +a6456 w B3 1 +a6470 w R8 1 +a6476 w N4 1 +a6477 b P6 1 +a6481 b D15 1 +a6487 w F2 1 +a6490 w O4 N4 2 +a64b0 b D18 D16 2 +a64b6 w Q5 1 +a64d7 w R17 1 +a6511 b Q14 1 +a6516 w H2 1 +a6530 w S14 1 +a6536 b B14 1 +a6557 b R8 S5 S4 3 +a65b7 b Q6 R7 2 +a65b7 w N18 1 +a65c0 w C7 1 +a65c7 w Q5 1 +a65e6 b H3 1 +a65e7 b N4 1 +a6617 w J17 E14 2 +a6620 b O17 1 +a6630 w R14 1 +a66c1 b Q17 1 +a66c7 b F2 1 +a6710 b D14 1 +a6727 b F7 1 +a6766 w C16 1 +a67a7 w B16 1 +a67d6 w P4 1 +a67e1 b O8 1 +a67f0 b C17 1 +a67f1 w R15 S15 Q16 3 +a6806 w B5 1 +a6811 w R4 1 +a6816 w O15 1 +a6837 w E2 1 +a68c0 b C3 C8 2 +a68c1 b C2 1 +a68f1 b R3 1 +a6910 w R11 1 +a6946 b S15 1 +a6950 b C11 1 +a6966 b O16 N16 2 +a6977 w D3 1 +a69a0 b E2 1 +a69a0 w P5 1 +a69a6 b C18 1 +a69c6 w B3 1 +a69e0 b R11 1 +a69e7 b D11 1 +a69f6 w Q16 1 +a6a06 w P15 Q16 2 +a6a07 w Q12 1 +a6a31 w S15 1 +a6a57 w Q6 1 +a6a60 w E15 1 +a6a87 w H3 1 +a6ab7 w S4 1 +a6ac6 w J17 1 +a6ae0 w S5 1 +a6ae6 b B17 1 +a6af1 w P13 1 +a6af6 w B5 1 +a6af7 w C6 1 +a6b20 b C13 1 +a6b46 b N6 1 +a6b50 w O18 1 +a6b51 b C15 1 +a6b57 b B13 1 +a6b60 b C18 1 +a6b61 w C5 1 +a6bb0 w Q15 1 +a6c06 b B12 1 +a6c37 w E5 1 +a6c77 b F7 1 +a6c86 w P4 1 +a6c90 b M4 1 +a6ce7 w F14 1 +a6d36 b R5 1 +a6d56 b L17 1 +a6d86 w B14 1 +a6d90 b Q13 1 +a6da7 w F7 1 +a6db1 b Q18 1 +a6db6 b E16 D14 2 +a6dc1 w D8 1 +a6e30 w E8 1 +a6e47 b Q6 P2 2 +a6e50 b F6 1 +a6e60 b O6 1 +a6e70 w E18 1 +a6e97 w D18 1 +a6ea1 b G5 1 +a6eb6 b D4 1 +a6f10 w C17 1 +a6f50 b E5 1 +a6f70 b B18 B16 2 +a6f86 b R4 1 +a6f90 w P5 1 +a6fb7 b S16 1 +a6ff1 b F17 B15 2 +a6ff7 w E7 1 +a7006 b D16 1 +a7021 w G4 1 +a7061 w D7 1 +a7066 w D6 1 +a7077 w P15 1 +a7080 b C5 F6 2 +a7090 b O18 1 +a70d7 w F15 G16 E17 3 +a70f6 w F16 1 +a7101 b D14 1 +a7116 b Q4 1 +a7150 b J6 1 +a7156 b R18 1 +a7157 b Q3 1 +a7166 w R4 1 +a7190 w G16 1 +a71b1 b E18 1 +a71b7 b P4 M18 2 +a71c0 b H6 1 +a7230 b C12 1 +a7251 w P15 1 +a7266 w F3 1 +a7281 b B15 1 +a72a6 w E6 1 +a72d6 w R7 1 +a7306 b O2 1 +a7320 w B16 1 +a7321 b G2 1 +a7381 b E18 1 +a73d6 b Q15 1 +a73d6 w D18 1 +a73f6 b O2 1 +a7407 b S2 1 +a7407 w D13 C12 C11 3 +a7410 b Q16 Q8 2 +a7446 b N4 1 +a7470 w E13 1 +a74a7 w D5 1 +a74c0 w L3 1 +a74e0 b G16 1 +a74e7 w C4 E4 2 +a7517 b D2 1 +a7521 b C8 1 +a7546 b B5 1 +a7567 b D15 D14 2 +a7576 w R3 1 +a7577 b G2 1 +a75a1 b G15 1 +a75a1 w C4 1 +a75b1 w O17 1 +a75c7 b S14 1 +a75d1 b S17 1 +a75e1 w R7 1 +a7600 b O5 1 +a76f0 b D17 D16 G17 E15 C17 5 +a76f0 w M15 1 +a7707 w Q3 1 +a7730 w E2 1 +a7741 w D3 B4 2 +a7776 w E17 1 +a7797 b P15 1 +a77c7 b O15 1 +a77d6 b E5 1 +a77e6 b P17 1 +a77f0 w E16 1 +a77f7 b R5 1 +a7800 b J3 1 +a7891 w D16 1 +a7896 b G2 1 +a78a0 w R18 1 +a78a6 b E15 D16 2 +a78b1 b H6 1 +a7907 b Q18 1 +a7951 b C2 1 +a7980 w R3 1 +a79a7 w R16 1 +a79c0 b J4 1 +a79d1 b F2 1 +a79e1 b F15 1 +a79e7 w D3 1 +a7a10 b E6 1 +a7a11 b Q3 1 +a7a27 b D4 1 +a7a40 b M17 1 +a7a41 w R5 1 +a7a47 w R2 1 +a7a70 w D15 1 +a7a76 b P3 P2 2 +a7a97 b N2 1 +a7b16 w R12 1 +a7b46 w P17 1 +a7b47 w D16 1 +a7b86 w P18 1 +a7b97 w O16 1 +a7bf6 w R17 1 +a7c00 w Q9 1 +a7c06 w E15 1 +a7c40 b B15 1 +a7c46 b B16 1 +a7c56 b M3 1 +a7c57 b D4 1 +a7c76 w D13 1 +a7cb7 w C16 1 +a7d11 b L5 1 +a7d21 w C18 F16 2 +a7d37 b N16 1 +a7d50 b M3 1 +a7da6 b Q15 1 +a7dc0 w P4 1 +a7dd1 w L4 1 +a7dd7 w B17 1 +a7e56 w J4 1 +a7e76 b D7 1 +a7ea6 b S6 1 +a7ee0 w E4 1 +a7f60 w D13 E16 2 +a7f71 b G19 1 +a7f87 b Q14 1 +a7f97 w C7 1 +a7fd7 b Q2 1 +a7fe0 b E2 1 +a8050 w G14 1 +a8051 w D18 1 +a8056 w G16 1 +a8070 w N7 1 +a8090 b P3 1 +a8101 b E18 1 +a8131 w Q13 1 +a8170 w R17 1 +a8177 b F4 1 +a81b6 w C18 1 +a81c7 w R4 1 +a81e6 w M2 1 +a81f7 w J16 1 +a8210 w C17 1 +a8246 w C5 C3 2 +a8250 w B3 1 +a8281 b E14 1 +a8291 w F4 1 +a82c6 b R6 1 +a82e7 w E5 1 +a8347 w D7 1 +a8361 w D3 1 +a8381 b O5 1 +a8396 b R16 1 +a83a1 b Q2 1 +a83a6 w N3 P4 2 +a8410 w T15 1 +a8416 b L7 1 +a8420 b B5 1 +a8446 b B16 1 +a8457 b Q12 1 +a8470 w R8 1 +a8471 b E3 1 +a8481 w C3 1 +a8496 b R17 1 +a84a0 b R6 1 +a84c6 b C12 1 +a84d6 b D18 1 +a84d6 w O2 1 +a8521 w C2 B4 2 +a8530 w D14 E14 2 +a8556 w D19 1 +a8580 w S16 1 +a8591 b S17 1 +a8596 b S3 1 +a8676 w D15 1 +a8686 w B15 1 +a8696 w N18 1 +a86a6 w B13 1 +a86b1 b D9 1 +a86e6 b P17 1 +a86f0 w C18 1 +a8700 w E2 1 +a8791 b G5 1 +a87c0 w F7 1 +a8800 b P17 1 +a8807 b N16 1 +a8861 w D11 1 +a8877 w R3 1 +a8881 b Q5 1 +a88a0 b B17 1 +a88a1 b R6 R3 2 +a88a7 b D7 1 +a88a7 w Q6 1 +a8900 w C17 1 +a8931 w Q4 1 +a8957 w P2 1 +a8980 w C11 1 +a8987 w F14 G13 2 +a8996 w E13 1 +a8997 b E13 1 +a89b7 w B5 1 +a89c6 b G5 1 +a8a06 b R3 1 +a8a30 w G14 1 +a8a36 b O16 1 +a8a67 b Q13 S14 2 +a8a86 w M17 1 +a8a97 w G5 1 +a8aa7 b R3 1 +a8ab0 w O16 1 +a8b00 b O18 1 +a8b20 b C17 D13 2 +a8b20 w R5 Q12 2 +a8b21 b C17 1 +a8b61 w M5 1 +a8b66 b R6 1 +a8b90 b F5 1 +a8b96 b M17 1 +a8b96 w R15 P14 2 +a8bc6 w D13 1 +a8c31 b P6 1 +a8c36 b F14 1 +a8c37 w F4 1 +a8c71 w O18 1 +a8ca1 b O7 1 +a8ca1 w D3 1 +a8cd0 w C13 1 +a8cf0 b S15 1 +a8d06 b R8 1 +a8d60 b P16 1 +a8d66 w C3 1 +a8d86 w R3 1 +a8d90 w D4 1 +a8da0 w G6 1 +a8db0 w H5 1 +a8dd0 w C18 1 +a8dd7 w Q3 1 +a8df0 w F18 1 +a8e06 w R2 1 +a8e41 b F16 1 +a8e77 b C18 1 +a8f00 b D5 Q5 2 +a8f17 w P15 1 +a8f31 w D11 1 +a8f57 b S12 1 +a8f61 b O4 1 +a8f77 b N3 1 +a8f80 w C11 1 +a8fa0 b D7 E4 2 +a8fb7 b J2 1 +a8fe1 w C14 1 +a9030 b Q3 1 +a9066 w P3 1 +a9077 w G5 1 +a90b6 w Q17 1 +a9106 w Q16 1 +a9111 b P3 S7 B16 3 +a9160 b N12 M14 2 +a9160 w N16 1 +a9161 w C7 1 +a9166 w Q2 M18 2 +a9167 b A18 1 +a9190 w C2 1 +a91e1 b F16 1 +a91e6 b D3 1 +a9211 w D13 1 +a9250 b E6 1 +a9280 b G8 1 +a92b0 w R5 1 +a92d7 b R17 Q14 Q16 P16 O15 P15 P13 Q13 O17 N17 N16 11 +a92d7 w R16 1 +a9311 b R18 1 +a9316 b A18 1 +a9317 b N4 1 +a9360 b S3 1 +a93d6 w L3 1 +a9401 w J16 1 +a9407 b D15 E16 2 +a94d1 w Q14 P18 2 +a9501 b R3 1 +a9516 w J16 1 +a9517 b S5 1 +a9520 w D5 1 +a9556 w S17 1 +a9561 b T14 1 +a9581 b E17 1 +a9586 w R18 1 +a9596 b G3 1 +a9597 b O4 1 +a95a7 w S14 1 +a95e7 b E7 1 +a95f1 b C3 1 +a95f1 w F18 1 +a9607 b E14 1 +a9631 b A4 1 +a9667 w Q5 1 +a96b7 b D13 C17 2 +a96c1 b Q14 1 +a96d0 w H15 1 +a96f1 b E6 1 +a9741 b R16 1 +a9776 w S7 1 +a97b1 b N5 1 +a97c7 b S15 1 +a97d0 b R18 1 +a97f6 w B17 1 +a97f7 b S3 1 +a9856 b B7 1 +a9890 b G17 1 +a9896 b R17 1 +a9897 b S17 1 +a98d1 w O3 1 +a98f0 b C3 1 +a98f7 w R15 1 +a9947 b D3 1 +a9957 b M3 1 +a9960 w F5 1 +a9961 b N6 1 +a9961 w Q8 1 +a99a7 w R16 1 +a99c7 b O17 P16 2 +a99f7 b F16 1 +a9a11 b C6 1 +a9a21 b F4 G3 2 +a9a57 w P5 1 +a9a70 w N7 1 +a9a81 b G17 1 +a9a86 w Q6 1 +a9ac7 w D16 1 +a9b00 w N18 1 +a9b20 w C15 1 +a9b26 b G16 1 +a9b31 b E5 1 +a9b36 w B16 1 +a9b46 w C16 1 +a9b67 b F4 1 +a9b71 b S16 1 +a9b96 b Q6 1 +a9bb7 w O6 1 +a9bd1 w R15 1 +a9be0 b E15 1 +a9c11 w F1 1 +a9c20 w P16 1 +a9c36 b B6 1 +a9c47 w D18 1 +a9c51 b O16 1 +a9c76 b Q14 R14 P17 P15 N17 N16 M16 7 +a9c76 w R17 R15 Q14 Q13 R13 5 +a9ca7 w G7 1 +a9cb1 b Q3 1 +a9cc6 b P2 1 +a9ce0 w B17 1 +a9ce1 w O17 1 +a9d00 b O16 1 +a9d11 w F5 D6 2 +a9d31 b R3 1 +a9d71 w P17 1 +a9df0 w R6 1 +a9e00 w P15 1 +a9e20 b E13 1 +a9e57 b R18 1 +a9e66 w D14 1 +a9e71 b E5 1 +a9e86 b N6 1 +a9e96 w E17 1 +a9ea0 b E5 1 +a9ea7 w G16 1 +a9eb7 b R17 1 +a9ec6 w H5 1 +a9ee6 b R4 1 +a9f00 w O6 1 +a9f01 b C18 1 +a9f06 b O4 P4 2 +a9f46 w C3 1 +a9f90 w D15 1 +a9fb0 w C12 1 +a9fc1 w N6 1 +a9fd0 b O4 1 +a9fd7 b D8 1 +a9ff7 b F3 1 +aa007 b R17 1 +aa050 b P17 1 +aa070 b R12 1 +aa0c0 b Q15 P4 2 +aa127 b B18 1 +aa131 w S4 R2 2 +aa171 w S12 1 +aa1b0 b D7 D5 2 +aa1e1 w F15 1 +aa1f0 w B6 1 +aa217 b S5 1 +aa217 w Q16 1 +aa237 w S2 1 +aa250 b E4 1 +aa2c0 b G15 1 +aa2e6 w Q2 1 +aa320 w O3 1 +aa330 b C6 1 +aa341 b N6 1 +aa381 w F4 G5 2 +aa397 b D7 B6 2 +aa3d1 b N3 1 +aa3e6 b P7 1 +aa406 b P17 O15 2 +aa436 w S14 1 +aa447 b S11 1 +aa466 b E17 1 +aa477 b S5 1 +aa477 w D11 1 +aa4b1 b O15 1 +aa4d0 b L13 1 +aa4e0 w D17 1 +aa4e1 w M17 1 +aa500 b F16 1 +aa517 w T7 1 +aa520 w O15 1 +aa526 b B17 1 +aa531 b Q5 1 +aa531 w O7 1 +aa536 b L3 1 +aa557 b B8 1 +aa576 w R18 1 +aa5b7 w C16 1 +aa5c1 b E18 1 +aa627 w B15 1 +aa637 w Q4 1 +aa656 b F6 1 +aa657 b C17 1 +aa666 w S2 1 +aa671 w F5 1 +aa6d1 w R5 1 +aa710 b B16 D16 2 +aa831 b R13 1 +aa840 w R13 1 +aa850 w N15 1 +aa867 b C16 1 +aa8a0 b R11 1 +aa8a1 w S11 1 +aa8b0 w R18 1 +aa8c6 b C6 D5 2 +aa8d7 b O5 1 +aa901 b C4 1 +aa906 w R4 1 +aa911 b Q17 1 +aa921 b S14 1 +aa930 w D7 1 +aa936 b C12 1 +aa946 b O16 1 +aa946 w F2 1 +aa950 b Q13 1 +aa951 b Q17 1 +aa971 b S17 C16 2 +aa9c0 w B4 D4 2 +aaa21 b N5 1 +aaa36 w G3 1 +aaa47 b F3 1 +aaa57 w S2 1 +aaa60 b F2 1 +aaa71 b J15 1 +aaa81 w N6 1 +aaac1 w O13 1 +aaac7 w C16 1 +aab26 w O4 1 +aab50 w Q5 1 +aab61 w C6 1 +aab67 w D17 1 +aabf1 w P17 1 +aac00 b E16 1 +aac01 b Q2 1 +aac11 b C17 1 +aac26 b N5 1 +aac50 b R7 S5 2 +aac50 w C6 D4 C4 3 +aaca6 b B17 1 +aacb1 w B15 1 +aacc0 b P15 1 +aacd0 w F6 1 +aacd1 w R8 1 +aacd6 b R6 1 +aace6 b F17 1 +aad16 w P15 1 +aad57 w G16 1 +aad70 w M14 1 +aada1 w N18 1 +aadb1 b C16 1 +aadf7 w O5 1 +aae40 b P4 1 +aae80 b F4 1 +aae97 w P13 Q14 2 +aaec0 w E3 1 +aaec6 w C4 1 +aaf36 b B17 1 +aaf60 b P4 1 +aaf76 w Q8 1 +aafb1 w E17 1 +aaff6 b D6 1 +ab031 b S5 1 +ab036 w D16 1 +ab047 b B6 1 +ab0a0 w F15 1 +ab0a7 w P14 1 +ab0d6 w C12 1 +ab0e0 w E7 1 +ab0f0 b C7 1 +ab0f6 b F14 1 +ab120 b D5 1 +ab121 w F3 1 +ab161 b Q6 1 +ab167 b O17 1 +ab170 b D5 1 +ab187 b J3 G3 2 +ab1a1 w F18 1 +ab1d1 b B5 1 +ab241 w Q9 1 +ab247 w E16 1 +ab270 b M15 1 +ab2c0 w R14 1 +ab2d6 w B6 1 +ab2e1 b P17 S13 2 +ab2f1 b P2 1 +ab2f7 b C13 1 +ab357 w N17 1 +ab367 b L18 1 +ab3e6 w O6 1 +ab411 b R3 1 +ab421 b P16 O16 Q15 P17 4 +ab427 b Q16 1 +ab451 w E3 D4 R11 O15 N15 5 +ab460 w R7 1 +ab477 b O3 1 +ab4b1 w Q4 1 +ab4c7 w N3 E3 2 +ab4d7 w D14 1 +ab4f1 w F6 1 +ab507 b S14 1 +ab511 b B8 1 +ab527 w P15 1 +ab547 b F3 1 +ab566 b P4 1 +ab5a0 w P6 1 +ab5d6 w F17 E16 2 +ab611 b B16 1 +ab620 b Q15 R13 2 +ab630 w E3 1 +ab646 b O15 1 +ab647 w O14 1 +ab656 w C16 1 +ab660 w P6 1 +ab677 b B17 1 +ab690 b F14 1 +ab6b6 w N14 1 +ab6b7 b B2 1 +ab6d1 b N16 1 +ab6e1 b R13 1 +ab727 w D3 D5 2 +ab741 b R9 1 +ab747 b E7 1 +ab757 w M18 1 +ab766 w R16 1 +ab7d1 w R4 1 +ab7e1 w E19 E1 2 +ab801 w D15 1 +ab806 b F4 G4 2 +ab827 w Q16 1 +ab847 w S14 1 +ab860 b E12 1 +ab876 w D12 1 +ab891 b S16 1 +ab8b7 b H17 1 +ab8d6 w T3 1 +ab937 b E12 1 +ab966 b C13 1 +ab967 b C6 1 +ab987 b O12 1 +ab9b1 b E2 1 +ab9d6 b S12 1 +ab9e1 b H4 D8 C16 3 +ab9f7 b C3 D3 C9 C8 B5 C4 6 +aba27 w Q16 1 +aba47 b D18 1 +aba61 b G14 1 +aba67 w C2 1 +aba81 w Q17 1 +abae6 w C8 1 +abaf0 b E6 1 +abaf7 b C7 1 +abb00 w R5 1 +abb21 b R4 1 +abb41 b P7 1 +abb61 w C11 1 +abb66 w C5 1 +abbb0 w P18 1 +abbb6 b O16 1 +abbc7 b E19 1 +abbe6 b B2 1 +abc01 w C2 1 +abc11 b D14 1 +abc17 b N2 1 +abc76 w N16 1 +abc91 w G4 C3 2 +abc97 b R4 1 +abcb1 b N3 1 +abce7 w N15 1 +abcf7 w C3 1 +abd11 b D2 1 +abd16 b C17 1 +abd81 w Q4 1 +abdf7 w F15 1 +abe26 w D4 1 +abe37 b S11 1 +abe51 w T6 1 +abe70 b R3 1 +abe71 w S13 1 +abef7 b S7 1 +abf06 w R2 1 +abf10 b D16 1 +abf11 w E17 1 +abf21 b S5 1 +abf30 w C1 1 +abf40 b O16 Q15 2 +abf51 b D18 B16 2 +abf80 b R14 O15 2 +abf90 b F13 C16 2 +abfa6 w G14 1 +abfa7 w Q6 P7 2 +abfb1 w E18 1 +abfe0 w O16 1 +ac016 b O18 1 +ac051 w E6 1 +ac086 b B15 1 +ac0a0 w Q5 1 +ac0a6 w C7 E2 2 +ac0a7 w N16 1 +ac0b1 w S5 1 +ac0b6 w R4 1 +ac0c7 b P12 1 +ac0c7 w E2 1 +ac106 w E16 1 +ac126 b P14 1 +ac176 b F2 1 +ac190 w O16 1 +ac1a6 w Q17 1 +ac1b1 b E3 B7 2 +ac1c1 b N4 1 +ac1f0 b O6 1 +ac216 b C15 1 +ac227 w S14 1 +ac277 w O17 S15 2 +ac291 b Q2 1 +ac2a7 w R17 1 +ac2d7 w R16 1 +ac2e1 w F17 1 +ac2e6 w O12 1 +ac2e7 b C9 1 +ac2f0 b Q5 1 +ac321 b E6 1 +ac326 b R11 1 +ac330 w E12 E13 C15 E15 4 +ac331 b O16 1 +ac337 w E2 1 +ac340 w S16 1 +ac3b6 b S15 1 +ac431 b P4 1 +ac441 w F5 1 +ac447 b C14 1 +ac447 w N4 1 +ac460 b R18 1 +ac470 w C3 1 +ac4b1 b P13 1 +ac4c0 w D12 1 +ac556 w S14 1 +ac567 b R17 1 +ac586 b P16 1 +ac5a0 w D6 D7 2 +ac5a1 b S3 1 +ac5a1 w R6 1 +ac5a7 w Q7 1 +ac5c0 w M16 1 +ac611 b D2 1 +ac621 b B8 1 +ac626 b O13 1 +ac677 b Q14 P15 2 +ac677 w R14 1 +ac696 b M17 1 +ac717 b C12 1 +ac737 w T16 1 +ac776 w E3 1 +ac7b6 b O16 1 +ac7c0 b C17 D17 2 +ac7e6 b S9 1 +ac7f6 b O17 1 +ac800 b E4 1 +ac810 w F17 1 +ac827 w C17 1 +ac846 w B3 1 +ac867 b D16 1 +ac881 b B15 1 +ac886 b F14 1 +ac8c6 w N16 1 +ac921 w C6 1 +ac927 w S16 1 +ac957 w N15 1 +ac976 w S16 1 +ac977 w E18 1 +ac980 w Q4 1 +ac981 b F14 1 +ac991 w E4 1 +aca40 w E18 1 +aca41 b B16 1 +aca61 w R16 Q18 2 +acaa0 w D3 1 +acac0 b Q6 1 +acad7 b B2 1 +acb07 w B15 1 +acb11 b D5 1 +acb11 w O5 1 +acb51 w R6 1 +acb57 b G3 1 +acb77 w D3 1 +acba0 b R4 1 +acc06 b R2 1 +acc10 w E15 1 +acc51 b B7 1 +acc67 b S5 1 +accb7 w N3 1 +acd07 b R8 1 +acd21 b S16 1 +acd31 b D6 1 +acd61 w C13 1 +acd76 w R15 1 +acd86 b S17 1 +acde6 b O16 1 +acdf1 w L17 M5 2 +ace56 w B17 C3 2 +ace60 b O6 1 +ace87 w D7 1 +ace90 w D6 1 +acea0 w E17 1 +acec7 b R14 1 +aced1 b H6 1 +aced6 b P16 Q15 2 +acf37 b Q4 Q8 2 +acf47 b R15 D14 2 +acf66 w R5 1 +acf77 b T18 1 +acf77 w H16 D12 2 +acf87 b O16 S15 2 +acfa7 b N16 1 +acfd0 b Q12 1 +acfe6 b N6 1 +ad086 w F4 1 +ad091 w O15 1 +ad0b1 w D9 1 +ad0c0 w R16 1 +ad0d1 w S7 1 +ad0d7 b Q15 1 +ad0e1 b Q3 Q5 2 +ad120 b E3 1 +ad140 w F18 1 +ad191 w C3 1 +ad196 w R7 1 +ad1a7 b J4 S16 2 +ad1b6 w B3 1 +ad1b7 w O15 S18 2 +ad1d1 b B16 1 +ad240 w S6 1 +ad266 b C18 1 +ad277 b G3 1 +ad280 w E4 1 +ad2a0 w O5 1 +ad2a6 b P16 1 +ad2d6 w P3 R3 2 +ad2e7 b N3 1 +ad320 b C2 1 +ad356 w Q15 1 +ad370 b D14 1 +ad380 w F3 1 +ad387 w Q14 R18 2 +ad3b1 b D2 1 +ad3c1 w G2 1 +ad3f0 w C11 1 +ad3f6 b G4 1 +ad3f7 b H4 1 +ad426 b G14 1 +ad427 b P18 1 +ad437 w R15 1 +ad450 b P8 P7 R5 P5 4 +ad486 w C14 D15 2 +ad4c6 b R17 Q16 2 +ad520 w H17 1 +ad530 b P16 1 +ad536 b R14 1 +ad540 b B4 1 +ad561 b R3 Q6 R6 M4 O5 5 +ad580 w F5 D6 2 +ad590 w R17 1 +ad596 w P16 1 +ad597 b D3 1 +ad597 w O4 1 +ad5a0 b D12 1 +ad5a1 w D16 C17 2 +ad5b0 w B17 1 +ad5d6 b S6 1 +ad5e0 w R15 1 +ad5f1 b H4 1 +ad610 b G16 D16 F15 3 +ad617 w C16 1 +ad636 w Q14 1 +ad640 b F14 1 +ad661 w R7 1 +ad667 w D7 1 +ad6a7 b O4 1 +ad6c1 b B16 C18 2 +ad6d7 w R13 1 +ad720 b B18 1 +ad721 w B15 1 +ad730 b Q15 1 +ad730 w B3 1 +ad766 w B15 1 +ad771 b B15 1 +ad790 b E12 1 +ad7c7 w R16 1 +ad901 w E3 1 +ad961 b Q17 1 +ad977 b F5 1 +ad980 b F4 F3 G3 D6 C8 D8 D9 7 +ad980 w F3 1 +ad981 w D18 1 +ad9b7 w S8 1 +ad9d0 w E16 1 +ada37 w R17 1 +ada56 w M17 1 +ada81 b E16 1 +adaf7 w S4 1 +adb07 w B15 1 +adb10 b D15 1 +adb16 b D17 D15 D2 3 +adb41 b C18 1 +adb47 w R3 1 +adb57 w R15 1 +adb67 w Q3 1 +adb76 b H18 1 +adb91 b C8 1 +adb96 w F14 1 +adbb1 w D14 1 +adbc1 b R5 1 +adbc6 w G16 1 +adbe7 w F4 1 +adbf0 w F6 1 +adbf1 b M3 1 +adbf7 w J2 1 +adc11 w M17 1 +adc61 w E18 1 +adc80 b C3 C5 E17 3 +adca0 w E2 1 +add46 w G4 1 +add51 w B15 1 +add57 w O16 1 +add71 b C17 1 +add97 b E15 1 +addb1 w Q3 1 +adde0 b G5 1 +adde7 b B7 1 +addf6 w P5 Q4 2 +ade06 w C4 1 +ade10 w R5 1 +ade16 w R17 1 +ade47 w N2 Q15 2 +ade60 w P2 1 +adeb6 b C2 1 +adee6 w E5 1 +adf01 b S3 L3 2 +adf31 w R14 1 +adf40 w F9 1 +adf50 w E2 1 +adf80 b R11 1 +adf90 w G18 1 +adfd7 w L18 1 +adff0 b D18 1 +adff0 w F16 1 +ae017 b B7 1 +ae036 w R17 Q16 2 +ae037 w Q6 1 +ae057 b C16 1 +ae060 w J17 1 +ae070 w M6 1 +ae090 b R9 1 +ae0a1 w N3 1 +ae0a6 w O8 1 +ae0c0 w D9 1 +ae0c7 w C7 1 +ae0d1 w Q8 1 +ae0f0 b O3 1 +ae147 b H3 S4 2 +ae180 w Q3 1 +ae1d7 w C5 G2 2 +ae1e0 w E6 1 +ae1e1 b C15 1 +ae230 b S16 Q14 2 +ae240 w E4 1 +ae257 b C7 1 +ae266 b Q18 1 +ae297 b B13 1 +ae2c7 b E2 1 +ae2d6 b B3 1 +ae2e0 b Q7 1 +ae316 b R18 1 +ae327 w Q5 P4 2 +ae331 w O15 1 +ae337 w M16 1 +ae371 w Q13 O12 2 +ae3a0 w P7 1 +ae3b7 w R3 1 +ae3c6 b F4 1 +ae3c7 w N17 D3 2 +ae3e0 w S5 1 +ae416 w F2 1 +ae417 b P14 1 +ae441 w P17 1 +ae476 b E18 1 +ae477 b Q5 R4 2 +ae477 w P4 R3 N3 Q6 Q8 5 +ae4a0 b Q16 1 +ae4b1 b B15 1 +ae4b6 b Q6 1 +ae4b6 w F17 1 +ae4d0 b Q3 1 +ae4f6 w D1 1 +ae510 b E18 1 +ae511 w P2 1 +ae516 w C5 1 +ae526 b N4 N3 R3 3 +ae541 b B4 1 +ae560 w P14 1 +ae5b6 b G2 1 +ae5c1 w C14 J17 G17 3 +ae5d1 b P18 1 +ae621 b D3 O4 2 +ae640 b D12 1 +ae676 b E5 1 +ae691 w S3 1 +ae6c0 w D5 G18 2 +ae6c7 w B17 B14 2 +ae6f1 w D2 1 +ae6f7 b G15 1 +ae716 w C2 1 +ae727 w P2 1 +ae731 w E12 1 +ae756 b R6 1 +ae771 b R17 1 +ae777 w Q13 1 +ae791 w C4 1 +ae796 b Q4 1 +ae7c6 w B17 1 +ae846 w N4 1 +ae887 b C15 1 +ae890 b N11 1 +ae896 b C6 1 +ae8a1 w P3 1 +ae8b7 w C13 M15 2 +ae8e0 b R11 1 +ae8e1 b Q6 1 +ae8e7 b Q17 1 +ae8e7 w M5 G4 2 +ae8f0 w H6 1 +ae8f7 w N5 1 +ae910 b D14 C14 C13 F16 H17 H16 J16 7 +ae910 w C14 1 +ae981 w C2 1 +ae986 b C16 1 +ae996 b C12 N15 2 +ae9b7 b D18 1 +ae9e7 b Q14 1 +aea26 w D7 1 +aea46 w S6 1 +aea80 b T18 1 +aeab6 w L16 M2 2 +aead7 b D17 1 +aeae0 b Q6 1 +aeae0 w F14 1 +aeaf0 b H15 1 +aeaf6 w H17 1 +aeb10 b C18 1 +aeb87 w R18 1 +aeba0 w O18 1 +aeba1 b E2 1 +aeba6 b O18 1 +aebc0 w R7 1 +aec00 b N4 R4 2 +aec30 b O3 1 +aec36 w E17 1 +aec60 w J3 1 +aec96 b R14 1 +aecb1 b C13 1 +aed16 w N4 1 +aed21 w D6 P8 2 +aed31 b D15 1 +aed47 b F6 1 +aed47 w J4 1 +aedc6 b M3 1 +aede1 b L15 E13 2 +aee21 b D2 1 +aee21 w P3 1 +aee36 b D17 1 +aee47 w C16 1 +aee60 b S4 1 +aee61 w D3 1 +aee76 b E7 1 +aeec1 b B4 1 +aeee0 w D7 1 +aef06 b R6 1 +aef07 b C3 D3 C6 3 +aef20 b Q8 1 +aef47 b P18 1 +aef66 b C16 D15 D11 S13 4 +aefa1 w D6 1 +aefb6 w R5 D14 2 +aefd6 w E15 D16 2 +aeff6 b F17 1 +af016 w R17 1 +af017 w G4 1 +af030 w F17 1 +af031 w B5 1 +af046 b E7 1 +af067 w D4 1 +af076 w M17 1 +af0c1 b B18 1 +af0f6 w F3 1 +af101 b D7 1 +af137 b N15 1 +af181 w J4 1 +af1a0 b E2 1 +af1a6 w Q13 1 +af1e6 b D6 1 +af1f7 b C17 1 +af211 b E12 1 +af260 b P4 1 +af270 b N17 1 +af2b6 b B7 1 +af2e1 w C12 1 +af300 b R8 1 +af311 b D14 1 +af320 w R5 1 +af337 w S16 1 +af391 w D4 1 +af3b1 w E7 1 +af3e6 b C13 1 +af3e6 w D6 1 +af401 w Q14 1 +af406 b R6 1 +af416 b R4 1 +af421 b G5 F4 M18 3 +af490 b D15 1 +af497 w E15 1 +af4c1 b C12 1 +af4d1 w B15 Q17 2 +af517 b C5 1 +af517 w C2 1 +af547 b E18 1 +af5b7 b R3 1 +af5e0 b H16 1 +af5e7 b S2 1 +af5f1 w Q16 1 +af610 w R6 1 +af640 b O16 1 +af646 w E3 1 +af657 b D9 1 +af657 w F3 D4 2 +af671 b G2 1 +af6d0 w Q16 1 +af6d6 b C3 1 +af6d7 b O13 1 +af6e7 b Q17 1 +af6e7 w F4 1 +af6f1 w M15 1 +af701 b B4 1 +af740 b F18 1 +af757 w C13 1 +af7a7 b E3 1 +af7b6 b R17 1 +af7f6 w C5 1 +af806 w R6 D8 2 +af810 w E14 1 +af831 w F4 1 +af846 b N4 1 +af866 b O4 1 +af880 b O4 1 +af897 b G2 1 +af8a0 b D15 1 +af8f1 b O14 1 +af8f6 b Q13 1 +af921 b S5 1 +af921 w S11 1 +af927 b D17 1 +af950 w C9 1 +af961 b R8 1 +af976 b S17 1 +af976 w C6 1 +af977 b A2 1 +af9b0 b E14 1 +af9b0 w R4 1 +af9d1 w Q8 1 +af9e1 w B15 1 +af9e7 b B17 1 +afa91 b D18 1 +afac6 b F5 G4 E6 3 +afae6 b C4 1 +afbb7 w Q12 1 +afbc6 b S14 1 +afbc7 b D7 F8 2 +afc31 w F18 1 +afc50 b F16 1 +afc81 w P8 1 +afc86 w G17 1 +afcc7 w R2 1 +afcd1 b O15 1 +afdd0 w S2 1 +afdd7 b F2 1 +afe01 b D5 1 +afe26 b D16 1 +afe40 b R2 1 +afea6 b C4 1 +afec1 w L4 1 +afef7 b R16 1 +aff07 b P17 1 +aff61 b D4 1 +aff61 w O16 1 +aff66 b Q15 1 +aff71 b S17 1 +aff71 w R18 1 +affa0 b M7 1 +affa6 w C13 1 +affa7 w P18 1 +b0000 w F4 1 +b0010 b G15 1 +b0036 w M3 1 +b0037 w C3 1 +b0057 w Q17 1 +b0090 w O14 1 +b00a7 b B17 1 +b00b0 w C13 Q8 2 +b00c6 w O17 1 +b00e0 w G15 1 +b0106 w R13 1 +b0116 b L3 R7 2 +b0117 w O3 1 +b0181 w D13 1 +b01c1 w O3 Q5 2 +b01f0 w F16 1 +b0286 b P7 1 +b0296 b C18 1 +b0297 w S2 1 +b02d0 b B3 1 +b02f0 w F3 1 +b02f7 w F15 1 +b0306 w E3 1 +b0327 b P2 1 +b0331 w C7 1 +b0347 b O7 1 +b0370 b Q7 Q4 P6 3 +b0371 w E3 1 +b0397 b D8 1 +b03d6 b S3 1 +b0430 b O3 1 +b0477 b B4 1 +b0477 w E6 1 +b0497 b R3 1 +b0497 w C3 F4 F3 D8 E6 5 +b04c0 b Q5 1 +b04d7 b N16 O16 2 +b04d7 w O16 1 +b0517 b R17 1 +b0531 b D17 1 +b0540 b R2 1 +b0591 b E3 C6 2 +b0591 w B5 1 +b05e6 w O15 1 +b0611 w R11 1 +b0636 b G16 1 +b0651 w D3 1 +b0690 b G6 1 +b06a1 w O4 1 +b0740 w D15 1 +b07a6 w H16 1 +b07d0 b C12 P13 2 +b07e1 w S6 1 +b0800 b R17 1 +b0830 b R15 R18 2 +b0840 w Q18 O16 2 +b08a0 w R12 1 +b08b7 w T4 1 +b0951 b C16 1 +b0967 b G4 F6 2 +b0976 b F5 1 +b0977 b E4 C7 2 +b09a7 b O17 1 +b0a46 b C17 1 +b0a60 b P4 1 +b0a61 w Q13 1 +b0a67 w D6 1 +b0a86 b F3 1 +b0a91 b P15 1 +b0a91 w E16 1 +b0aa0 b P7 1 +b0aa6 b R2 1 +b0ab1 w O15 Q14 2 +b0b01 b N3 1 +b0b01 w Q13 1 +b0b21 w C4 1 +b0b47 b P14 1 +b0b56 b N4 1 +b0b60 b N12 1 +b0ba7 b H14 1 +b0bc6 w F5 1 +b0bd0 w Q2 P3 2 +b0bd1 b R5 O3 2 +b0bd1 w P2 1 +b0bd6 w E5 1 +b0be7 b J3 E6 E7 3 +b0bf0 b P2 1 +b0c06 b F4 C4 2 +b0c21 b R18 1 +b0c46 b G17 1 +b0c81 b R14 1 +b0c87 b N14 1 +b0ca7 w S15 1 +b0cc1 w D15 1 +b0cd1 w Q16 1 +b0ce1 b R8 1 +b0ce1 w H15 1 +b0cf1 b B4 1 +b0cf6 b C4 1 +b0d00 b N3 1 +b0d00 w P15 1 +b0d01 w R14 1 +b0d11 b S15 1 +b0d16 w M5 1 +b0d21 w R15 1 +b0d30 b C3 1 +b0d30 w D11 1 +b0d57 w B5 1 +b0d66 w S14 B5 2 +b0d81 w C3 1 +b0d90 b R4 Q4 R7 P5 R3 5 +b0db7 w P18 1 +b0dc0 w Q17 1 +b0dd7 w Q14 1 +b0df0 b P15 1 +b0df7 b D4 1 +b0e50 b E18 1 +b0e81 b O14 1 +b0e90 w D15 1 +b0e96 w C17 1 +b0eb6 w C7 C5 2 +b0ee7 w E6 C3 2 +b0f07 w P5 1 +b0f20 b S5 1 +b0f30 w C3 1 +b0f77 w E15 1 +b0f80 b R5 1 +b0f80 w P4 1 +b0f90 w O3 1 +b0f96 w Q17 1 +b0fa0 b P16 1 +b0fa6 w B15 1 +b0fc1 b S17 1 +b1000 b E3 1 +b1001 b O4 1 +b1016 b B18 1 +b1027 b D15 1 +b1071 b P2 1 +b1097 w S16 1 +b10c6 b P18 1 +b10e1 b P5 1 +b10e6 b B15 B18 2 +b10f1 b C13 1 +b1107 b C7 1 +b1196 w D15 C14 2 +b11d6 w N18 1 +b1210 b D7 1 +b1217 b G5 1 +b1236 w R13 1 +b1260 w Q16 1 +b1281 w E16 1 +b12b6 b B2 1 +b12c1 w P5 P3 2 +b12f1 b H17 1 +b1366 b S15 1 +b1370 b R6 1 +b1380 b R15 1 +b1381 w C8 1 +b13b0 w O4 1 +b1406 w R2 1 +b1410 b Q15 1 +b1416 w F4 1 +b1437 w E18 1 +b1480 b B16 1 +b1481 w B2 1 +b14c0 b Q2 1 +b14d0 b M16 1 +b1511 b C4 1 +b1526 b R15 1 +b1531 b F14 G13 2 +b1550 w T5 1 +b1557 b F4 1 +b1587 w R18 1 +b15a0 b P17 1 +b15c1 w Q14 P13 2 +b15d0 w Q13 1 +b15e0 w F16 1 +b16b7 b C5 1 +b16c7 w C2 1 +b16d0 w E18 1 +b16e7 w D6 1 +b16f0 w B18 1 +b16f1 w R4 Q5 R3 3 +b16f7 w B5 1 +b1717 w S15 1 +b1736 w O3 P4 2 +b1750 b B18 1 +b1757 b O17 1 +b1761 b S3 1 +b17b1 b D3 1 +b17c6 b D15 1 +b17e7 w E5 1 +b17f7 w H17 1 +b1810 w P17 1 +b1817 b D16 1 +b18a1 b Q16 1 +b18b1 b A17 1 +b18b7 w N13 1 +b1921 b D14 1 +b1951 b Q4 1 +b1961 w F5 C3 2 +b1970 w Q16 Q3 2 +b19a6 b D5 C6 2 +b19a7 b B5 1 +b1a27 w N13 1 +b1a41 w O3 1 +b1a67 w Q2 1 +b1aa0 b Q6 1 +b1ab1 w D8 1 +b1af1 b Q12 1 +b1b00 b B17 1 +b1b16 b G4 1 +b1b66 b S14 1 +b1b67 b P4 1 +b1b86 w R2 1 +b1bb6 w H13 1 +b1bf0 w P16 1 +b1c00 b O6 1 +b1c11 w S7 1 +b1c31 w F13 1 +b1c91 b F16 1 +b1c96 w C2 1 +b1d17 w E7 1 +b1d26 b N3 Q3 2 +b1d46 w P16 Q15 2 +b1d96 w N3 1 +b1dd0 b R13 S15 2 +b1e60 b E17 1 +b1ea6 b B17 H14 2 +b1eb0 w H16 1 +b1eb1 b D13 1 +b1f06 b G5 1 +b1f66 b H3 1 +b1fa1 w E14 C17 2 +b1fa6 b F16 1 +b1fd1 w D16 1 +b1fd6 b D5 1 +b2016 b E4 1 +b2017 b C18 1 +b2040 w P3 1 +b2047 w P2 1 +b2091 w S17 1 +b2097 w S13 1 +b20b6 b C2 1 +b20d7 w D6 1 +b2110 b S18 1 +b2170 b N6 1 +b2177 b M17 1 +b2180 b Q16 1 +b2187 b F13 1 +b21a7 w C18 1 +b21b0 b F16 G16 2 +b21b0 w P5 1 +b21c1 w R6 1 +b2241 b B17 1 +b2256 w D17 1 +b2266 b N2 1 +b2276 w B16 1 +b2291 w R18 1 +b22e6 b R17 1 +b2337 b G2 1 +b2347 b D16 1 +b2367 b E2 1 +b2377 w R3 N4 2 +b2387 b O13 1 +b2390 w P16 N7 2 +b23b7 b O13 1 +b23d0 w C7 E5 2 +b23e0 w S3 1 +b2437 w C12 1 +b24d6 w P7 1 +b24e1 w D16 1 +b2521 b P7 1 +b2541 w D15 E16 2 +b2547 w E3 1 +b2586 b E14 D13 F15 3 +b25c6 b Q17 H2 2 +b25e0 w B6 1 +b25e7 w C9 F5 2 +b2626 w C18 1 +b2640 b R15 1 +b2647 b Q18 1 +b2660 w R3 1 +b2677 b G6 1 +b2686 w C5 1 +b26c7 b R5 1 +b26e6 b C4 1 +b2711 b R4 R3 2 +b2730 b J3 1 +b2760 b O5 1 +b2761 w F14 1 +b2780 w N5 1 +b27a6 w D18 1 +b27f0 w P18 1 +b2801 w P18 1 +b2820 b L14 1 +b2841 b C6 1 +b2847 w F18 1 +b2867 b D14 1 +b28c1 b C4 1 +b28d0 b N7 1 +b28d1 b D17 1 +b2900 w S3 1 +b2916 w N17 1 +b2921 w E17 1 +b2951 b Q7 1 +b29a1 w S3 1 +b29b1 b R2 1 +b29b7 w S5 1 +b29c0 b Q9 1 +b29e0 b B17 1 +b2a30 w D18 1 +b2a36 b C3 1 +b2a36 w T17 1 +b2a76 b S8 1 +b2a77 b C13 1 +b2a87 b D4 1 +b2a97 b F5 1 +b2aa0 b C4 1 +b2aa1 b O6 1 +b2ab1 b R5 R6 Q6 Q5 4 +b2ae0 w L17 1 +b2ae6 b B4 1 +b2b01 b E7 1 +b2b41 b R3 1 +b2b46 b P16 1 +b2b90 b R9 1 +b2bc7 b B14 1 +b2bf7 b L15 R16 2 +b2c00 b E7 1 +b2c21 b E14 1 +b2c76 b D12 1 +b2c76 w C6 1 +b2c90 w D11 1 +b2cb1 w N6 1 +b2cc7 w P8 1 +b2cd0 w P16 1 +b2d16 b G16 1 +b2d66 b R2 1 +b2d71 b B7 1 +b2d87 w Q4 1 +b2da0 w E4 1 +b2da1 b P16 1 +b2de7 w D15 C13 2 +b2df0 w E2 D3 2 +b2e00 b E14 1 +b2e06 b B8 1 +b2f00 b D3 C4 2 +b2f27 w O6 1 +b2f40 w S14 1 +b2f41 w R19 1 +b2f67 w O2 1 +b2f76 w C5 1 +b2fc0 w E5 1 +b3006 w N16 N17 R17 3 +b3007 b B3 1 +b3007 w R1 1 +b3020 b F6 1 +b3026 w R18 1 +b3036 w P3 1 +b3046 b E16 1 +b3060 b T5 1 +b3071 b H14 1 +b3081 b G17 1 +b3086 b M4 1 +b3087 w Q2 1 +b3091 w B6 1 +b3097 b Q3 S4 2 +b3170 b C14 1 +b31a7 b C9 C7 2 +b31b6 b Q19 1 +b31b7 w Q2 R3 2 +b31c1 b R3 Q4 Q5 P6 Q6 Q7 6 +b31c1 w Q3 1 +b31d6 b P17 1 +b31e1 w C2 1 +b31f7 w R7 1 +b3200 w D15 1 +b3247 b R3 Q3 R6 3 +b3256 b D14 1 +b3260 w D14 1 +b3287 w P13 1 +b3296 w O15 1 +b32a0 b F14 1 +b3316 b C12 1 +b3347 b O15 1 +b3350 b C18 1 +b3356 b G3 1 +b3380 b E18 1 +b33a0 b Q7 1 +b33d0 b E4 1 +b33e6 b R9 1 +b33e7 w M16 1 +b33f0 w E3 F6 2 +b3447 w Q14 Q13 2 +b3466 b S8 1 +b3496 w O16 1 +b34f7 w G7 1 +b3556 b Q18 1 +b3566 b C15 1 +b35a1 w D12 1 +b35b6 b Q2 1 +b35c0 w P3 1 +b35f0 w R12 1 +b3661 w J18 1 +b3687 w C16 1 +b36d6 w F4 1 +b3701 w B6 1 +b3730 b H17 1 +b3730 w E2 1 +b3737 b P7 1 +b3796 b P17 1 +b3797 w O7 1 +b37c1 b C5 1 +b37c7 w P6 Q6 2 +b3807 b H3 1 +b3847 w C5 1 +b3867 b S17 1 +b3936 b P4 1 +b3941 w G3 1 +b3950 b D12 1 +b3961 w P15 1 +b3977 w P2 1 +b3980 b B6 1 +b3991 b G4 1 +b3996 b D13 1 +b39c7 b P5 1 +b39e1 b J5 1 +b3a01 b O14 1 +b3a16 w C6 G3 2 +b3a20 w Q6 Q7 2 +b3a30 b N16 1 +b3a56 w S15 1 +b3ad6 b O16 1 +b3ad6 w R8 1 +b3ae6 w D6 1 +b3af6 b R18 1 +b3b10 w S2 1 +b3b30 b B14 1 +b3b41 w R18 1 +b3b66 w O17 1 +b3b67 w D3 1 +b3b90 w Q6 1 +b3ba1 b B3 1 +b3bd1 b D13 1 +b3bd7 b F2 1 +b3be0 b S15 1 +b3be0 w N2 1 +b3bf7 b P15 1 +b3c10 w C2 1 +b3c61 b N3 1 +b3c76 w R6 Q5 2 +b3c91 b R7 1 +b3ca0 w M3 1 +b3cb0 w S17 1 +b3ce0 w M17 1 +b3ce7 b F17 1 +b3cf0 w C7 B5 2 +b3d00 w D14 1 +b3d16 w Q2 1 +b3d20 b P11 1 +b3d26 b B3 1 +b3d36 w P5 1 +b3d41 w O6 1 +b3d47 w C17 1 +b3d57 w P2 1 +b3d71 w B7 1 +b3d97 w S17 1 +b3da6 w B16 1 +b3dd0 b O3 1 +b3dd1 b R2 1 +b3e10 b S14 1 +b3e30 b Q14 R17 2 +b3e40 b F4 1 +b3e50 w G16 1 +b3e70 w L17 1 +b3e86 w D17 1 +b3ec6 b F14 1 +b3ed1 b R1 1 +b3ee7 w H14 1 +b3f01 b S15 1 +b3f20 w N6 Q3 2 +b3f41 b B7 1 +b3fc0 w E6 1 +b3fc1 w C2 1 +b3fc7 w D3 1 +b3fd0 b P18 1 +b3fe1 w G17 1 +b4037 w D3 1 +b4070 w D3 1 +b4087 b Q2 1 +b40a6 b P3 1 +b4130 w Q15 1 +b4161 b B17 1 +b4166 w G3 1 +b41c1 w B3 1 +b4220 b D13 1 +b4231 b F5 1 +b4250 b R11 1 +b4260 b T17 1 +b4266 b H3 1 +b4301 b C16 1 +b4310 b C3 1 +b4326 b E18 1 +b4327 b O18 1 +b4327 w C17 1 +b4331 w G4 1 +b4336 b O14 1 +b4340 b G16 1 +b43d6 w R15 1 +b43d7 b B13 1 +b43d7 w D18 1 +b43f1 b P6 1 +b4420 b D7 1 +b4426 b P3 1 +b4441 w Q13 1 +b4447 w J16 1 +b4497 b N6 1 +b44c0 w C5 1 +b44d0 w R9 1 +b44d7 w C17 E18 G16 3 +b44f6 w D13 1 +b4517 w F16 1 +b4527 w C2 1 +b4531 w S2 S16 2 +b4566 b D5 1 +b4580 b C17 1 +b4596 w P3 Q4 2 +b45a6 w Q18 1 +b45d0 b B17 1 +b4620 w B16 1 +b46b6 w N17 S15 2 +b46d6 b R14 1 +b46e0 w F13 1 +b4706 w N18 1 +b4720 w R4 1 +b4731 w Q7 1 +b4740 w G18 1 +b4791 w O3 N18 2 +b47d6 b O3 O4 2 +b4831 w R2 1 +b4881 b F11 1 +b48a7 w H2 1 +b48c0 w D15 1 +b4931 b F16 1 +b4980 b C6 1 +b4980 w H2 1 +b49e0 b B14 1 +b49e7 b C3 1 +b4a00 w R15 1 +b4a16 b H17 1 +b4a20 b D8 1 +b4a26 w O4 1 +b4a27 b S6 1 +b4a91 b B2 1 +b4ad1 w D13 B14 2 +b4ae6 w Q18 1 +b4b60 b S6 1 +b4bb0 b D3 1 +b4be0 w E17 1 +b4c00 b Q3 1 +b4c10 w S12 1 +b4c16 b B4 1 +b4c21 b E18 1 +b4c21 w N16 1 +b4c27 b B7 1 +b4c77 b D17 1 +b4c81 w Q15 1 +b4c86 b R9 1 +b4ca6 b H16 F17 2 +b4ca7 w S16 1 +b4cc0 b J3 1 +b4cd0 w E17 1 +b4ce1 w N18 M15 2 +b4d11 w E12 1 +b4d27 w Q2 1 +b4d71 b S7 1 +b4dc0 b N15 1 +b4dc1 b P16 1 +b4dc6 w S3 1 +b4de7 w F16 1 +b4e00 b B18 1 +b4e11 b P15 1 +b4e40 w O6 R3 2 +b4e41 b E6 1 +b4e46 b Q18 1 +b4e61 w Q16 O16 F5 3 +b4ec0 b D9 1 +b4ee0 b R14 1 +b4ee1 b O4 1 +b4ef0 w N16 1 +b4f31 b N15 1 +b4f40 w R13 1 +b4f56 b F3 1 +b4f60 b Q6 1 +b4fb7 b L3 1 +b4fc7 w Q13 1 +b4ff1 w F18 1 +b4ff6 w H16 1 +b5031 w B3 1 +b5056 b B19 1 +b5057 w R4 1 +b5060 w B17 1 +b5080 w C3 1 +b5097 w E13 1 +b50a0 w G14 1 +b50b6 w H18 1 +b50b7 b C8 1 +b50d6 w Q15 1 +b50d7 w D7 1 +b50e1 b C15 1 +b5101 b B17 1 +b5116 w R7 1 +b5131 w B13 1 +b5147 w E5 1 +b5150 b F16 F17 2 +b5171 w R6 1 +b5187 w P5 1 +b51a1 b G3 1 +b5211 w O16 1 +b5230 b O16 1 +b5230 w M15 1 +b5237 b B9 1 +b5256 b G5 1 +b5276 b E3 1 +b5287 b B4 1 +b52a1 w O15 1 +b52b6 w O4 1 +b52d7 b C2 1 +b52e6 w B3 N3 2 +b52f6 b C4 1 +b5331 b D14 1 +b5366 w H19 1 +b5371 w D13 1 +b53d0 b R15 1 +b53e1 b C4 1 +b53e6 w O3 1 +b5431 w N17 1 +b5437 b R3 Q4 Q5 P6 Q6 B16 6 +b5437 w Q3 1 +b5456 w B15 1 +b5466 b O9 1 +b5481 b S5 O2 2 +b54d6 b R8 1 +b54f1 b S18 1 +b54f1 w P18 1 +b54f7 w C18 1 +b5500 b O15 1 +b5516 b G16 1 +b5531 b C16 1 +b5540 b F2 1 +b5560 w D13 1 +b5566 b Q3 1 +b5570 w S11 1 +b5577 b R2 1 +b5587 w P18 1 +b5590 b E8 1 +b5591 b P17 1 +b55c7 w C2 1 +b55d6 b S5 1 +b55f7 w T2 1 +b5626 w E2 1 +b5650 b R3 1 +b5667 w C2 1 +b56a0 b Q14 1 +b56b1 b S5 1 +b56d6 w N15 1 +b5707 b R13 1 +b5710 w O14 1 +b5750 b P4 1 +b5791 w F16 1 +b57c7 w S17 S5 2 +b57e6 b E3 1 +b5800 w D6 D7 2 +b5820 b D5 C5 2 +b5870 b B2 1 +b58d7 w P17 1 +b58e0 w R15 1 +b5906 w G3 E5 2 +b5920 b Q8 1 +b5936 b E13 1 +b5946 b E15 1 +b5957 b G5 1 +b5960 b C5 1 +b5977 b R17 1 +b5981 b S17 1 +b59b6 b G15 1 +b59c7 b C4 1 +b59c7 w C3 D4 E4 F5 F4 G4 6 +b5a16 b F6 1 +b5a40 b D14 1 +b5a86 b P14 1 +b5a97 w S17 1 +b5ab0 b Q3 1 +b5af6 b R4 1 +b5b00 b E15 1 +b5b21 b D19 1 +b5b41 b P6 1 +b5b57 w C7 1 +b5b66 w H17 1 +b5b67 b M4 1 +b5b80 b Q17 Q16 2 +b5b87 b E13 1 +b5ba6 b N5 1 +b5c00 b C6 1 +b5c00 w F6 1 +b5c57 b F4 1 +b5ca0 b M16 1 +b5cc7 b N2 1 +b5d16 b Q3 1 +b5d27 b M18 1 +b5d27 w C3 E2 G4 3 +b5d30 w P16 1 +b5d40 w E18 1 +b5d46 b B2 1 +b5d86 b B17 1 +b5dd6 b E2 B2 2 +b5e00 w S6 1 +b5e30 b D3 1 +b5e61 b Q8 1 +b5e96 w D14 1 +b5eb0 w F3 1 +b5ec7 b Q13 1 +b5ed0 w R15 1 +b5ee1 w R17 1 +b5f20 w R8 1 +b5f51 b D5 1 +b5f67 b R1 1 +b5f70 b T17 1 +b5f91 w H17 1 +b5fa7 b B16 1 +b5fb7 b J5 1 +b5fc0 w M16 1 +b5fe7 b L2 1 +b6050 b O17 1 +b6056 b B17 1 +b6066 b N18 1 +b6091 b S15 1 +b60c7 b N3 1 +b60d7 w Q14 1 +b60f1 w G6 1 +b6110 w C5 1 +b6131 b E17 1 +b6186 b R12 1 +b61a6 w C13 1 +b61b1 w B15 1 +b61d0 w H4 1 +b61d6 w M17 1 +b61e1 b P2 S6 2 +b6201 b F15 F16 2 +b6210 w O14 1 +b6240 w D7 1 +b6247 b E15 1 +b6257 b Q4 1 +b6266 b C7 1 +b6271 b O18 1 +b6276 b N11 1 +b6277 w Q7 Q4 2 +b6281 b D18 1 +b62c0 w C5 1 +b62c1 w S3 1 +b62c6 w L18 1 +b62e1 b P5 1 +b6301 w E2 1 +b6327 b R4 1 +b6366 w R3 1 +b63a1 b H3 1 +b6410 w S5 1 +b6481 b P5 1 +b64a0 w P4 1 +b64e0 w D8 1 +b6501 b D13 1 +b6501 w B3 1 +b6516 b G3 E3 2 +b6526 b F4 1 +b6536 w P15 1 +b6537 b R7 1 +b6560 w Q15 1 +b6576 w Q14 1 +b6597 w D18 C17 D17 C12 4 +b65c7 w G13 1 +b65d7 w B3 1 +b65e6 b F4 1 +b65e7 b O3 1 +b6606 b D15 1 +b6667 w R16 1 +b6697 b G4 1 +b66a0 b R17 1 +b66a1 b N5 1 +b66b6 w C6 1 +b66d6 b E2 1 +b66e6 b C18 1 +b66e6 w H18 1 +b66f6 w Q2 1 +b6700 w E11 1 +b6720 w Q15 1 +b6730 w N15 1 +b6796 b Q13 1 +b6836 b J17 1 +b6840 b C17 1 +b68a7 w Q18 1 +b68b6 b Q3 1 +b68d0 b P12 O12 2 +b68f0 b F3 1 +b6980 w G7 1 +b69a1 b D14 1 +b69b1 w C9 1 +b6a00 b D6 1 +b6a30 b D5 J3 C6 C7 C8 D7 D6 7 +b6a30 w D5 D6 G3 H3 J3 G4 H4 J4 8 +b6a41 b P14 Q13 R15 3 +b6a47 b P6 1 +b6a56 w D2 1 +b6a61 w E2 1 +b6a86 b P17 1 +b6aa1 b R3 S5 Q7 3 +b6ab1 w O17 1 +b6ad0 w D5 1 +b6ae0 b P13 1 +b6b20 b Q6 1 +b6b20 w E13 1 +b6b27 w E13 1 +b6b46 w N2 1 +b6b67 b D17 1 +b6bd0 b P6 1 +b6c11 b G2 B3 2 +b6c61 b P4 1 +b6ca0 b T2 1 +b6ca6 w E16 1 +b6d06 w D2 1 +b6d31 b R2 1 +b6da0 w R15 1 +b6da7 b F5 1 +b6dc1 b Q17 N4 2 +b6dd7 w B7 P14 2 +b6df1 w E12 1 +b6df6 w B4 1 +b6e06 b F16 1 +b6e16 b Q6 1 +b6e41 b N15 1 +b6e60 b Q14 1 +b6e60 w B4 1 +b6e67 w D18 1 +b6e97 b E1 1 +b6eb7 b Q7 O6 2 +b6ed1 b M16 Q12 2 +b6f21 w R7 1 +b6f31 b S13 1 +b6f36 w P6 1 +b6f37 b E6 1 +b6f67 w R18 1 +b6f96 w E14 1 +b6fa1 b E14 1 +b6fb6 b Q3 1 +b6ff1 b F6 1 +b6ff7 b E3 1 +b7001 b P5 1 +b7020 b R18 1 +b7037 w F4 1 +b7091 w P18 1 +b7096 b R3 1 +b70f7 b D13 1 +b7120 w M3 1 +b7127 b Q16 R17 2 +b7140 w B15 1 +b7147 w E18 1 +b7171 b J4 1 +b7181 w O3 Q16 2 +b71a0 w B2 1 +b71d0 b D17 C16 2 +b71f7 w N4 1 +b7211 b D2 1 +b7236 b O17 N16 2 +b7267 b S6 1 +b7276 b M18 1 +b7281 b B4 1 +b72a7 b R4 1 +b72d1 w C4 1 +b72d7 w D13 1 +b72f7 w O5 N4 P3 3 +b7311 b C12 1 +b7317 b M3 1 +b7330 b P12 O5 2 +b7350 b S5 1 +b7380 w C9 1 +b7390 w F4 1 +b73d0 w F15 1 +b73d7 b C15 B15 D16 3 +b7400 w D2 1 +b7427 b N16 1 +b7461 b A16 1 +b7491 b B15 1 +b74c1 b D14 1 +b74f7 b C13 1 +b7501 b G16 1 +b7530 b R5 1 +b7536 w S16 1 +b7561 b P18 1 +b7561 w Q13 1 +b7570 b O6 1 +b7586 b F13 1 +b7587 w O15 1 +b75b0 w C15 1 +b75e1 w O15 1 +b75f0 b C18 1 +b7607 w S14 1 +b7611 w P4 1 +b7636 b D6 1 +b7661 b D4 1 +b76a6 b T17 1 +b7700 w Q5 1 +b7711 w G16 1 +b7716 b O16 1 +b7751 w R17 1 +b7760 w D1 1 +b7781 b C15 1 +b7790 w C14 1 +b77a0 w N4 R4 2 +b77b7 b E6 1 +b77d7 w Q14 1 +b7816 w Q12 1 +b7851 w R3 R4 O3 3 +b7866 b C3 1 +b7896 w F5 1 +b78a0 w Q17 R16 2 +b78c0 b P3 1 +b78d1 w P14 1 +b78d7 b B15 1 +b78f6 b G17 E17 2 +b7927 b E18 1 +b7967 w Q2 1 +b7991 b R2 1 +b79a6 b R3 1 +b79a7 w P6 1 +b79f1 w Q2 1 +b7a21 b P18 1 +b7a47 w F16 1 +b7ac6 w P17 P18 2 +b7ae7 b J17 1 +b7af0 w E4 1 +b7af6 b O5 1 +b7b36 b Q8 1 +b7b40 w R6 1 +b7b56 w C4 D5 D9 3 +b7b57 w F16 1 +b7b71 b O14 1 +b7b87 w E6 D6 2 +b7bb0 w C13 1 +b7bf1 b D6 1 +b7c07 b G18 1 +b7c77 b R4 1 +b7d00 b C15 1 +b7d20 w G5 1 +b7d67 b C11 1 +b7d67 w S4 1 +b7da7 w N5 1 +b7de7 b G6 1 +b7e41 b C16 1 +b7e76 b E4 1 +b7e77 w O7 1 +b7ea7 w P16 Q15 2 +b7ec0 b H15 1 +b7ec1 b O18 1 +b7ec6 w B15 1 +b7ee1 w G8 1 +b7f10 b P16 1 +b7f56 w P15 1 +b7f57 b N3 1 +b7f60 b G15 1 +b7f66 b M2 1 +b7f80 w D6 1 +b7f81 w E3 1 +b7f97 w D15 1 +b7fa0 w G15 D13 2 +b7fb1 w R12 1 +b8007 b R12 1 +b8020 b D13 D16 E14 3 +b8020 w Q3 1 +b8021 b O4 1 +b8031 b N16 1 +b8036 w C6 1 +b8047 b R15 Q17 2 +b8047 w Q18 1 +b8070 w G17 1 +b80a1 b S15 1 +b80c0 b N17 1 +b80d0 w O4 O2 2 +b80d1 w O17 1 +b80e7 w O16 1 +b8146 b B4 1 +b8171 w O2 1 +b8176 w P15 1 +b8187 w S16 1 +b81b6 w P16 1 +b81c6 b D14 1 +b81e6 w M3 1 +b81e7 b S15 E6 2 +b8206 b R18 1 +b8231 w Q17 1 +b8237 b R16 1 +b8246 b J18 1 +b82c7 w B15 1 +b82f1 b D13 1 +b82f6 b P6 D6 2 +b8340 b G5 1 +b83b0 w G14 1 +b83b1 b S5 1 +b83f6 b R13 F17 2 +b8400 w S5 1 +b8456 w P14 1 +b8486 b B3 1 +b8497 b G17 1 +b84b7 w R2 1 +b84c6 w R18 1 +b8500 b D16 1 +b8511 b N15 1 +b8530 b N5 F5 2 +b8546 b P16 1 +b8570 b G16 1 +b8571 b Q17 1 +b8577 w E18 1 +b8580 w C18 1 +b8596 b G2 1 +b8597 b Q2 1 +b85a0 w D6 1 +b85a6 b P18 1 +b85b7 w E17 1 +b85e7 w E15 1 +b8640 b G14 1 +b8656 w C15 C18 2 +b8680 b B17 1 +b86c0 b Q17 1 +b86f0 b A5 1 +b8747 b R15 1 +b8767 b C5 1 +b8770 w O4 1 +b8771 w P7 1 +b8780 b H5 1 +b87a6 w E15 1 +b87d0 w C17 1 +b8800 w O2 1 +b8807 b Q17 1 +b8810 b C4 1 +b8857 b D2 1 +b8860 w P4 1 +b8896 b C13 1 +b88d1 b Q14 N17 2 +b88f6 b S18 1 +b8907 w B4 1 +b8916 b S2 1 +b8936 w G3 1 +b8937 b S6 1 +b8967 w C12 1 +b8997 b P17 1 +b89a0 w J14 1 +b89c1 w O16 N15 2 +b89c6 w Q4 1 +b8a16 w P3 1 +b8a17 b H2 1 +b8a37 w Q3 1 +b8a56 w O16 1 +b8a57 w S3 1 +b8a71 b Q7 1 +b8a80 w B2 1 +b8ab7 b R17 1 +b8ac6 b S4 1 +b8ae1 w F17 1 +b8b30 b S8 N18 2 +b8b80 w N3 1 +b8bf7 b Q6 1 +b8c30 w M4 1 +b8c37 b C8 1 +b8c46 b R8 1 +b8c57 b E8 1 +b8cc0 b E18 1 +b8cc6 w S15 1 +b8ce6 w G16 1 +b8cf1 b Q18 1 +b8d27 b M3 1 +b8d70 w C7 1 +b8d86 b R18 1 +b8da0 b N14 1 +b8da6 b D16 1 +b8df6 w Q7 1 +b8e07 w Q18 1 +b8e41 w Q4 1 +b8e47 w R4 1 +b8e60 w P17 1 +b8ea7 b E18 1 +b8f40 w D13 1 +b8f41 w Q2 1 +b8f61 b O18 1 +b8f77 b P17 1 +b8f81 w D9 R13 2 +b8fa1 w E4 1 +b8fa6 w F4 1 +b9011 b B3 1 +b9037 b F17 1 +b90d1 b G16 Q7 2 +b90f6 w L3 1 +b9100 b O6 1 +b9110 w B14 1 +b9131 b G3 1 +b9137 b G4 1 +b9151 w Q3 1 +b9180 b Q1 1 +b9187 b B7 1 +b91a0 b E13 1 +b91e1 w C16 1 +b9220 b E4 1 +b9231 w Q4 1 +b9270 b S5 1 +b9271 w N17 1 +b9287 w N16 1 +b9290 w P12 1 +b9296 w Q4 1 +b92b7 b D14 C17 2 +b92d0 b R3 1 +b92d7 w N4 1 +b92e1 w C18 1 +b92f7 w C17 B17 2 +b9351 w B6 1 +b93b6 b C5 1 +b93c0 b D4 1 +b93c0 w P2 1 +b9430 w P14 1 +b9437 b G17 1 +b9447 b F18 1 +b9461 w R14 1 +b94a1 w P6 1 +b94d7 b C5 1 +b94e7 b F17 1 +b94f0 b C7 1 +b9501 b C15 1 +b9507 w C18 1 +b9511 w F17 1 +b9516 b F18 1 +b9520 b O5 1 +b9536 b T3 1 +b9556 w G5 1 +b95c6 w O2 1 +b95d7 b R17 1 +b9636 w B3 1 +b9640 w O3 1 +b9667 w B14 1 +b9671 w P4 1 +b9690 b J17 1 +b96b1 w Q3 1 +b96b7 b M3 E5 2 +b96f6 w O14 1 +b9710 b P15 1 +b9730 b N4 1 +b9750 b C9 1 +b9760 b D3 D4 G3 E5 C3 5 +b97b6 b Q8 1 +b97c6 w R5 1 +b9827 b C12 1 +b9876 w C15 1 +b9887 b O2 1 +b9891 w R5 1 +b9910 w B2 1 +b9911 b C13 1 +b9947 w C3 1 +b99d0 w Q4 1 +b99d1 w C6 1 +b99e7 b L17 1 +b9a11 w R4 1 +b9a36 w P3 1 +b9a87 w O16 1 +b9ab0 w B15 1 +b9ad6 w C14 1 +b9ae6 w P4 1 +b9b41 b B16 1 +b9b50 w D6 1 +b9b57 w F6 1 +b9b97 w P15 1 +b9ba6 w Q4 1 +b9be0 w O4 1 +b9c00 b H15 1 +b9c01 b R7 1 +b9c20 w M17 1 +b9c30 b D13 1 +b9c41 b G18 1 +b9c91 w R7 1 +b9cb0 w E7 1 +b9cf0 b C5 1 +b9d06 w G17 E15 2 +b9d60 w C17 1 +b9d76 b R15 1 +b9db7 b N4 1 +b9dc0 b O4 1 +b9dc7 w N3 1 +b9dd6 b J16 1 +b9de0 b R3 1 +b9e00 w B15 1 +b9e40 w D17 1 +b9e60 b G4 1 +b9e70 w N2 1 +b9e81 w P14 1 +b9e86 b C17 1 +b9e90 b E12 1 +b9f21 w N18 1 +b9f60 b O18 1 +b9f70 w R6 1 +b9f77 b C8 1 +b9f97 w C2 1 +b9fb1 w Q4 1 +b9fb6 w D18 1 +b9fd0 b D5 1 +ba020 w G15 1 +ba046 w P18 1 +ba050 w P18 1 +ba0e7 w P2 1 +ba106 b F18 1 +ba106 w B4 1 +ba107 w E3 1 +ba121 b E18 1 +ba147 b R4 1 +ba150 w B5 1 +ba1a0 w C2 1 +ba1e0 b R18 1 +ba206 b F8 1 +ba221 w C17 1 +ba226 b R17 1 +ba230 w S16 1 +ba260 w E6 1 +ba261 b D7 1 +ba267 w G2 1 +ba277 w R2 1 +ba280 b J3 1 +ba2f0 w S4 Q6 2 +ba321 w P2 1 +ba366 w R8 F17 2 +ba3b7 b C3 H4 2 +ba3e1 b C18 1 +ba3e6 w B17 1 +ba3e7 b B16 1 +ba417 b S2 1 +ba417 w P5 1 +ba447 b O2 1 +ba4a6 b C5 1 +ba4a7 b B4 1 +ba4b6 w R8 1 +ba4c7 b G15 1 +ba4f7 w D5 1 +ba516 b R7 1 +ba546 b R3 1 +ba547 b B14 1 +ba5a0 w R2 1 +ba5e7 w S17 S14 2 +ba5f1 b M16 1 +ba5f1 w R2 1 +ba601 b D4 1 +ba607 w C1 1 +ba611 w E15 1 +ba631 b G2 1 +ba636 w S1 1 +ba641 w Q7 1 +ba661 w R17 1 +ba666 b Q17 H3 2 +ba677 b Q6 R3 2 +ba687 b O7 1 +ba787 w B4 1 +ba821 w D18 1 +ba826 b S6 1 +ba856 b D13 1 +ba857 b D15 1 +ba861 b F5 1 +ba876 w R2 1 +ba877 b O15 1 +ba8a0 b H5 1 +ba8b1 b R4 1 +ba8c0 b O4 1 +ba8d1 b F5 1 +ba901 w G14 1 +ba917 w P13 1 +ba947 b H6 1 +ba951 w C14 1 +ba976 w O16 1 +ba997 b D6 1 +ba9a0 b T15 1 +ba9b7 b Q5 1 +baa06 b O13 1 +baa31 w F5 1 +baa37 b E4 1 +baa50 b C16 1 +baa56 w S11 1 +baa77 b O12 1 +baaa0 w Q12 1 +baab6 b R7 1 +baae6 b B4 1 +bab86 b Q7 1 +bab87 b H16 R15 2 +baba0 w G3 1 +babc0 b O1 1 +babd1 b P12 F3 2 +babf1 w E15 1 +bac00 w E3 1 +bac01 w P3 1 +bac11 w J2 1 +bac26 b B14 1 +bac30 w E6 1 +bac40 b N16 Q16 O15 3 +bac90 b P3 1 +baca0 w R16 1 +baca1 w F15 C6 F3 D6 F4 D7 G4 C7 G3 C9 J3 11 +bacf7 w E16 1 +bad06 b P3 1 +bad07 w D17 1 +bad30 b D18 1 +bad41 w B14 1 +bad51 b C2 1 +bad66 b F17 F16 2 +bad76 b F6 1 +bada7 w Q18 1 +badd6 b F17 1 +badf1 b G15 1 +bae26 w C18 1 +bae80 b G12 1 +bae97 w H3 1 +baed0 b R13 P15 Q13 3 +baef0 w Q13 1 +baef7 b S14 1 +baf20 b N2 1 +baf30 w P18 1 +baf31 w C16 1 +baf41 w R16 1 +baf57 w G6 1 +baf80 b O16 1 +bafa6 b S16 1 +bafc6 w D17 1 +bafd1 w R7 M15 2 +bafe1 b C4 1 +bb021 b C3 1 +bb026 b O18 1 +bb026 w O15 1 +bb031 b F2 1 +bb057 b B15 1 +bb057 w O16 1 +bb070 b Q6 1 +bb0d6 w E5 D4 2 +bb0e6 b B15 1 +bb117 w G16 1 +bb121 w F15 1 +bb1b1 b F7 1 +bb1d1 w C4 1 +bb1d7 b C5 1 +bb1f6 w E15 1 +bb200 b C6 1 +bb200 w N14 Q17 2 +bb221 w N3 1 +bb257 w C3 1 +bb2a0 b S6 1 +bb2b6 b O16 1 +bb2b7 b E14 1 +bb2d7 w B2 1 +bb2e7 w R15 R14 Q14 Q15 4 +bb316 b O17 1 +bb340 w R1 1 +bb377 b O17 1 +bb397 w P2 S6 2 +bb3a7 b O15 Q14 2 +bb3c7 w L16 1 +bb3e0 w P17 1 +bb3f1 b Q4 1 +bb410 b D9 1 +bb411 w B2 1 +bb436 w M3 1 +bb477 b R18 D17 2 +bb481 w O5 1 +bb490 w F16 1 +bb491 b R2 1 +bb491 w Q9 1 +bb4b1 b Q9 1 +bb4d6 b E15 1 +bb4e6 b D17 1 +bb501 b O4 1 +bb511 b C18 1 +bb527 w S11 1 +bb536 w B12 1 +bb537 w P17 1 +bb546 w R3 1 +bb547 b C17 1 +bb5c6 w M15 1 +bb5d1 b P18 1 +bb5d7 w C3 1 +bb607 b B6 1 +bb627 b Q5 1 +bb6b0 w C11 1 +bb6c7 b E6 1 +bb707 b E15 1 +bb720 w H6 1 +bb741 w O3 1 +bb756 w R2 1 +bb770 w C4 D3 2 +bb7a0 w C9 1 +bb811 b E5 1 +bb841 b D3 1 +bb860 w F4 1 +bb8b1 b C13 1 +bb8c1 w B17 1 +bb8f1 b G4 H3 J3 3 +bb920 w Q14 1 +bb966 b G17 1 +bb967 w Q3 1 +bb9a6 b F14 1 +bb9b7 b P6 1 +bba21 w S15 1 +bba36 b P2 1 +bba41 w N4 1 +bba50 b D15 1 +bba67 b P17 Q16 2 +bba80 b D7 1 +bba81 w E17 1 +bba87 b F4 E5 2 +bba87 w F3 1 +bbab6 w C18 1 +bbac6 b C3 1 +bbae0 w Q7 Q4 P6 3 +bbb07 b N3 1 +bbb37 w N15 1 +bbb51 w P1 1 +bbb56 b O17 1 +bbb96 b F5 1 +bbbf1 b F6 1 +bbc00 b D2 1 +bbc26 w R14 1 +bbc30 w H6 1 +bbc57 w D5 1 +bbc87 b N6 1 +bbca0 w Q4 1 +bbca6 b D18 1 +bbcb0 b E15 R13 H15 H14 4 +bbd26 w S3 D15 2 +bbd87 w M4 1 +bbdc0 b O13 1 +bbe27 b C12 1 +bbe30 w D6 C6 2 +bbe50 b Q6 R6 2 +bbe86 w P12 1 +bbef0 w F13 1 +bbef6 w A17 1 +bbf00 b M17 1 +bbf10 w R2 1 +bbf30 w O14 1 +bbf56 b P18 1 +bbf67 b B3 1 +bbfb0 b M3 1 +bbff1 b G14 1 +bc007 w E13 1 +bc030 w O18 1 +bc076 b O16 1 +bc077 b C2 F4 2 +bc090 w D8 1 +bc0c1 w Q5 1 +bc0f1 w D6 1 +bc137 w R4 1 +bc147 w F18 1 +bc150 w G15 1 +bc176 b P15 1 +bc197 b O18 1 +bc1d1 w E15 1 +bc1d7 b C11 1 +bc1f7 w F16 1 +bc200 b G15 1 +bc201 b G3 1 +bc216 b R16 1 +bc257 w O5 O4 2 +bc2b6 b C17 1 +bc380 w D2 1 +bc381 b Q6 1 +bc387 b G4 1 +bc391 b M15 1 +bc3b7 b N12 1 +bc3d6 w D7 1 +bc3d7 w P16 O16 Q15 P17 4 +bc3e0 b E5 1 +bc401 b M3 1 +bc426 w M17 1 +bc437 w F16 1 +bc440 b B14 1 +bc467 b M3 1 +bc476 b E16 F17 2 +bc497 b N17 1 +bc4a0 w F5 1 +bc4a6 b R3 1 +bc4d0 w D9 1 +bc4e1 b C4 1 +bc4e1 w R6 1 +bc507 w E14 1 +bc516 w C17 1 +bc526 b N4 1 +bc540 w Q3 1 +bc557 b R12 1 +bc566 b N8 1 +bc570 b P17 S17 2 +bc571 b P6 1 +bc571 w D6 E2 2 +bc576 w C15 1 +bc577 b R18 1 +bc577 w N16 1 +bc581 b C4 1 +bc581 w C3 D6 D4 E4 F5 E5 E7 D7 F3 G3 G4 11 +bc5a1 b Q3 1 +bc5c1 b G16 1 +bc5f1 b Q14 1 +bc636 b F7 1 +bc640 b S18 1 +bc640 w P18 R17 Q14 R14 4 +bc641 w D16 1 +bc690 w N11 1 +bc6c1 w E15 1 +bc6d1 w R16 1 +bc6e7 w H16 1 +bc746 w C17 1 +bc747 b R3 1 +bc780 b E14 1 +bc781 b R13 1 +bc796 w D5 1 +bc7c1 b P2 1 +bc7f0 b R17 1 +bc7f6 w C15 D16 2 +bc8a7 w P7 1 +bc8d0 b J17 1 +bc8f0 b D2 1 +bc8f7 b P6 1 +bc910 b N17 1 +bc921 b R17 1 +bc921 w O3 1 +bc930 b O4 1 +bc931 w M17 1 +bc966 w R5 1 +bc9a6 w G16 1 +bc9d6 b G14 1 +bca36 w P5 1 +bca37 w F15 1 +bca70 b G6 1 +bca71 w C16 1 +bcaa6 b E6 F3 2 +bcaf1 w C18 1 +bcb00 b C16 1 +bcb20 w C8 1 +bcb96 w F2 1 +bcba1 w R14 1 +bcbc6 w G2 1 +bcbd0 b R2 1 +bcbe6 b F16 1 +bcc16 b D6 C6 E3 E5 G3 G4 H4 7 +bcc16 w C3 C5 D6 D7 C7 5 +bcc90 w O3 1 +bcc96 w G14 1 +bcca0 w O7 1 +bcce6 w R17 1 +bcd01 w E3 1 +bcd20 w R4 1 +bcd37 b C18 1 +bcd50 w M5 1 +bcd60 w C13 F17 2 +bcd87 b R14 1 +bcdc0 w D15 1 +bcdd1 w O16 1 +bcde6 w E4 1 +bce70 w P3 1 +bce71 b R16 1 +bce87 w N14 1 +bced6 b N16 1 +bcee6 w B15 1 +bcef1 w N6 1 +bcf10 b P16 1 +bcf50 w Q6 1 +bcf56 b E3 1 +bcf76 b B16 1 +bcf97 w R16 1 +bcfa1 b Q17 1 +bcfa7 w Q15 1 +bcfb7 w G15 1 +bcfd1 w G6 1 +bcfe0 w Q9 1 +bd007 b R4 1 +bd077 w E9 1 +bd0c7 b F7 1 +bd0d6 b P14 Q13 O15 3 +bd150 w Q18 1 +bd151 w B5 1 +bd157 w R4 1 +bd1a0 b C9 1 +bd1d6 b C13 1 +bd1f0 w C3 1 +bd201 b D13 1 +bd220 w G17 E15 G16 3 +bd230 b Q3 1 +bd240 b E5 1 +bd257 b R4 1 +bd261 b Q4 1 +bd2c1 b B17 1 +bd2d0 w S2 1 +bd2d1 w B6 R17 2 +bd2f0 w D18 1 +bd320 w N3 R6 2 +bd350 b F6 1 +bd387 b D17 1 +bd391 b D16 1 +bd3c0 b Q14 Q13 2 +bd3f7 w Q2 1 +bd420 b R5 1 +bd426 b G4 1 +bd430 b F5 N3 2 +bd441 b N5 1 +bd497 w Q14 1 +bd540 b E6 1 +bd626 b G15 1 +bd636 b F16 1 +bd636 w D18 1 +bd656 b R15 1 +bd677 b C3 1 +bd696 w C5 1 +bd717 w S2 1 +bd730 w S7 P7 2 +bd741 b D3 1 +bd770 w M16 1 +bd797 b Q11 1 +bd7b0 b R15 1 +bd7d1 b Q15 1 +bd7d7 w B8 1 +bd801 w B14 1 +bd821 w F2 1 +bd836 w D14 1 +bd846 b P15 1 +bd860 b M16 1 +bd870 b N3 1 +bd880 w D6 1 +bd881 w O3 1 +bd8f1 b R6 Q4 2 +bd8f1 w A3 1 +bd907 b P15 1 +bd916 w Q7 1 +bd931 w P14 1 +bd947 b G14 1 +bd961 b Q16 1 +bd971 b C4 1 +bd987 b C18 1 +bd9b6 b N5 1 +bd9e6 w B2 B8 2 +bd9f0 w G4 1 +bda06 b D15 1 +bda20 b L6 1 +bda41 b R2 O2 2 +bda51 b G15 F16 2 +bda60 b G17 1 +bdaa6 w H5 1 +bdb20 w Q15 1 +bdb76 w H15 1 +bdb86 w E16 1 +bdb87 b S3 1 +bdb96 b G4 1 +bdba0 b R6 1 +bdba6 w D5 1 +bdbc7 b L5 R4 2 +bdbf7 b S15 1 +bdc10 b E12 1 +bdc27 b R17 1 +bdc46 b F3 1 +bdc50 b E4 1 +bdc56 b N4 1 +bdc56 w B17 1 +bdc61 w O17 1 +bdc86 w P5 1 +bdcb0 b E3 1 +bdcb7 b R7 1 +bdcd7 w P14 1 +bdd17 b E15 1 +bdd37 b B5 1 +bdd51 w R17 1 +bdda0 b E15 1 +bdda1 w D15 1 +bddb7 b E16 1 +bddc0 w P5 1 +bddd0 b Q7 1 +bddd1 w O2 1 +bddd6 b Q2 1 +bde10 b D8 1 +bde40 b B6 1 +bde61 w Q6 1 +bde67 b Q16 1 +bded6 b D17 1 +bdee0 w N17 1 +bdef6 b E14 D14 2 +bdf10 b F5 1 +bdf41 b P14 1 +bdf50 b G3 1 +bdf60 b B5 1 +bdfd6 w C14 D13 2 +bdff6 b D5 1 +be037 b G16 1 +be056 w S6 1 +be057 b G6 1 +be0c6 b R13 1 +be0e0 w C16 D17 2 +be0f0 b S18 1 +be100 w E17 1 +be101 w B8 1 +be126 w S15 1 +be1a1 b R14 1 +be1b6 b B6 1 +be1c0 w D17 C16 2 +be1f7 w P17 1 +be220 b O13 1 +be257 w N4 1 +be2f0 w Q4 1 +be3c1 b N3 1 +be3f7 b B15 1 +be420 b D17 1 +be447 b E3 1 +be486 w O18 1 +be4c1 b Q8 1 +be4d7 b L4 1 +be4e6 w Q4 1 +be536 w S2 1 +be546 b C11 1 +be557 w O7 1 +be5a7 w M2 1 +be5c0 b B15 1 +be5d1 w Q9 1 +be5d7 w G4 1 +be5f6 b C2 1 +be626 b R7 1 +be627 b Q14 1 +be660 w P16 1 +be680 b Q5 1 +be6b7 w C3 1 +be6f0 b O6 1 +be711 w E6 1 +be727 b R14 1 +be727 w S4 1 +be731 w Q4 1 +be766 w O18 1 +be780 b N4 1 +be790 b M3 1 +be791 b E6 1 +be7d7 w E16 F15 2 +be7e7 w H4 1 +be7f0 b R17 1 +be830 b Q13 1 +be847 b B17 1 +be870 w J17 1 +be871 b F15 1 +be886 b M17 1 +be8c6 w R16 1 +be8c7 w B15 1 +be8d0 b R3 1 +be950 b E5 1 +be961 b S3 1 +be966 w F16 1 +be971 b C16 1 +be987 w Q14 1 +be991 b S13 1 +bea20 w F15 1 +bea30 w E15 1 +bea56 b Q16 1 +bea66 w E17 1 +bea71 w B13 1 +beac1 b L16 1 +bead0 b A4 1 +beae7 w R5 1 +beaf1 b E14 1 +beb01 w P17 1 +beb61 w C5 B5 D4 3 +beb70 w E2 1 +bebe1 b C14 1 +bebf7 b L17 1 +bec11 w B16 S7 2 +bec26 b P5 1 +bec26 w D14 1 +bec51 w G16 D16 2 +bec80 b Q14 1 +becc7 w F5 1 +becd7 b M16 1 +bece7 b D6 1 +becf1 b D13 1 +becf7 b R2 C9 2 +bed06 w Q16 1 +bed11 w D4 1 +bed61 b E5 E6 C3 C7 4 +bed70 b N4 1 +bedb6 w M4 1 +bedc1 w E5 1 +bedc6 w O6 1 +bede1 b C7 1 +bee06 b B17 1 +bee21 w O15 1 +bee26 b P13 1 +bee56 b Q8 1 +bee76 w R5 F6 2 +bee87 b H3 E18 2 +beeb1 w H4 1 +beee7 w E2 1 +bef00 b N15 1 +bef17 b E13 1 +bef57 w S7 1 +bef66 w E15 1 +bef86 b R3 1 +befa0 w E7 1 +befc7 b C17 1 +bf020 b D7 1 +bf030 b C14 1 +bf051 w D16 C17 2 +bf057 w E3 1 +bf080 b O6 1 +bf086 w S13 1 +bf0c1 w C8 1 +bf111 b D15 E16 2 +bf140 w J4 Q4 2 +bf167 w E8 1 +bf171 b R16 1 +bf1c7 w R17 1 +bf1e6 b D14 1 +bf1e7 w D4 1 +bf1f6 b B16 1 +bf237 w D19 1 +bf260 b F16 1 +bf270 w O16 1 +bf287 b D4 1 +bf2e6 b P16 1 +bf2f7 b N4 R3 2 +bf330 w P7 1 +bf351 w J3 1 +bf361 b E4 1 +bf367 b B3 1 +bf397 b R3 Q6 Q4 P4 O5 P5 P7 Q7 O3 N3 N4 11 +bf397 w R4 1 +bf3c0 b L3 1 +bf420 w P2 1 +bf427 w D5 1 +bf466 w O16 1 +bf476 b N16 1 +bf476 w B18 1 +bf4d1 b E14 1 +bf501 w Q3 1 +bf517 b C4 1 +bf536 b B3 1 +bf556 w B14 1 +bf571 b G17 1 +bf571 w D2 1 +bf5b0 w B17 1 +bf5e0 b E16 F16 C13 C12 C11 D13 D12 D11 8 +bf5e0 w E16 C11 F17 G17 H17 G16 F16 7 +bf601 w L16 1 +bf607 b R12 1 +bf621 b B5 P13 2 +bf666 b R17 Q16 2 +bf671 b C3 1 +bf681 b S15 1 +bf681 w E6 C3 2 +bf726 b R5 1 +bf737 b R16 1 +bf741 b N14 1 +bf777 w G3 H2 2 +bf790 w O17 Q16 Q17 3 +bf7a7 w D2 1 +bf7e1 b C15 1 +bf811 b C1 1 +bf821 w P7 1 +bf827 b B4 1 +bf857 w H5 1 +bf860 b M3 1 +bf861 b O16 1 +bf866 b C2 1 +bf881 b F16 1 +bf8a7 w O5 1 +bf8b1 w R14 1 +bf910 w E5 1 +bf916 b F6 1 +bf917 w S3 1 +bf920 b H16 1 +bf927 w F15 1 +bf930 b P6 1 +bf940 w Q2 1 +bf961 b B4 1 +bf9f0 b N17 P15 2 +bfa07 b C15 1 +bfa10 b C17 1 +bfa11 b N5 1 +bfa20 w F15 1 +bfa50 w R6 1 +bfa51 b R15 1 +bfa61 b C17 1 +bfa66 b G4 1 +bfa81 b B2 1 +bfa81 w N16 1 +bfa91 b R16 1 +bfab6 b P3 1 +bfad1 w O17 1 +bfb60 b Q12 1 +bfb70 w P15 1 +bfba1 b P18 1 +bfbf0 w B7 1 +bfbf1 b R17 Q16 P16 O15 O16 N16 6 +bfbf1 w R16 1 +bfc31 b N4 1 +bfc40 w D14 1 +bfc56 w O13 1 +bfc70 b G15 1 +bfc76 w R2 1 +bfc77 w T14 1 +bfcd6 w N18 1 +bfd17 w P3 1 +bfd21 w F3 1 +bfd26 b F2 C17 2 +bfd37 w Q18 1 +bfd41 b E2 1 +bfd56 b C17 1 +bfd61 w P2 1 +bfd67 b D18 1 +bfd71 w R3 M4 2 +bfd91 b E16 D15 2 +bfd91 w C15 B15 D16 3 +bfda6 w C16 1 +bfdb7 b C4 D2 2 +bfdc0 w E14 1 +bfdd1 w R15 1 +bfe00 b R15 1 +bfe16 w P14 1 +bfe31 w H3 1 +bfe57 w Q2 1 +bfe66 b G6 1 +bfe91 b H3 1 +bfee1 w S2 1 +bff66 b Q3 1 +bff66 w O16 R16 2 +bffd1 w P8 1 +bffe0 w R3 1 +bfff7 b N17 1 +c0001 b O15 1 +c0017 w C6 D4 2 +c0020 b O5 1 +c0020 w B4 1 +c0040 b E14 1 +c0047 w B17 1 +c0066 b E15 1 +c0096 b R15 1 +c0116 b L16 1 +c0137 b Q3 1 +c0137 w R3 Q4 Q5 P6 Q6 Q7 6 +c0141 w Q17 1 +c0146 w Q13 1 +c0147 w P18 1 +c0170 b P15 1 +c0181 w Q14 1 +c0187 b R18 1 +c01a7 w R14 1 +c0257 b S16 1 +c0257 w R17 1 +c0261 w B16 1 +c0281 w N16 1 +c0290 b F4 C3 2 +c02b1 w R14 1 +c02b7 w D7 1 +c02d7 b S18 1 +c02e6 w B17 1 +c0340 w R4 1 +c0347 w Q6 1 +c0356 w G6 1 +c0360 w P18 1 +c0381 b C15 1 +c0390 b P14 1 +c0396 w C3 1 +c03a0 b N15 1 +c03b1 w R18 1 +c03c0 b C14 1 +c03d1 b D8 1 +c0401 b B8 1 +c0410 b O15 1 +c0420 w E12 1 +c0426 w N16 1 +c0446 w Q3 1 +c0477 w F2 1 +c04a1 b R4 1 +c04a6 b D16 1 +c04a7 w S15 1 +c04f6 b R1 1 +c0526 w Q6 1 +c0531 b D2 1 +c0531 w S4 1 +c0537 b R14 1 +c0540 b E5 1 +c0550 b O4 O3 2 +c0566 b S6 1 +c05c0 w N15 1 +c05c6 b Q6 1 +c05f7 b S3 1 +c0610 b N6 1 +c0636 w F4 1 +c0670 w R16 1 +c06a0 w C2 1 +c06b0 w F17 1 +c06f1 w F5 1 +c06f7 w B3 1 +c0707 b D17 1 +c0727 b B18 1 +c0731 b B15 1 +c0760 b J17 1 +c07a7 b R3 Q3 R9 R8 S5 R4 6 +c07c0 b S2 1 +c07c0 w D15 1 +c07c1 b F14 1 +c07c7 b O2 1 +c07d7 b C14 1 +c0821 w D16 E17 C15 F16 D14 5 +c0860 b B5 C3 F4 F3 4 +c0867 b F2 1 +c0870 w R6 1 +c0877 w D8 1 +c0886 w C3 1 +c08c0 b B11 1 +c0926 w B5 1 +c0930 w Q14 1 +c0966 b C13 J17 2 +c0981 b G13 1 +c0a01 w D4 1 +c0a11 w F4 1 +c0a21 w S13 1 +c0a56 w P18 1 +c0aa0 b P5 1 +c0aa1 b E3 1 +c0ab7 w C17 1 +c0ae0 b Q15 L17 R14 R13 R12 Q13 Q14 7 +c0ae0 w Q15 Q14 N17 M17 L17 N16 M16 L16 8 +c0af1 w E6 F6 E3 F3 4 +c0b10 b D3 1 +c0b20 b D2 1 +c0b26 w B9 1 +c0b46 b E15 1 +c0b46 w C18 1 +c0b71 w M5 1 +c0b76 b H5 1 +c0bb0 w Q17 R16 2 +c0be1 b D8 1 +c0bf1 w O2 1 +c0bf7 b R4 1 +c0c27 b Q7 S6 2 +c0c30 w D5 1 +c0c41 b D13 1 +c0c47 b H14 1 +c0ca0 b Q8 1 +c0cb1 w M17 1 +c0cc0 b C13 1 +c0cc0 w S1 1 +c0d10 b D15 1 +c0d61 w S6 1 +c0dd0 b Q2 1 +c0de0 b O9 1 +c0de6 b O6 1 +c0e26 w O3 1 +c0e56 w R12 1 +c0e57 b D5 G3 2 +c0e66 b R14 1 +c0e81 b O12 1 +c0e96 b M16 1 +c0ed1 w R2 1 +c0ed7 b L4 1 +c0ee7 w P3 1 +c0ef0 w P2 1 +c0f11 b O6 N7 2 +c0f47 w R17 1 +c0f60 b C8 1 +c0f66 b S18 1 +c0f80 w C4 Q15 2 +c0f90 b R13 O18 2 +c0fc6 b C7 J3 2 +c0fd1 w M3 1 +c0ff1 w E15 1 +c1001 w D11 1 +c1007 w Q16 1 +c1011 w E11 D17 2 +c1046 w P4 1 +c1056 b R12 1 +c1060 b F15 1 +c1066 w F5 1 +c1081 b E6 1 +c1086 w R3 1 +c1087 b S1 1 +c10c0 b L2 1 +c10c7 b P2 1 +c1111 b C17 1 +c1147 b F17 F18 2 +c1170 w G14 1 +c1176 b B14 1 +c11a1 b E5 1 +c11a1 w C7 1 +c11b7 w R12 1 +c11f0 b E8 1 +c11f6 b E18 1 +c11f7 w O14 N13 2 +c1217 w R4 1 +c1227 w P6 1 +c1240 w R5 1 +c1246 w R18 1 +c1261 w G5 1 +c1276 w C4 1 +c1296 w F16 1 +c12d6 w G4 1 +c12e1 b S17 1 +c1306 w D15 1 +c1340 b O7 1 +c1350 b F8 1 +c1350 w S5 R4 G5 3 +c1377 b C13 1 +c1407 b R8 1 +c1411 b B4 1 +c1457 b D16 1 +c1476 b G4 1 +c1481 b B16 1 +c1496 w N17 P17 2 +c14c1 b B16 C17 C16 H17 4 +c1527 w Q16 1 +c1537 w R17 N16 2 +c1561 b C14 1 +c1587 w D2 1 +c15b0 w Q17 1 +c15f0 b Q18 1 +c1621 w F16 1 +c1657 b C3 C4 J3 H3 E2 D3 6 +c1676 w R17 1 +c16e0 w E3 1 +c16f0 w R18 1 +c1727 b N16 1 +c1731 b Q18 1 +c1740 b P2 1 +c1771 w Q2 1 +c1781 w Q4 1 +c1787 w G2 1 +c17a7 b C6 E4 2 +c17c0 w C8 1 +c17d1 w R12 1 +c17f1 w O2 1 +c1837 b D9 1 +c1866 w D7 1 +c18f0 b P17 1 +c1946 b Q5 1 +c1950 b O15 1 +c1971 b D16 D2 2 +c19e0 b E5 1 +c1a00 b D14 1 +c1a40 w Q14 1 +c1a46 w S16 1 +c1a50 b H6 1 +c1a56 b R5 1 +c1a61 b D6 1 +c1a86 b R15 1 +c1a96 b D16 1 +c1ac6 w O16 1 +c1ad0 b Q13 1 +c1af6 w Q15 1 +c1b00 b F17 1 +c1b20 b M5 1 +c1b46 b D15 1 +c1b46 w B18 1 +c1b51 w C6 D5 2 +c1b71 w J15 1 +c1b76 w N18 1 +c1ba6 b P7 1 +c1c00 b Q14 1 +c1c01 b E17 1 +c1c06 b Q5 1 +c1c17 b Q2 1 +c1c31 w S4 1 +c1c50 w M3 1 +c1c86 w R6 1 +c1c97 b B4 1 +c1cd1 b Q3 1 +c1ce0 w P15 1 +c1cf1 w N2 1 +c1cf6 w N4 1 +c1d01 w N3 1 +c1d40 b S5 1 +c1d41 w S8 C17 C16 F17 4 +c1d60 b G4 1 +c1d61 b C5 1 +c1d81 b S15 1 +c1d96 b P16 1 +c1d97 b P17 1 +c1db7 w P13 1 +c1df7 b P6 R3 2 +c1e31 w Q5 1 +c1e36 b C7 1 +c1e56 w O18 1 +c1e70 w F7 1 +c1e76 b Q6 1 +c1e86 b S2 1 +c1e97 w N4 1 +c1ef1 b E15 1 +c1f00 w D6 1 +c1f16 w P13 1 +c1f40 b G15 1 +c1f56 b G3 1 +c1fa1 w B6 1 +c1fa6 w O6 1 +c1fe1 w N18 1 +c2070 b E3 1 +c2070 w R15 1 +c2081 w D9 1 +c2087 b R14 1 +c20a7 b S16 1 +c20c6 b F2 1 +c2101 b R2 1 +c2140 w S3 1 +c2186 b O7 1 +c21a6 w Q2 1 +c21d1 w L5 R4 2 +c2230 w L3 1 +c2266 w N4 1 +c2271 w D7 1 +c2297 b C3 1 +c22c7 b M6 1 +c22d0 w D17 1 +c22d6 w O5 D3 2 +c2301 b M6 1 +c2307 w G16 1 +c2327 b B17 1 +c2336 w D12 1 +c2377 b P15 1 +c2381 b O16 1 +c23a0 b P19 1 +c23b1 w R14 1 +c23f6 b E16 1 +c2416 w D5 1 +c2456 b P18 1 +c2460 w C8 1 +c2490 b C13 1 +c2496 b E13 1 +c24c1 b P14 1 +c2541 b C15 1 +c2547 w Q13 S17 2 +c25a6 b N3 1 +c25c1 b P17 O18 P18 3 +c25e0 w M5 1 +c25e7 w Q17 1 +c2617 b O2 1 +c2660 b P16 1 +c2671 b R15 1 +c2680 b O15 1 +c26c1 b Q14 1 +c26d7 b P16 1 +c2711 w P2 1 +c2746 w C7 1 +c2770 w E2 1 +c2787 w S13 1 +c27a7 b N17 1 +c27b6 w C17 1 +c27d0 w T6 1 +c27f0 w F5 1 +c27f7 w D6 1 +c2810 b P16 1 +c2826 w R6 1 +c2841 b Q18 1 +c2847 w D2 1 +c2851 w C5 1 +c2857 b D15 C16 2 +c2857 w E16 C17 G17 D14 D12 5 +c2861 b S15 1 +c28c7 w B16 1 +c28d7 w C9 C7 2 +c2957 w D3 1 +c29a0 b A17 1 +c29a7 b D3 1 +c29c0 b R13 1 +c29d1 b Q2 1 +c2a00 b M6 1 +c2a01 w R18 1 +c2a06 b R16 P16 2 +c2a10 b P12 E16 2 +c2a16 w N4 1 +c2ab6 w C9 1 +c2af7 b L18 1 +c2b46 b R4 1 +c2b56 w M17 1 +c2b57 w P13 1 +c2b76 b D14 1 +c2b76 w D18 1 +c2b81 w M17 1 +c2b97 w H2 1 +c2be6 b R16 1 +c2bf0 w R11 1 +c2c00 b H4 1 +c2c00 w S5 1 +c2c27 b D17 D3 2 +c2c61 b H3 1 +c2c70 w H7 1 +c2c97 b S17 1 +c2ca0 b C15 1 +c2ca1 w F15 1 +c2cc0 b O13 1 +c2cc6 b E7 F7 2 +c2cf1 w O16 1 +c2d01 w S13 1 +c2d36 b D5 1 +c2d36 w S4 1 +c2d57 w A16 1 +c2d71 w C8 1 +c2d80 w E15 1 +c2dc1 w D16 1 +c2de6 b C13 1 +c2e21 b P16 1 +c2e26 w F16 1 +c2e30 w Q13 1 +c2e37 w F17 1 +c2e50 w F16 Q13 2 +c2e76 w Q7 D12 2 +c2e86 w J2 1 +c2ea6 w B2 1 +c2eb1 w T2 1 +c2ed6 b F5 1 +c2f06 w O4 1 +c2f16 w R6 1 +c2f50 w R6 1 +c2f56 w G2 1 +c2ff1 b P17 1 +c3036 w Q3 1 +c3066 w N14 1 +c3067 w O4 1 +c3081 b A19 1 +c30e0 b M17 1 +c30f6 w P6 1 +c3106 b R6 1 +c3140 b Q16 1 +c3141 w F17 1 +c3180 b M16 1 +c3180 w R11 1 +c3187 w R17 R18 2 +c3190 w D15 1 +c3197 w G16 1 +c31d7 b N16 1 +c3240 b Q17 1 +c3256 b B7 1 +c3256 w F17 G16 2 +c3260 w O17 1 +c3261 b O8 1 +c3286 w G16 G17 C17 3 +c32d1 w N16 Q16 2 +c32d7 b P17 O17 P16 Q15 4 +c32d7 w Q16 1 +c32e0 b G4 1 +c32e7 w F4 1 +c3320 w Q15 1 +c3337 w P3 1 +c3361 b C8 1 +c3377 b D5 1 +c3397 b Q7 1 +c33a7 b C18 1 +c3420 w S18 1 +c3467 w G5 1 +c34a1 b H16 1 +c34c6 w E15 1 +c34d7 w Q4 1 +c3511 w C14 1 +c3536 b S3 1 +c3536 w Q4 1 +c3550 b P2 1 +c3556 w Q14 1 +c3576 w P16 1 +c35c0 w E15 1 +c35c1 w D7 1 +c35c7 b C12 1 +c35e1 w F15 1 +c35f0 w E16 1 +c3606 w P14 1 +c3617 b D7 1 +c3650 w E5 R18 2 +c3660 w D3 1 +c3676 b F17 1 +c3710 w F18 1 +c3730 w C18 1 +c3747 b Q15 1 +c3777 w R3 1 +c3786 w B13 1 +c3791 b M3 1 +c37e6 b Q7 1 +c3826 b E3 1 +c3886 w F5 1 +c38b6 b D14 1 +c38e0 w R14 1 +c38e6 b R14 1 +c38f0 w N4 1 +c3911 w R17 R16 O17 3 +c3950 b H3 1 +c3976 b D12 1 +c3996 w F4 1 +c39d0 b N6 1 +c39e1 b R3 Q4 P4 O5 O4 N4 6 +c39e1 w R4 1 +c39e6 b H17 1 +c3a00 b R9 1 +c3a11 b C4 1 +c3a11 w M3 1 +c3a27 b H17 1 +c3a27 w P3 1 +c3a41 w Q7 1 +c3a60 w Q5 1 +c3a71 w P17 1 +c3ab7 w D4 1 +c3ac0 b D3 1 +c3ac6 b C3 1 +c3ad0 b C9 1 +c3ad0 w Q6 1 +c3ae6 w P7 1 +c3af7 b C15 1 +c3b20 w C7 1 +c3b26 w B14 1 +c3b41 b F13 1 +c3b56 b D13 1 +c3b77 b M4 1 +c3b96 b Q14 1 +c3bd7 b M16 1 +c3be0 b E16 1 +c3be6 b O16 1 +c3c17 w S6 1 +c3c20 w N13 1 +c3c26 w G18 1 +c3c37 w Q6 R2 2 +c3c56 b R6 1 +c3ca1 b C17 1 +c3ca1 w H3 1 +c3cb0 b R11 1 +c3cd6 w E17 1 +c3d31 b P14 1 +c3da7 w S17 1 +c3dd7 b R3 1 +c3df6 w E16 1 +c3e37 b O18 1 +c3e46 b Q13 1 +c3e66 b S5 1 +c3e67 b D18 1 +c3e91 w R4 1 +c3eb1 b P4 O4 Q5 P3 4 +c3eb1 w B13 R3 2 +c3ec6 w B5 1 +c3ed6 b S14 E14 2 +c3ef0 w Q11 1 +c3f00 w P6 1 +c3f17 w R18 1 +c3f51 b Q12 1 +c3f56 w O4 1 +c3f90 w E3 1 +c3fa1 w C3 1 +c4006 b R12 1 +c4021 b R17 1 +c4030 w B15 1 +c4051 b P18 1 +c4070 w E3 S13 2 +c4086 b D13 C13 C17 3 +c4096 b Q13 1 +c4097 w Q2 1 +c40a6 b F18 1 +c40d6 b P3 S3 2 +c4100 w Q12 1 +c4106 b O4 1 +c4111 b S18 1 +c4137 b E4 E5 D7 C3 4 +c4137 w D5 1 +c4166 w E4 1 +c41d0 b R17 1 +c41e0 b O13 1 +c41e7 w T1 1 +c41f7 b P15 1 +c4207 b N3 1 +c4231 b G2 1 +c4237 b Q3 1 +c4266 w B3 1 +c4280 w E13 1 +c4351 b E4 1 +c4357 w M16 1 +c43d1 b C3 1 +c43e7 b P14 O16 2 +c4427 b T14 1 +c4451 w R18 1 +c4456 b Q6 1 +c4457 w C13 B12 2 +c4460 w C18 1 +c44a0 w Q3 1 +c44b1 w J16 1 +c44c0 b P16 1 +c44e0 b E3 1 +c44e6 b F4 1 +c44e7 w G17 1 +c4501 w G17 1 +c4510 b S14 1 +c4516 b P14 1 +c4517 w C7 1 +c4520 b O15 1 +c4587 w D2 1 +c45a0 b R14 1 +c45a1 b D6 1 +c45a6 w Q14 1 +c45b7 w C5 1 +c45c7 w D7 1 +c45e0 b C8 1 +c4601 w P11 Q17 2 +c4621 b H17 1 +c4631 w C6 1 +c4661 b N5 1 +c4681 b Q3 1 +c46e1 w O3 1 +c4726 b E3 1 +c4727 b N18 1 +c4760 b C6 1 +c4786 b D4 1 +c47f7 b H3 1 +c4807 b E9 1 +c4831 w J3 1 +c4841 b R5 1 +c4846 b F16 1 +c4861 w P8 1 +c48a7 w Q9 1 +c48b0 w E19 1 +c48b1 b B14 1 +c48e6 w C2 1 +c4911 w M16 1 +c4917 b S16 1 +c4941 w R4 1 +c4956 b F18 1 +c4971 b O17 S15 2 +c4977 w P18 1 +c4981 w M7 1 +c49d1 w Q4 1 +c49e7 w P8 1 +c4a06 w B5 1 +c4a31 w F4 1 +c4a46 w S17 1 +c4a80 b R15 1 +c4a80 w E6 1 +c4ac0 b O16 1 +c4b00 b Q18 1 +c4b21 w L17 1 +c4b26 w G15 1 +c4b41 w P3 1 +c4b87 w Q13 1 +c4ba7 w Q17 1 +c4bb7 b S5 1 +c4bc1 w F2 1 +c4bd7 b H17 1 +c4be7 b R4 Q5 R3 3 +c4c10 w P3 1 +c4c87 b Q16 1 +c4c97 b R8 1 +c4cc0 b F12 1 +c4cc7 w S18 1 +c4cd0 w N5 1 +c4cf1 b B16 1 +c4cf6 b O4 1 +c4d10 b G4 D4 F5 3 +c4d20 b E16 1 +c4d26 b O15 1 +c4d36 w D14 1 +c4d60 b R16 1 +c4d66 w Q14 1 +c4d87 w F17 1 +c4da7 b C5 D4 2 +c4db7 w D14 1 +c4dd0 w R2 1 +c4dd1 w P5 1 +c4de6 w F16 1 +c4df7 w C4 1 +c4e01 b C16 1 +c4e06 w M16 1 +c4e31 w P15 1 +c4e40 b M16 S2 2 +c4e51 b B14 1 +c4e87 b D16 1 +c4e96 b C16 1 +c4e97 b E15 1 +c4ee1 w D16 1 +c4f01 w R3 1 +c4f10 b D5 1 +c4f11 w R12 1 +c4f21 b B18 1 +c4f30 w P14 1 +c4f31 w O3 1 +c4f47 w O16 1 +c4f56 w Q3 Q5 2 +c4f66 w E17 1 +c4f77 w G3 1 +c4f87 b R13 1 +c4fa6 b P4 1 +c4fa6 w P4 1 +c4ff6 w F3 1 +c5000 b E18 1 +c5046 w O16 1 +c5067 b G16 1 +c5071 w D18 1 +c5077 b O3 1 +c5086 b C16 1 +c50c0 w Q16 1 +c5146 b S5 1 +c51a7 b C2 1 +c51d0 w L17 1 +c5216 w S6 1 +c5230 b Q14 1 +c5277 b R7 1 +c5281 w R2 1 +c5290 w G12 H14 2 +c52d0 b C6 1 +c52f1 b P18 1 +c5331 b R14 1 +c5346 b S17 1 +c5346 w R14 Q16 2 +c5350 b G16 1 +c5356 b S14 1 +c5357 b D16 1 +c53a1 w F16 E15 2 +c53b6 w C2 1 +c53e6 b G18 1 +c5437 b O6 1 +c5441 b R7 1 +c5447 b N2 1 +c5476 b H4 1 +c5477 b F17 1 +c54a0 b R17 1 +c54d1 b E11 1 +c54d7 w A18 1 +c54e7 b D18 1 +c54f6 w C9 1 +c5510 b S2 1 +c5526 w F17 1 +c5530 b O16 1 +c5550 w P13 1 +c5577 b P15 R15 2 +c5627 b C3 1 +c5651 w Q16 1 +c5676 b P15 Q16 2 +c5696 b O14 1 +c56b0 b C13 1 +c56b7 b O17 1 +c56c7 b A17 1 +c56f6 w Q17 1 +c5701 b H15 1 +c5707 w R18 O18 2 +c5726 b H3 O2 2 +c5727 b L4 1 +c5736 w S6 1 +c5746 b H17 1 +c5777 b B18 1 +c57a1 w R4 1 +c57b6 w N18 1 +c5801 w C16 1 +c5807 b G14 1 +c58c0 w C14 1 +c58d6 b H16 1 +c5916 w C8 1 +c5931 b O16 1 +c5947 b D5 1 +c5956 w D14 D13 2 +c5997 w C4 1 +c59c0 b P17 1 +c59d1 w R13 1 +c59d6 b O4 1 +c5a06 w R13 1 +c5a10 b D4 1 +c5a66 b E18 1 +c5a70 w B3 1 +c5aa0 b D7 1 +c5ab0 w F3 1 +c5ac1 b O15 1 +c5ad7 b F18 1 +c5b10 w R18 1 +c5b20 w D8 1 +c5b31 b H17 1 +c5b67 b Q3 1 +c5bc6 w O16 1 +c5bf0 b G5 1 +c5bf1 w J2 1 +c5bf7 w P13 1 +c5c06 w E18 1 +c5c26 b R5 1 +c5c31 b P3 1 +c5c40 b H4 1 +c5c40 w P16 1 +c5c76 w H2 1 +c5c77 b S17 1 +c5c97 w C2 1 +c5cd1 b S13 1 +c5cd6 w E5 1 +c5cd7 w O18 1 +c5d16 b S6 1 +c5d17 b H17 1 +c5d21 w E4 1 +c5d51 w E18 1 +c5db7 b F8 1 +c5dd1 b C2 1 +c5df0 b P16 1 +c5e00 w E7 1 +c5e37 b C17 1 +c5e47 b F8 1 +c5ea0 b Q18 1 +c5ed6 w P15 1 +c5f20 w Q15 1 +c5f36 w N16 1 +c5f56 w Q2 1 +c5fa0 w E6 1 +c5fb6 w D6 1 +c5fc7 w P5 1 +c5fd7 b F18 1 +c6080 b B17 1 +c6086 w R15 1 +c6090 b C8 1 +c60a6 b Q15 1 +c6130 w C18 1 +c6131 b Q5 Q6 P4 R5 4 +c6146 b S4 1 +c6151 w R4 1 +c6166 w P7 1 +c6167 b B5 1 +c6176 b F2 1 +c61b0 b D14 1 +c61b7 b G4 1 +c61c0 b S3 1 +c61c6 b H2 1 +c61d1 w D2 1 +c6226 w D18 1 +c6266 b Q3 1 +c62b0 b D16 1 +c62d6 b O3 P4 2 +c6301 b O16 1 +c6317 b F16 1 +c6320 b M13 1 +c6340 w S15 1 +c6346 w S16 1 +c6356 b Q15 1 +c6377 b Q3 1 +c6390 w G18 G15 2 +c6396 b R16 Q15 Q11 3 +c63a1 w G17 1 +c63a6 w C5 1 +c63b1 b M14 1 +c63c7 b P18 1 +c63c7 w O15 1 +c63e7 b D14 1 +c63f6 w R16 1 +c6416 b D16 1 +c6416 w C17 B13 2 +c6466 b G16 1 +c6480 w B4 1 +c6486 b B15 1 +c6491 b C2 C9 2 +c64a6 w B8 1 +c64c7 w Q13 1 +c64d0 w C13 1 +c6511 w R3 1 +c6521 w B17 1 +c6540 w C17 D13 2 +c6546 b T3 1 +c6546 w D7 1 +c6551 w O5 1 +c6560 b E17 1 +c6581 b R5 O13 2 +c65b0 b B15 1 +c65c6 w C4 C14 2 +c65f1 w S3 1 +c6646 w D15 1 +c6671 b B6 1 +c6686 w Q17 1 +c6690 w G7 1 +c6696 w F16 1 +c66d0 w R15 1 +c6707 w N17 1 +c6751 b H4 1 +c6777 b P2 1 +c67b7 w D2 1 +c67c0 w E18 1 +c67f7 b E2 1 +c6827 w Q4 1 +c68a6 w O9 1 +c68c0 w Q13 1 +c68c7 w S15 1 +c68e7 b Q4 1 +c68f1 w Q7 1 +c68f7 w R15 1 +c6900 w Q2 1 +c6956 w P5 1 +c6960 b J3 1 +c69d1 w O15 1 +c6a07 w Q1 1 +c6a16 b C15 E14 2 +c6a20 b F18 1 +c6a21 w F18 1 +c6a86 w Q14 1 +c6ab1 w M6 1 +c6ad0 w G4 1 +c6ad6 w R3 1 +c6b01 b H17 1 +c6b17 b C16 1 +c6b21 w D12 1 +c6b36 w Q8 1 +c6b76 b B7 1 +c6b87 w D17 1 +c6bf6 b R17 1 +c6c01 b B17 1 +c6c06 b P15 1 +c6c06 w R18 1 +c6c16 w D2 1 +c6c66 w Q3 1 +c6c67 w C2 1 +c6c91 w D13 1 +c6ca6 w P17 1 +c6cf7 w T16 1 +c6d37 w N3 1 +c6d57 b S18 1 +c6d66 b E13 1 +c6d76 w O18 1 +c6e60 b S13 1 +c6e80 b F14 1 +c6e80 w C8 1 +c6e97 b C6 1 +c6e97 w O8 1 +c6eb1 w R5 Q4 2 +c6ec1 w S16 1 +c6ef1 b E7 1 +c6f17 b C18 1 +c6f37 w Q4 1 +c7017 b G4 1 +c7020 w Q13 P16 2 +c7051 w R6 1 +c7070 b Q9 1 +c7076 w N5 1 +c7097 w N7 1 +c70b1 b N3 1 +c70d7 b R18 1 +c7110 w P5 1 +c7117 b D7 F5 2 +c7141 b E2 1 +c7157 w C18 1 +c7160 w C8 1 +c7161 b R4 1 +c7170 b O3 1 +c71a0 w J16 1 +c71d1 w N4 1 +c71e0 b E5 1 +c71e6 b E6 1 +c7201 b P16 1 +c7210 w O7 1 +c7237 w Q13 Q18 2 +c7246 w P5 Q4 2 +c7260 b R15 1 +c7267 w C14 1 +c7287 w P2 1 +c7290 w E8 1 +c72c0 w B6 1 +c72f7 w D17 1 +c7370 b E3 1 +c7371 b F15 1 +c73b1 b G3 1 +c73b7 w D6 1 +c73c7 b H17 1 +c7440 b B5 1 +c7451 b O16 1 +c7480 w F14 1 +c74a6 w R3 1 +c74a7 b L4 1 +c7506 b M2 1 +c7517 b S15 1 +c7571 b Q13 1 +c7577 w E15 1 +c75a7 w F2 1 +c75d1 w F18 1 +c75d6 w D15 F16 2 +c75e0 b O16 1 +c75e6 w S3 1 +c75f6 b D2 1 +c7656 w P4 1 +c7666 b B17 1 +c7696 w D7 1 +c7697 w S16 R17 2 +c76b7 w D5 1 +c7716 b Q17 1 +c7716 w E17 B17 2 +c7741 w S6 1 +c7747 w R4 1 +c7771 w P2 1 +c7787 w G3 C5 2 +c77a0 b H3 1 +c77d6 w F4 1 +c77e6 b D14 1 +c7816 w F16 1 +c7820 w E15 1 +c7826 b H17 1 +c78d6 w F3 1 +c7937 b P19 1 +c79a0 b F17 1 +c79a7 w O16 1 +c79c1 w E3 1 +c79c7 w O12 1 +c7a26 w S12 1 +c7a30 w G16 1 +c7a36 w E18 1 +c7a41 b C17 1 +c7a46 b E18 1 +c7ae6 w L2 1 +c7b17 w D17 1 +c7b20 w C16 1 +c7b61 b D4 1 +c7bb7 w M4 1 +c7bc7 b O5 1 +c7be0 b D17 1 +c7c06 w O16 1 +c7c46 b C18 1 +c7c57 b S17 1 +c7cc1 b E6 D6 2 +c7cf0 b C14 1 +c7cf6 w G3 1 +c7cf7 w L16 1 +c7d26 w D12 C14 2 +c7d77 b R15 1 +c7d80 b O17 1 +c7d80 w F15 O16 O17 N17 Q14 R12 Q12 Q11 8 +c7db0 w P2 1 +c7dd0 b E16 1 +c7dd0 w M16 1 +c7df0 b E16 1 +c7e00 b P6 1 +c7e16 b A8 1 +c7e16 w F5 1 +c7e31 w Q4 1 +c7e36 b M17 1 +c7e40 w F18 1 +c7e46 w O3 O4 2 +c7e76 w R18 1 +c7e87 w C15 1 +c7ed1 w C4 1 +c7ed7 w E2 1 +c7ef7 b Q15 1 +c7f40 b Q4 1 +c7f86 w O15 R14 2 +c7f90 w P15 1 +c7fa0 b O4 1 +c7fd6 w R16 1 +c7fe1 w M16 1 +c7ff7 w B17 1 +c8007 b S6 1 +c8010 b M16 1 +c8026 b P2 1 +c8030 w C5 1 +c8090 b P4 1 +c80a0 w P4 1 +c80d0 b E13 1 +c8100 w C3 H3 2 +c8101 b D4 1 +c8117 b E18 1 +c8137 b S3 1 +c8150 b S3 1 +c8150 w P6 1 +c8170 b D5 1 +c8180 b H16 1 +c8190 w R3 1 +c8197 b O18 1 +c81b6 b E6 1 +c81c0 b P7 1 +c81e1 w D7 1 +c8200 b Q18 1 +c8211 b R4 1 +c8211 w S15 O5 O6 R5 R6 5 +c8226 b R6 Q4 2 +c8231 b S5 1 +c8291 b F3 1 +c82b1 w Q2 1 +c82d0 w N3 1 +c8301 w R12 1 +c8307 b P6 1 +c8310 b H17 1 +c8330 b P18 1 +c8361 w F17 E16 2 +c8376 w J18 1 +c83a7 w E17 1 +c83b6 b B18 1 +c83b7 b O4 1 +c83e7 b R14 1 +c8447 b Q15 1 +c8456 w P15 P16 2 +c84a0 w P13 1 +c84f1 b E13 1 +c84f7 w C17 1 +c8501 b N6 1 +c8541 b Q6 1 +c8560 b D13 1 +c8567 b R14 1 +c8567 w P16 1 +c8581 b N15 1 +c8590 b Q17 1 +c85d0 b O4 R5 P6 R8 4 +c85f6 b D2 Q4 2 +c85f7 w F5 1 +c8631 b S4 1 +c8687 w P2 1 +c8696 w E18 1 +c8697 b R12 1 +c86e0 b D17 1 +c8757 w B17 1 +c8776 b O6 1 +c87a6 w J2 1 +c87d1 w R14 1 +c87d7 b C2 1 +c87f0 b L18 1 +c8811 w H14 1 +c8871 w E6 1 +c8880 b B4 1 +c8881 w S4 1 +c88a0 w G4 1 +c88a6 w C4 1 +c88e1 w S3 1 +c8907 w Q17 1 +c8977 w O16 1 +c8997 w Q15 1 +c89c1 b C17 1 +c89c6 w C8 1 +c89e0 w O16 1 +c8a10 b S15 1 +c8a21 w L16 1 +c8a56 w D6 1 +c8a76 w S5 1 +c8ae0 b D18 1 +c8ae1 w B4 1 +c8b10 b B17 B16 2 +c8b46 b R17 1 +c8bb0 b H17 1 +c8bc7 b C12 1 +c8c16 w G6 M18 2 +c8c60 w R5 1 +c8c71 w F5 1 +c8c97 w C15 1 +c8cb0 b C17 1 +c8ce0 b D3 1 +c8cf7 b B17 1 +c8d27 b O18 1 +c8d30 b Q4 1 +c8d37 w R16 1 +c8d40 w R8 1 +c8d41 b E17 1 +c8d46 b R2 1 +c8d67 b M6 1 +c8d86 b B9 1 +c8da1 b R4 1 +c8db0 w E12 1 +c8de0 w O5 1 +c8e11 b B5 1 +c8e17 b L3 1 +c8e27 b D17 1 +c8e61 b S18 1 +c8e67 w H4 1 +c8e70 b F4 1 +c8e87 b R3 1 +c8e91 b F16 1 +c8ea7 w Q14 1 +c8eb0 w C5 1 +c8ee7 b C3 D4 E4 F5 F4 P2 6 +c8ee7 w C4 R5 O3 3 +c8f27 w R3 1 +c8f40 w B4 1 +c8f50 w P4 1 +c8f80 w H5 1 +c8f97 w D2 1 +c8fd7 w O16 1 +c8fe7 w S6 1 +c8ff0 w C6 1 +c9007 b P6 1 +c90c1 w O4 1 +c90d1 b C4 1 +c9121 b C1 1 +c9126 b N16 1 +c9137 w Q6 1 +c9140 w C14 1 +c9147 w P14 1 +c9151 w C17 D12 2 +c9186 b E16 1 +c9187 w R3 C16 2 +c9190 w D5 1 +c91c7 b H6 1 +c91e1 b P13 1 +c9236 w S17 1 +c9240 w B14 1 +c92a1 w D13 1 +c92b0 w B6 1 +c92d0 w E16 1 +c92d1 b Q18 1 +c92f6 w D4 1 +c9301 b L16 1 +c9351 w O16 S15 2 +c9357 w C3 1 +c9387 b F12 1 +c9397 w R2 1 +c93d0 w Q2 Q4 2 +c93f6 b D4 1 +c9407 b Q18 1 +c9440 b R4 1 +c9441 b R17 1 +c9496 b D13 1 +c9497 w D9 1 +c94d1 b P13 1 +c94e1 b D16 1 +c94f6 w R18 1 +c94f7 w Q4 1 +c9507 b J17 1 +c9511 w N14 1 +c9516 b C13 1 +c9546 b R18 1 +c9567 w P14 1 +c95c6 b O14 1 +c95c6 w N4 1 +c95c7 w N18 1 +c95f7 w F4 1 +c9631 b C3 1 +c9657 b D6 1 +c9690 w D17 Q7 2 +c9696 b C14 1 +c96a0 w G16 1 +c96d1 w G17 1 +c96d7 b D14 1 +c9756 b D3 1 +c9761 w C7 1 +c9770 w B17 1 +c9791 w R11 1 +c97a0 b F3 1 +c97a1 w O5 1 +c97b6 w C12 1 +c97c7 b N5 1 +c97e1 b Q18 1 +c97e6 b R6 1 +c9826 b R2 1 +c9841 w R13 1 +c9867 w C3 1 +c9890 b C16 1 +c98a0 b S16 1 +c98a0 w M4 1 +c98b0 b D5 1 +c98e1 w F4 1 +c9917 w C16 1 +c9930 b N17 P18 2 +c99a1 w O3 1 +c9a01 b E18 1 +c9a21 b F3 1 +c9a61 b E5 1 +c9a80 b P16 1 +c9ab0 w R3 1 +c9ab1 w G17 1 +c9af7 w P13 1 +c9b20 b Q4 1 +c9b21 w L18 1 +c9b37 b G6 1 +c9b46 w P6 1 +c9b56 b B5 1 +c9b61 w C14 1 +c9b86 b E5 1 +c9ba6 w G17 1 +c9be1 b N1 1 +c9c30 w G17 1 +c9c87 w O12 1 +c9c90 w E15 1 +c9cb1 w N17 1 +c9cf1 w E17 1 +c9d11 b R14 1 +c9d27 b F19 1 +c9d37 w Q3 1 +c9d47 w O16 N16 2 +c9d97 b D4 1 +c9db1 w E7 1 +c9dc6 b C5 1 +c9dc7 w P3 1 +c9de0 w S3 1 +c9e26 b Q14 1 +c9e26 w O14 1 +c9e30 b D17 1 +c9e40 b C3 1 +c9e57 w T1 1 +c9e67 b S4 1 +c9e87 b D17 1 +c9ed1 b E17 1 +c9ed6 b L17 1 +c9f00 w Q12 1 +c9f66 w E5 D5 2 +c9f67 w M5 1 +c9fb6 b R4 P4 2 +c9fb7 w D17 C17 2 +c9fc6 w P2 1 +c9fd0 b O15 1 +c9fe6 b D18 1 +ca007 b Q18 1 +ca026 b E14 1 +ca037 b F14 1 +ca041 w G14 1 +ca071 b N18 1 +ca091 w D17 1 +ca0f0 w R1 1 +ca136 w P17 1 +ca137 b R15 1 +ca150 b R17 1 +ca167 b R12 1 +ca1a0 b F5 1 +ca1f0 w H17 1 +ca220 b H16 1 +ca261 b C17 1 +ca270 w O5 1 +ca276 b N17 1 +ca287 b F2 1 +ca330 b F4 1 +ca387 b S4 1 +ca456 w Q3 1 +ca476 w C5 1 +ca477 w Q2 1 +ca481 b E2 1 +ca4b1 w P6 1 +ca4c1 w F15 1 +ca4e0 b F4 1 +ca500 w S5 1 +ca507 w C5 1 +ca550 b E15 1 +ca566 b B8 1 +ca5a0 w Q16 1 +ca5c6 b C7 1 +ca5f7 b B14 1 +ca657 b M16 1 +ca657 w O13 1 +ca660 b F4 D5 2 +ca667 w R17 O15 R14 Q14 4 +ca681 w C6 1 +ca6b7 w O18 1 +ca6c1 w S4 1 +ca736 b E14 1 +ca780 b D8 1 +ca7b7 w R2 1 +ca7c1 w R9 1 +ca7f0 b G14 1 +ca7f6 b D18 1 +ca816 w E5 1 +ca8e6 w F4 1 +ca901 w O4 1 +ca950 b S15 1 +ca950 w Q17 1 +ca9a0 w B5 D5 2 +ca9b1 w J4 1 +ca9b7 b P12 1 +caa06 w R16 1 +caa30 b S3 1 +caa50 b D17 1 +caa86 b D8 1 +caa86 w F6 1 +caac0 b G16 1 +caae0 b R18 1 +caaf1 b F2 1 +cab01 b E18 1 +cab06 w E2 1 +cab37 b B16 1 +cab61 w R6 1 +cab96 b H15 1 +caba6 w P2 1 +cabb7 b O16 1 +cabc6 b E17 1 +cabe0 b L3 1 +cabf0 b O15 1 +cac10 b E17 1 +cac21 b T3 1 +cacc6 b D15 1 +cacf0 w C11 1 +cad26 w R3 1 +cad41 b B5 S16 2 +cad56 w F16 1 +cad66 b J2 1 +cad90 w E14 1 +cadb0 b E13 1 +cadf1 w S13 1 +cae01 w H3 1 +cae06 w P5 F16 2 +cae16 b R3 1 +cae31 w E18 1 +cae47 w E6 1 +cae51 b O5 1 +caeb1 w G3 1 +caec7 w B16 P2 2 +caed7 b G16 1 +caf26 b M16 1 +caf57 w E13 1 +caf77 w F2 1 +caf81 b Q4 1 +caf90 w Q3 Q4 N3 P5 R3 5 +caf96 b N17 1 +cb007 w E17 1 +cb0b0 w G6 1 +cb0e0 b N4 1 +cb0e7 b B12 Q15 2 +cb106 b E16 1 +cb150 b Q15 1 +cb1a1 w R2 1 +cb1b6 w F15 1 +cb1c7 w Q7 1 +cb1e1 w D13 F14 2 +cb217 b D18 B17 2 +cb237 w P17 1 +cb261 b G16 1 +cb287 b P15 1 +cb287 w G17 1 +cb2a1 b Q14 1 +cb2e1 b S15 1 +cb347 w B3 1 +cb376 w F14 1 +cb380 b F17 1 +cb390 w R7 P5 2 +cb397 b C17 1 +cb3c0 w E14 1 +cb3d7 b E14 1 +cb416 w S5 1 +cb460 b J14 1 +cb477 b E12 1 +cb4b0 w D5 1 +cb4e1 b M3 1 +cb517 b P17 1 +cb527 b S7 1 +cb531 w R15 1 +cb580 b R6 1 +cb587 w B17 1 +cb5a0 b B4 D4 2 +cb5c0 b N17 1 +cb5c1 b R13 1 +cb5e0 w P3 1 +cb606 b E13 1 +cb631 w N16 1 +cb6a0 w Q8 1 +cb737 w O9 1 +cb770 b G6 1 +cb7a6 w P16 1 +cb7d0 w P5 1 +cb831 w P5 1 +cb840 w F4 1 +cb850 b N16 1 +cb861 w D18 1 +cb870 b C12 1 +cb886 b S5 1 +cb8d7 w Q16 1 +cb900 b S4 1 +cb901 w G5 1 +cb911 b D13 C12 C11 3 +cb920 b C3 1 +cb957 b L3 1 +cb9a7 b Q2 S3 2 +cb9e1 b C17 1 +cba11 b C15 1 +cba30 b F6 1 +cba31 b E6 1 +cba56 w N5 1 +cba67 b P3 1 +cba87 w C17 1 +cbab7 w P7 1 +cbad0 w C17 1 +cbae0 b R6 1 +cbae1 b Q6 1 +cbaf0 b O14 1 +cbb57 w S4 1 +cbb70 w R15 1 +cbb77 w B2 1 +cbbe1 b S2 1 +cbc60 b G16 1 +cbc67 b Q2 1 +cbc96 b Q8 1 +cbca7 w C1 1 +cbcc0 b G6 1 +cbce0 w J16 1 +cbce6 w S5 1 +cbd10 b N8 1 +cbd30 w P8 C15 2 +cbd47 w D14 1 +cbd57 w O4 1 +cbd61 b B13 1 +cbda1 b P15 O15 R17 N17 4 +cbda6 b Q16 1 +cbdf0 b D14 1 +cbdf0 w R15 R18 2 +cbe26 b N18 1 +cbe40 w S15 Q15 2 +cbe80 b D6 1 +cbe81 b E17 1 +cbec1 w J16 1 +cbee0 w F17 1 +cbf36 w R13 1 +cbf60 w Q1 1 +cbf66 b H4 1 +cbf87 b P15 S18 2 +cbfc0 w P5 1 +cbfe0 b R8 1 +cbff1 b C2 1 +cc027 w B18 1 +cc0a0 w P14 1 +cc0b0 w F1 1 +cc106 w Q13 1 +cc110 b E3 1 +cc127 b E18 1 +cc127 w O17 1 +cc136 b Q18 1 +cc147 w P2 1 +cc150 b C8 1 +cc180 w E17 1 +cc190 b R8 1 +cc1c7 b S19 1 +cc1d6 b E4 1 +cc1e7 b L4 C3 2 +cc207 b S18 1 +cc217 w S15 1 +cc276 w F13 1 +cc281 w Q2 1 +cc2b6 w N3 P3 O2 3 +cc2b7 w G4 1 +cc2c6 w F18 N15 N14 3 +cc316 b D5 R15 2 +cc337 w M18 1 +cc381 w D15 1 +cc3a6 b J13 1 +cc3d6 b E17 1 +cc400 w Q7 1 +cc401 b P2 1 +cc426 b P17 S17 2 +cc431 b Q19 1 +cc481 w Q17 1 +cc490 w E4 1 +cc4a7 w H3 1 +cc4f1 w P18 1 +cc531 w G2 1 +cc556 w H3 1 +cc587 b H17 1 +cc587 w S4 1 +cc590 w T15 P16 2 +cc5b6 b Q14 1 +cc5d0 w B17 1 +cc5e6 b Q6 1 +cc606 w E7 1 +cc626 b Q7 1 +cc667 b B4 1 +cc667 w E6 1 +cc676 w N3 S5 2 +cc686 w R3 1 +cc696 w O15 1 +cc6a1 w D13 1 +cc6c0 w C6 1 +cc6c1 b P5 1 +cc6e6 b J2 1 +cc6e7 b O3 1 +cc706 w H3 1 +cc710 w O3 P6 2 +cc711 w F3 1 +cc717 w Q17 1 +cc751 w O16 1 +cc756 w D14 1 +cc781 w S15 1 +cc7a6 b C3 1 +cc7a7 b E3 D4 2 +cc7d7 b D16 D14 2 +cc7e6 b C14 1 +cc810 w O3 1 +cc816 w Q2 1 +cc817 w Q17 1 +cc826 b G18 1 +cc876 b F14 1 +cc8a6 w C4 B5 2 +cc8b1 w O17 1 +cc8e1 b N2 1 +cc906 w P16 1 +cc907 b O17 1 +cc911 w P17 Q16 2 +cc930 b C3 1 +cc951 w D3 1 +cc967 b F5 1 +cc990 b D4 1 +cc991 w G18 H15 2 +cc9a0 w E3 1 +cc9c1 w B3 1 +cc9c6 b Q17 1 +cc9d7 w F17 B15 2 +cca06 b E17 1 +cca17 w R14 1 +cca20 w M15 1 +cca37 b C15 1 +cca40 b B16 1 +cca60 w D7 1 +cca71 b P15 1 +cca80 w F4 1 +ccaa7 w B17 1 +ccad0 w M4 1 +ccaf0 b F4 1 +ccb17 b E16 1 +ccb60 w C14 1 +ccb81 w E17 1 +ccb86 w T16 1 +ccb91 w B5 1 +ccba1 b Q17 1 +ccba6 w R7 1 +ccba7 w E15 1 +ccbb0 w D2 1 +ccbd7 b E13 R17 2 +ccbe1 b H3 1 +ccc31 w R6 1 +ccc60 b E12 1 +ccc61 b S6 1 +ccc66 w Q14 Q13 2 +ccc86 w E7 1 +ccca1 w R14 1 +cccf6 b G18 1 +ccd00 b Q14 1 +ccd96 w C14 1 +ccda0 b D14 D13 2 +ccda6 w S8 1 +ccdc7 b P5 1 +cce27 b P2 1 +cce31 b L16 N3 2 +cce76 b E3 1 +cceb6 w H16 1 +ccec7 w B5 1 +ccf07 b F18 1 +ccf40 w P16 1 +ccf46 w F15 1 +ccf56 b R1 1 +ccf81 b F5 1 +ccfb7 w C4 1 +ccfe1 w Q16 1 +cd000 w S3 1 +cd001 b D17 1 +cd017 w F16 B17 2 +cd036 w N16 1 +cd087 b C12 1 +cd0f6 b R5 1 +cd101 b Q5 1 +cd110 b S2 1 +cd121 w S4 1 +cd131 w P16 1 +cd151 b F9 1 +cd157 w C12 1 +cd1c6 b Q14 1 +cd210 b R15 1 +cd256 b D6 1 +cd291 b S17 1 +cd2a1 w Q8 1 +cd2a6 b S3 1 +cd2f0 w L16 1 +cd2f1 b C18 1 +cd2f7 w S4 1 +cd316 w O6 1 +cd327 b Q4 1 +cd347 w P15 1 +cd351 w L17 1 +cd381 b E15 1 +cd387 w F6 1 +cd396 w S4 1 +cd397 b N4 1 +cd3b1 w D2 1 +cd3b6 w N2 1 +cd3b7 w C5 1 +cd3d6 b Q16 1 +cd3e0 w Q3 1 +cd416 b L2 1 +cd446 b P18 1 +cd4c6 b O14 1 +cd4e1 b B14 1 +cd4e7 b Q5 1 +cd4f0 b E16 G17 2 +cd510 b Q3 1 +cd521 b C19 1 +cd541 w O14 1 +cd561 b B14 1 +cd591 w D11 1 +cd5b0 b P18 1 +cd5b1 b D18 1 +cd5d1 w R5 1 +cd606 w B7 1 +cd620 w M17 1 +cd660 w E2 1 +cd666 w R7 P5 2 +cd671 b S17 1 +cd680 w M16 1 +cd6b1 w N2 1 +cd6c6 b F2 1 +cd6d7 b F4 1 +cd767 w F15 1 +cd771 w D15 G17 2 +cd777 b G17 1 +cd786 b C8 1 +cd796 w E2 1 +cd7b6 w G18 1 +cd7d6 w O17 1 +cd800 w R5 1 +cd811 w L2 1 +cd826 w D6 1 +cd827 w O6 1 +cd841 w Q16 1 +cd851 b R16 1 +cd8a6 b R15 1 +cd8a7 w G5 1 +cd8b1 w R3 1 +cd8c7 b N17 1 +cd8d6 w R14 1 +cd916 w G3 1 +cd926 b F4 1 +cd931 b O5 1 +cd980 b C5 1 +cd997 w B17 1 +cd9c0 b D6 D7 2 +cd9d0 w D16 1 +cda01 w P12 1 +cda46 w Q14 1 +cda50 w P6 1 +cda67 w B13 1 +cda91 b R17 1 +cdaf7 b B3 1 +cdaf7 w B4 1 +cdb27 b B3 1 +cdb36 b B16 1 +cdb50 b C3 1 +cdba0 b Q5 1 +cdba1 w H4 1 +cdbb0 b Q9 1 +cdbc6 w N2 1 +cdbe1 b G6 1 +cdc17 w R3 S5 Q7 3 +cdc21 w F3 1 +cdc30 b D12 1 +cdc31 w S14 1 +cdc76 w D16 1 +cdc90 b O17 1 +cdcb7 w O6 1 +cdcc1 w R18 1 +cdcc7 w R3 1 +cdcf1 b C2 1 +cdd11 b C16 1 +cdd11 w D3 1 +cdd21 b Q6 1 +cdd41 b C17 D14 C14 H16 F15 5 +cdd56 b D16 1 +cdd57 b O2 1 +cdd67 b D14 1 +cdd70 w O15 1 +cdd87 w B6 1 +cdd91 w O13 1 +cddd1 b C17 1 +cddd7 b T3 1 +cde66 b N12 1 +cde66 w E17 1 +cdec0 w Q14 1 +cdec7 b F15 1 +cdef1 b B4 1 +cdf11 b N3 1 +cdf26 b A4 1 +cdf80 w C17 1 +cdf81 w E16 1 +cdfd0 w F6 1 +cdfd1 w P3 1 +cdfd7 w N16 1 +cdfe7 b O5 O6 R5 R6 4 +cdff0 w F18 1 +cdff6 w Q2 1 +ce010 b N4 1 +ce050 w C18 1 +ce060 b G5 1 +ce061 b R3 1 +ce070 b D15 C13 2 +ce0d6 b F6 1 +ce0e6 w F3 1 +ce116 b F14 1 +ce120 w S15 1 +ce131 w S2 1 +ce137 w O15 N16 P17 3 +ce1a0 w Q13 1 +ce1a1 b E3 1 +ce1b1 b R17 1 +ce1b6 w P6 1 +ce1d1 w P2 1 +ce1d6 w H4 1 +ce1e0 w G17 C14 2 +ce1e7 w M17 Q18 2 +ce1f6 b E17 1 +ce221 b F4 1 +ce266 w F16 1 +ce276 w C7 1 +ce280 w H3 1 +ce297 b S6 1 +ce297 w C4 1 +ce2a1 b E14 1 +ce2d1 w G16 1 +ce326 b S7 1 +ce407 b Q12 1 +ce410 b D18 1 +ce427 w C7 O18 2 +ce447 b R18 1 +ce456 b B14 1 +ce486 w S12 1 +ce491 b H6 1 +ce4b7 w G18 1 +ce4c6 b N16 1 +ce4d6 w C2 1 +ce4d7 w R7 1 +ce4f1 b E2 1 +ce581 b H2 1 +ce5a0 b C5 1 +ce5b1 b C3 1 +ce5e1 w D4 1 +ce5e7 b R4 1 +ce5f0 b C7 E5 D7 3 +ce610 b D9 1 +ce627 w B17 1 +ce631 b D13 1 +ce650 w P15 1 +ce651 b D17 1 +ce6a7 b S16 1 +ce6b6 b C3 E3 F4 G4 G3 5 +ce6b6 w F4 F3 C5 E5 C7 D7 D8 7 +ce6d0 b G17 1 +ce6e6 b R17 1 +ce710 w N5 Q7 2 +ce711 w O17 1 +ce750 b S5 1 +ce757 b J16 1 +ce786 w S3 1 +ce7e7 w B17 1 +ce841 b C7 1 +ce846 w C5 1 +ce860 b C16 1 +ce867 w P16 1 +ce8f0 w P13 1 +ce910 b R5 1 +ce911 w C18 1 +ce917 w B18 1 +ce920 b B6 1 +ce930 b F13 1 +ce937 b C19 1 +ce947 w P2 1 +ce960 w D15 1 +ce997 w F6 1 +ce9c1 b R4 1 +ce9f7 w N18 1 +cea36 b O17 1 +cea37 w M17 1 +ceaa0 b F18 1 +cead1 b B14 1 +ceae7 w D5 1 +ceaf0 w R3 N3 2 +ceaf1 w C14 1 +ceb20 b F6 1 +ceb60 b Q15 1 +ceb70 b S17 1 +cec06 b G17 1 +cec07 b P4 1 +cec21 b S4 1 +cec36 b H16 1 +cec56 w D2 1 +ceca7 w R5 1 +cecb6 w E16 1 +cecd0 w S3 1 +cecd7 w R8 1 +ced36 w D17 1 +ced77 w O5 1 +cedb6 b D13 1 +cedc7 b N2 1 +cee01 w D2 1 +cee41 w N15 1 +cee61 w Q6 1 +cee67 w D2 1 +cee81 w N16 1 +ceea1 w F14 1 +ceea7 b S17 1 +ceed0 w D3 1 +ceef6 b D9 1 +ceef7 w R18 1 +cef00 b B15 1 +cef10 b H5 1 +cef20 b Q15 1 +cef30 w S15 1 +cef31 w E4 F4 2 +cef36 b E3 C3 2 +cef37 w C17 1 +cef81 w O2 1 +cf017 w S5 R16 2 +cf036 w Q7 1 +cf057 w R3 P6 O3 O4 4 +cf077 b G12 1 +cf086 w O13 1 +cf091 b D4 1 +cf096 b F5 1 +cf0b1 w S2 1 +cf0d0 b S5 R4 2 +cf0f7 b S5 1 +cf121 w R12 1 +cf127 b C12 1 +cf127 w Q12 1 +cf146 b Q6 D15 C14 3 +cf177 b G16 1 +cf1b6 w C7 1 +cf1c1 w S2 1 +cf1d0 w L6 1 +cf241 w S15 1 +cf260 b O5 1 +cf261 b B15 1 +cf286 b M15 1 +cf290 b C16 1 +cf2a0 w Q5 1 +cf2d1 w F17 1 +cf2f1 w S3 1 +cf310 b O2 1 +cf317 b G18 1 +cf320 w Q3 1 +cf326 w J17 1 +cf341 w D6 1 +cf380 w O11 1 +cf3f0 w C15 1 +cf450 b R17 1 +cf457 w F5 1 +cf467 w M17 1 +cf4c0 w M5 1 +cf4e7 b E17 1 +cf4f1 b C3 1 +cf526 b J3 1 +cf540 b P13 1 +cf541 w D4 1 +cf576 w S4 1 +cf5b0 w N17 1 +cf5f7 b P16 1 +cf611 w S17 1 +cf621 b R3 1 +cf646 b F16 C16 2 +cf661 b Q18 1 +cf670 b F16 1 +cf686 b R2 1 +cf696 w C5 1 +cf6a6 w C3 1 +cf6c0 b E4 1 +cf6d7 b C2 L16 2 +cf6e0 b J17 1 +cf747 b B16 1 +cf771 b R3 1 +cf781 b R3 P2 N4 3 +cf7a0 b G16 1 +cf7a7 b S17 1 +cf7b6 w D15 1 +cf800 b N3 1 +cf831 w N4 1 +cf860 b Q7 1 +cf871 w R17 1 +cf890 b P18 1 +cf8b1 w F17 1 +cf8d6 w F18 1 +cf8e1 b D16 1 +cf8e7 w D13 1 +cf8f6 w M4 1 +cf901 b P16 1 +cf901 w Q15 P15 N16 R17 4 +cf936 w Q3 1 +cf967 w G5 S17 2 +cf980 w E6 1 +cf9c0 b Q13 1 +cf9f7 w J4 1 +cfa17 w N15 1 +cfa31 w E3 1 +cfa41 w C4 D2 2 +cfa86 w O14 1 +cfa91 b O18 1 +cfaa0 w F3 1 +cfaa1 w J4 1 +cfae0 b E5 1 +cfb07 w C5 1 +cfb20 w S4 1 +cfb57 b C16 1 +cfb67 w D6 1 +cfb81 b B14 1 +cfbb6 b F17 E16 2 +cfc06 b E18 1 +cfc06 w E7 1 +cfc36 w F15 1 +cfc66 b B3 1 +cfc66 w C17 1 +cfca7 b D6 E2 2 +cfcd0 b E13 1 +cfcd0 w L14 Q18 2 +cfcf1 b E14 1 +cfd00 b S16 1 +cfde6 b O16 1 +cfe91 b N5 1 +cfed1 b H3 1 +cff57 b D7 1 +cff90 w C4 D3 D6 3 +cffa0 w S3 1 +cffc1 w B7 1 +cffd7 w S3 1 +cffe1 w R5 1 +cfff7 b B9 1 +d0010 b C14 1 +d0057 b S17 1 +d0066 b P18 1 +d0077 w P15 1 +d0087 b N16 1 +d00a0 w D6 L3 2 +d00e6 b D6 1 +d0150 w Q5 1 +d0166 b E5 1 +d0187 w T3 1 +d0216 w S1 1 +d0261 b P3 1 +d0267 w C3 1 +d0280 w B15 1 +d0290 b D16 1 +d02e7 b C13 1 +d02f1 b E5 1 +d0336 w Q13 1 +d0366 b N3 R9 2 +d0367 b G4 D4 2 +d03b1 w Q2 1 +d0460 w C2 1 +d04e1 w P15 Q6 P5 3 +d04f1 w H2 1 +d0517 w C16 1 +d0531 w D16 1 +d0537 b C1 1 +d0540 w E1 1 +d0600 w O3 1 +d0636 b Q16 1 +d0681 w C16 1 +d0687 b R3 1 +d0696 w R3 1 +d06b7 w S7 1 +d06d1 w H2 1 +d06e1 w C5 1 +d06e7 w E15 F15 C17 G17 4 +d0737 b R3 1 +d0771 w F5 1 +d0790 w R18 1 +d07a0 b F17 1 +d07a6 b S6 1 +d07d0 b E8 1 +d07d1 w G2 1 +d07e0 b D17 D16 2 +d07e7 w O16 C5 2 +d0840 b M5 1 +d0847 w Q3 1 +d0870 b C7 1 +d0881 w Q18 S17 2 +d0921 b C2 1 +d0940 b F19 1 +d0941 b R17 1 +d0957 b Q15 P16 2 +d0967 b H3 1 +d0977 w E2 1 +d09b7 b S17 P17 O16 N17 N18 5 +d0a10 b O4 1 +d0a21 b C18 1 +d0a66 b Q6 Q5 2 +d0a67 b E17 1 +d0a70 b E4 1 +d0a77 b F17 1 +d0a81 w N5 1 +d0a86 b B3 1 +d0aa7 w Q15 P16 2 +d0ab0 w N17 P15 2 +d0ac6 b G4 1 +d0ad6 w Q5 1 +d0ae0 w F16 F15 2 +d0b10 b Q12 1 +d0b30 b R15 O14 2 +d0b71 b E13 1 +d0b87 b B16 1 +d0b97 b C15 1 +d0ba0 w P4 1 +d0bb0 w P4 1 +d0bb6 b E5 1 +d0bd6 w S2 1 +d0c06 w S5 1 +d0c30 w D5 1 +d0c36 w F2 1 +d0c80 b E6 1 +d0c80 w D6 1 +d0c81 w S5 1 +d0ce6 w O2 1 +d0d00 w C2 1 +d0d01 w H4 1 +d0d10 w S4 R5 2 +d0d11 b D3 1 +d0d20 w R17 1 +d0d41 b Q4 1 +d0d66 w Q2 1 +d0d67 w S13 1 +d0d96 b H18 1 +d0db1 w B2 1 +d0e10 b E5 1 +d0e17 b P15 P17 2 +d0e20 b S4 1 +d0e27 w S17 1 +d0e37 b E14 1 +d0e40 b S15 1 +d0e70 w O15 1 +d0eb7 b O5 1 +d0ec7 w Q6 1 +d0f26 w N3 1 +d0f31 b B3 1 +d0f37 b O13 1 +d0f40 w E19 1 +d0f50 b P6 E6 2 +d1031 w Q3 1 +d1050 b E17 F14 2 +d1060 b G4 1 +d1061 w C16 1 +d1076 w T4 1 +d10a0 w O6 1 +d10b1 b C7 1 +d10b6 w S6 1 +d10e7 b D3 1 +d10f6 w B4 1 +d1120 b E14 1 +d1121 b O17 1 +d1187 b Q7 1 +d11b0 b Q12 1 +d11b7 b R2 R5 Q6 R7 S7 5 +d1221 b Q14 1 +d1250 b D16 1 +d1256 b B16 1 +d1256 w C16 1 +d1267 b T18 1 +d1287 b S4 1 +d12a1 w M3 P2 Q2 3 +d12d7 w H17 1 +d1330 w R6 1 +d1351 b B5 1 +d1351 w P18 1 +d1366 b C15 1 +d1376 w F5 1 +d1386 b C14 1 +d1390 b Q6 1 +d13a1 w M4 1 +d13d0 w F3 1 +d13d6 b P6 1 +d13e0 b G4 1 +d13e7 w A3 1 +d1427 b G3 1 +d1440 w R17 R12 2 +d1447 b C3 D6 D4 E4 F5 E5 E7 D7 F3 G3 G4 11 +d1447 w C4 1 +d1460 w D13 1 +d1487 w N17 1 +d1491 b M17 1 +d14a1 b R17 1 +d14e0 w R4 Q3 2 +d14f1 b P7 1 +d1521 w C3 1 +d1567 b D3 1 +d1587 b G4 1 +d1591 b N16 R15 2 +d15a1 b G18 1 +d15a6 b N15 1 +d15e7 w C7 1 +d1650 b O17 1 +d1677 b S19 1 +d16a0 b D14 1 +d16c1 b R16 1 +d16c1 w C8 1 +d1706 b Q15 1 +d1731 b N5 1 +d1761 b N7 1 +d1771 b S17 1 +d1777 b F15 1 +d1796 b R14 Q13 2 +d17d7 b S5 1 +d17f6 w D18 1 +d1807 w R3 1 +d1820 w P8 E12 2 +d1821 w G16 F14 2 +d1860 b G14 D17 2 +d1877 w D1 1 +d18a0 w P3 1 +d18a7 b S15 1 +d18d1 b B8 1 +d18e6 w D14 1 +d1906 b O16 1 +d1906 w N3 1 +d1931 w C5 1 +d1936 b B17 R3 2 +d1947 b C6 1 +d1977 b C12 1 +d1981 b E2 1 +d1986 b O5 1 +d19b7 b H4 1 +d1a11 w B16 B7 2 +d1a27 w H17 1 +d1a31 b O4 1 +d1a31 w D7 1 +d1a36 b B6 1 +d1a37 w Q15 1 +d1a40 b R4 1 +d1a86 w F3 E4 2 +d1a97 w O18 1 +d1aa0 w C18 1 +d1aa6 w C16 1 +d1ab0 b F7 1 +d1ad1 w E6 1 +d1ad6 b C15 1 +d1b01 b D13 1 +d1b06 w B2 1 +d1b20 w E2 1 +d1b46 w Q17 1 +d1b56 w C9 1 +d1b77 b P5 1 +d1b87 b B8 1 +d1bb0 w R17 1 +d1bc7 w O16 1 +d1bd0 w N15 1 +d1c07 b F2 1 +d1c21 w N3 1 +d1c47 w D9 1 +d1c91 b O4 1 +d1ca0 w Q16 1 +d1cb6 w G4 1 +d1cb7 w Q14 1 +d1cc7 b C15 1 +d1cd0 w G5 1 +d1d50 w P6 O4 2 +d1d76 b D5 1 +d1d87 w Q3 1 +d1dc1 b Q2 1 +d1df1 b D13 1 +d1e21 b D3 1 +d1e50 w F14 1 +d1e51 b C2 1 +d1e57 b C14 1 +d1ea7 b C15 1 +d1ec7 b E15 C14 2 +d1f16 b R8 1 +d1f27 b B7 1 +d1f27 w P5 1 +d1f40 w P14 1 +d1f46 b G17 1 +d1f51 b E4 1 +d1f60 w E12 1 +d1fd1 b R12 1 +d2007 w G16 1 +d2020 b P2 1 +d2031 b C7 1 +d2081 b N16 1 +d20a1 b D14 C18 2 +d20d6 w M16 1 +d2110 w R15 1 +d2157 w Q15 1 +d2221 b E18 1 +d2230 w N4 1 +d2276 b C5 1 +d2281 b Q14 R18 2 +d2291 w S5 1 +d22a7 b C16 1 +d22b1 w F17 1 +d22b6 w N4 1 +d22e6 b O15 1 +d2300 b S16 1 +d2361 b D17 1 +d2361 w C17 D16 D15 E14 D14 5 +d2380 b Q14 1 +d23a1 b R16 1 +d23d0 b Q15 1 +d23e1 b D5 1 +d23f1 b G18 1 +d23f7 b N16 Q17 2 +d2407 b E6 1 +d2407 w Q4 1 +d2421 w N17 1 +d2446 b M16 1 +d2467 w S4 1 +d2471 b B16 1 +d24c1 b O15 N16 P17 3 +d24c7 w P16 1 +d24e0 b J3 1 +d2506 b R7 1 +d2507 b Q4 1 +d2536 b P2 1 +d2546 b O3 1 +d2596 w M4 1 +d2601 w S5 1 +d2616 b F2 1 +d2626 b Q14 1 +d2666 b B2 1 +d2667 b S5 1 +d2680 w Q14 1 +d2696 b O17 1 +d26a0 b D17 1 +d26a1 b R17 1 +d26a6 b S18 1 +d26d7 w O5 R3 2 +d26f0 b O16 1 +d2721 b Q17 1 +d2726 w P2 1 +d2731 w E14 1 +d2737 w C18 1 +d2756 w H17 1 +d2766 b E4 1 +d27e0 b Q16 1 +d27e6 w A16 P3 2 +d27e7 w F7 1 +d2807 b Q11 1 +d2821 b F3 1 +d2830 b G5 1 +d2837 b G15 1 +d2857 w P17 1 +d2860 b P5 1 +d2876 b G15 1 +d2880 w M16 1 +d28a7 b S15 1 +d28b0 b R4 1 +d28c6 w D8 1 +d28d7 b O17 1 +d28e6 b S4 1 +d2941 b S11 1 +d2977 w F5 1 +d29e0 w M4 1 +d29e6 w R12 1 +d29f7 w B4 1 +d2a21 b E16 1 +d2a27 w O17 1 +d2a41 w C16 1 +d2a46 b F5 1 +d2a61 w R13 1 +d2aa6 w Q13 1 +d2ab0 b F16 F15 2 +d2ab1 b B5 1 +d2af7 w H2 1 +d2b01 w C17 1 +d2b07 b D14 1 +d2b20 w G3 1 +d2b31 w G4 1 +d2b40 w E12 1 +d2b41 b N4 1 +d2b67 b S12 1 +d2b76 b B17 1 +d2b81 w R5 1 +d2bd0 w O17 1 +d2bd1 w L2 1 +d2c00 w F5 1 +d2c16 w D17 1 +d2c67 b S6 1 +d2c80 w Q14 1 +d2c91 b D6 1 +d2cb7 w D12 1 +d2cc6 w J3 1 +d2cd6 w R3 1 +d2d46 b O17 1 +d2d56 b O14 1 +d2d67 w P18 1 +d2d76 b B5 1 +d2d77 w N17 1 +d2d90 w L17 1 +d2da7 w D13 1 +d2db7 w Q9 1 +d2dc0 b B3 1 +d2dd0 b C18 1 +d2de0 b E6 1 +d2df6 w R12 1 +d2e07 b G18 1 +d2e21 b C16 1 +d2e26 b P13 1 +d2e40 w Q11 1 +d2e50 b R6 O5 2 +d2e51 b P15 1 +d2e67 w S15 1 +d2e87 b B5 1 +d2e97 w N16 1 +d2eb0 b S18 1 +d2ed0 w D5 1 +d2ef7 b S5 1 +d2f01 b C16 1 +d2f10 w E6 1 +d2f20 b R2 1 +d2f70 b R9 1 +d2fa0 b N3 S15 2 +d2fd1 w S2 1 +d2fd6 b E16 1 +d2ff1 b C13 1 +d3006 b R4 E16 2 +d3011 w F12 1 +d3017 w S8 1 +d3041 b C13 1 +d3050 b N17 P15 N16 3 +d3051 w B2 1 +d3056 b E12 1 +d3066 w C5 1 +d3077 b D14 1 +d3086 w F6 1 +d30c1 b S14 1 +d30e0 w E5 1 +d30e1 w E15 1 +d30f7 w H3 1 +d3107 b Q4 1 +d3137 w H3 1 +d3151 w S1 1 +d3166 w S17 1 +d3190 w P16 1 +d3197 w Q7 1 +d31a1 b E14 C17 2 +d31b6 b F15 1 +d31c1 w Q3 1 +d31c7 b R3 Q4 P4 O5 O4 5 +d31c7 w R4 1 +d31d6 b H3 1 +d31f7 b S11 1 +d3200 w G5 1 +d3220 b O7 1 +d3226 w Q6 1 +d3266 w R8 1 +d32a7 b P18 1 +d32b6 w B3 1 +d32c6 w R4 S5 2 +d32e7 w C17 G16 2 +d32f6 b P15 1 +d3301 b Q4 1 +d3301 w R5 R6 Q5 P4 4 +d3310 b D6 1 +d3336 w S13 1 +d3341 w R14 S14 2 +d3356 b R4 1 +d3387 b E17 1 +d3397 w F4 1 +d33c6 w N11 1 +d3410 b L3 1 +d3411 w E5 1 +d3420 w E2 1 +d3421 b R16 1 +d3441 w C3 1 +d3456 b C7 E5 2 +d3487 b S13 1 +d34e7 b E18 1 +d34e7 w C15 F17 2 +d34f1 b F5 1 +d3507 w B15 1 +d3531 w S17 1 +d35d0 b Q16 1 +d35f0 b H17 1 +d3606 b G16 1 +d3606 w Q15 1 +d3640 b Q5 1 +d3641 b N3 1 +d3677 b C16 1 +d3686 b P16 1 +d3697 w D6 1 +d36b6 w R17 1 +d36f0 w Q18 1 +d3700 w E2 1 +d3717 w R16 P6 2 +d3721 w B6 1 +d3726 w L17 N6 2 +d37a6 w N17 1 +d37d1 w S3 1 +d37f0 w H5 1 +d37f1 b E3 1 +d3801 b F14 1 +d3820 w F15 1 +d3827 b P3 1 +d3880 w D15 1 +d3886 b C13 J7 2 +d38a7 b D6 1 +d38b6 w D3 1 +d38c0 b F18 1 +d3917 w E5 1 +d39d0 b C18 1 +d39f0 w D3 1 +d3a10 b E15 1 +d3a27 b B14 1 +d3a36 b D4 1 +d3a40 b B5 1 +d3a66 w R13 1 +d3a77 w S17 1 +d3a80 w P17 1 +d3a90 w S5 1 +d3a96 b G17 1 +d3af0 b R3 1 +d3af1 w S7 1 +d3b10 b B13 E13 2 +d3b26 w G16 1 +d3b61 b D14 1 +d3c77 b E3 1 +d3ce7 b F17 1 +d3d10 w E18 1 +d3d70 w F4 1 +d3d80 w S15 1 +d3da7 b F17 1 +d3e00 w Q13 1 +d3e87 w C3 1 +d3ec0 b J3 1 +d3f57 b D16 D14 2 +d3f81 w R18 1 +d3fe1 w O18 1 +d3fe6 b D18 1 +d3ff6 w C13 E15 2 +d4001 b C6 E2 2 +d4007 w C15 1 +d4047 b C18 1 +d40d1 w R16 1 +d40e1 w E14 1 +d40f0 b O17 1 +d4140 b C17 1 +d4141 b S18 1 +d4146 w D17 1 +d41c0 b P8 1 +d41c7 w P18 1 +d41e0 b E16 1 +d4206 b B5 1 +d4230 b C17 1 +d4257 w S5 1 +d4260 b F12 1 +d4297 w D18 1 +d42b1 b C5 1 +d42c6 w D16 1 +d42d7 b R3 1 +d42e0 b F4 G4 2 +d4317 b R17 1 +d4321 w D17 1 +d4386 b E2 1 +d4397 w E6 D7 C5 3 +d43a7 b R5 1 +d43b6 b E18 1 +d43c1 w N4 1 +d4411 w Q11 1 +d4446 w H3 1 +d4457 b N14 1 +d4466 w O3 1 +d4471 w E18 1 +d4481 b C6 1 +d4496 w P17 1 +d44b7 b R15 1 +d4536 w O6 1 +d4557 w D5 P3 2 +d4577 b C17 1 +d45c7 b D7 1 +d45e1 b E5 1 +d4611 w R9 1 +d4646 w E13 1 +d4647 b E14 1 +d4656 b O17 1 +d4661 b Q5 1 +d4666 w S8 1 +d4711 w C12 D2 2 +d4747 b D14 1 +d4747 w B13 1 +d4766 b B5 1 +d47c7 b S5 1 +d47d0 w F19 1 +d47f7 w E16 1 +d4836 w E4 F3 2 +d4840 b P4 1 +d4841 b Q4 1 +d4856 w Q17 P18 2 +d4866 b N15 1 +d4881 b F16 1 +d48b7 w S5 1 +d48c6 b S17 N4 2 +d48c7 b R6 S6 2 +d48f6 b F3 1 +d4910 b Q18 1 +d4930 w E3 1 +d4931 w C3 1 +d4947 w S17 1 +d4951 b P18 S14 2 +d4980 w C6 F5 2 +d4981 b D17 1 +d49a6 b P15 P16 2 +d49a6 w R7 1 +d49b1 b E2 1 +d49b6 b C6 D7 2 +d49c0 w C4 1 +d49c6 b R16 1 +d49e0 b Q9 1 +d4a27 b B16 1 +d4a47 w S6 1 +d4a61 w C2 1 +d4a66 b E16 1 +d4a80 b S13 1 +d4a96 w S9 1 +d4ab6 w Q3 1 +d4af0 w E15 1 +d4b36 b N2 1 +d4b40 w P4 1 +d4b51 w Q17 1 +d4b60 b F5 1 +d4b61 w D13 1 +d4b66 b Q13 1 +d4b70 w S18 1 +d4bb6 b D2 1 +d4bd0 w M3 1 +d4bd1 w R18 1 +d4be6 w B8 1 +d4bf1 b J14 1 +d4c30 b F5 1 +d4c40 b P8 1 +d4c50 b E18 1 +d4c60 b M6 1 +d4c80 b Q13 1 +d4c96 w D5 1 +d4cd6 b N16 1 +d4d67 w D15 E16 2 +d4da1 b B17 1 +d4da1 w R7 1 +d4dc1 b Q14 1 +d4dd1 w D6 1 +d4df7 b P3 1 +d4e17 b B7 1 +d4e41 b F17 1 +d4e80 w Q5 1 +d4ed7 w H16 1 +d4ef0 b R17 1 +d4f01 b R15 N18 2 +d4f06 w R5 1 +d4f07 b R8 1 +d4f41 b P3 1 +d4f56 w O16 1 +d4f60 w Q15 1 +d4f67 b E2 1 +d4f77 b F2 1 +d4f87 b C7 1 +d4f96 w B9 1 +d4fe1 b G4 1 +d4ff6 w P14 1 +d5036 w S3 1 +d5037 w R15 1 +d5070 w P3 1 +d5087 w M18 1 +d5097 w D16 1 +d50a7 b P9 1 +d50b0 w S17 1 +d50b7 b N14 1 +d50d7 b R6 P4 2 +d5107 w P6 1 +d5110 b F5 1 +d5110 w L7 1 +d5121 w R13 1 +d5156 b Q15 R14 2 +d5171 b P7 1 +d5186 w P16 1 +d5196 b B4 1 +d51a1 b D18 1 +d51b0 b Q5 1 +d51c6 w F16 1 +d51d6 b R18 1 +d51e6 w R13 1 +d5200 b C7 1 +d5211 b F14 1 +d5237 b C12 1 +d5240 w B16 1 +d5267 b P17 1 +d5276 b B4 1 +d5286 b C12 1 +d52a1 b E7 1 +d53a6 w Q5 R6 2 +d53b1 w N3 1 +d53c0 b R9 1 +d5401 b F4 1 +d5410 b N16 1 +d5420 w Q17 Q16 2 +d5450 b P14 1 +d5467 w Q5 R5 2 +d5480 b B16 1 +d54a7 b Q4 1 +d54d6 w B3 1 +d54f0 w P13 1 +d5517 w E17 1 +d5526 w C12 1 +d5531 w P13 1 +d5541 w F16 1 +d5566 w R17 1 +d55c1 w F13 1 +d55d1 w S17 1 +d55e0 b H16 1 +d5606 b O17 1 +d5627 w R17 1 +d5671 w P14 O16 2 +d56a7 b D7 1 +d5737 b D7 1 +d5797 w P3 1 +d57b0 w F15 1 +d57b6 w C6 D5 2 +d57b7 b D4 D6 2 +d57f7 b S16 1 +d5800 b O3 P6 2 +d5837 b E16 D17 2 +d5837 w D15 C17 C13 F16 H16 5 +d5890 w D6 1 +d58d0 w R8 N3 2 +d58d7 b O7 1 +d58e7 w B4 C3 C4 H3 4 +d58f6 b C2 1 +d58f7 w E16 1 +d5900 w R6 1 +d5930 w G6 D3 2 +d5940 w N14 1 +d5971 w D17 1 +d5977 b F16 C17 C4 3 +d59a7 b S2 1 +d59b7 b S12 1 +d59e0 b O15 R3 2 +d59e7 w C6 1 +d5a00 b S15 1 +d5a06 b F16 1 +d5a11 b Q2 R3 2 +d5a66 w R3 1 +d5a97 b P4 R7 2 +d5ad1 b Q18 1 +d5af1 w M3 1 +d5af6 b N14 1 +d5b10 b O17 1 +d5b57 b O3 1 +d5b91 b O16 1 +d5b96 w A4 1 +d5bc0 b F4 1 +d5bc1 b S5 1 +d5bd6 w D17 1 +d5bf7 w P14 Q13 R15 3 +d5c10 w G12 1 +d5c36 b Q14 Q17 2 +d5c40 w D12 1 +d5c46 w B11 1 +d5c96 b D16 1 +d5cb0 w E14 1 +d5cd0 w S5 1 +d5ce0 w E9 S17 2 +d5cf7 w E3 1 +d5d31 b O12 1 +d5d36 w E15 1 +d5d91 b E16 Q14 2 +d5d91 w D15 E15 G16 C17 4 +d5db6 w R7 1 +d5de7 b D7 F6 2 +d5e01 w R3 1 +d5ea6 w E6 1 +d5ec7 b P5 1 +d5f56 w A4 1 +d5fc6 b D14 1 +d6027 b C16 1 +d60a6 b E14 1 +d60c6 b C15 1 +d6117 b Q3 1 +d6117 w R13 1 +d6130 b E4 1 +d6147 w B17 1 +d6190 w O18 1 +d61e0 b J4 1 +d6201 b Q16 1 +d6206 b P5 1 +d6207 w E6 1 +d6211 b D12 1 +d6247 b J16 1 +d6267 b R16 1 +d6287 b P5 P3 2 +d6291 b B15 1 +d6296 b S2 1 +d62e7 w E17 1 +d6310 w R16 1 +d6320 w E5 1 +d6340 w P14 1 +d6350 b O4 1 +d6360 b M16 1 +d6381 b E16 1 +d63b1 b Q13 1 +d63d0 w O14 1 +d63e1 w H2 1 +d6410 b S2 1 +d6420 w E6 1 +d6421 b D16 1 +d6430 b E5 1 +d6446 w E15 1 +d6451 b N3 1 +d6476 w R14 Q13 2 +d6490 b O15 1 +d6491 b B5 1 +d6496 b Q13 1 +d64a0 b B15 1 +d64a6 w Q5 1 +d6507 w S5 1 +d6550 w N15 1 +d65c7 w S18 1 +d65d7 w F9 1 +d6616 b N18 1 +d6626 b Q14 1 +d6637 b D4 F4 2 +d6661 w O15 1 +d6667 b R17 1 +d66a0 b N16 C15 2 +d66c1 b R8 1 +d66e1 w O6 1 +d66f1 b C13 1 +d66f6 b L18 1 +d66f7 w Q4 1 +d6717 w B4 1 +d6751 b Q16 1 +d6751 w R15 R14 Q15 P16 4 +d6797 b D16 1 +d67a1 b B4 1 +d67b6 b P4 1 +d67b6 w D17 1 +d67f1 w O15 1 +d6820 w B5 1 +d6827 b F4 1 +d6840 w P16 1 +d6867 w D18 1 +d6896 w D4 1 +d68a1 b C2 1 +d68a1 w R2 1 +d68a6 w F4 1 +d68f6 b R15 1 +d6926 b D17 1 +d6931 b O9 1 +d6940 b O17 1 +d6996 b Q13 1 +d69a6 b S16 1 +d69d0 w J3 1 +d69e7 w S3 1 +d69f1 b G7 1 +d6a00 b N4 Q4 O5 3 +d6a11 b O16 N15 2 +d6a27 b S15 1 +d6a27 w Q5 1 +d6aa6 b P5 1 +d6ad6 b O4 1 +d6ad7 w C17 1 +d6af6 w O18 1 +d6b30 w O18 1 +d6b40 w P13 1 +d6b67 w S14 1 +d6b91 w E18 1 +d6b97 b Q18 1 +d6bd7 b S14 1 +d6c26 w R13 1 +d6c80 w Q12 1 +d6c91 b E18 1 +d6cd6 b R11 1 +d6ce6 w N15 1 +d6d01 w Q17 1 +d6db6 b S3 1 +d6dc6 w F7 1 +d6dd7 b B6 1 +d6e00 w O13 1 +d6e07 b P17 1 +d6e20 w R4 1 +d6e67 w Q6 1 +d6e70 b D5 1 +d6e71 b O2 1 +d6ec6 w A3 1 +d6ee0 w F7 1 +d6f57 w O6 1 +d6f61 w E13 1 +d6f80 b F16 1 +d6fa0 w E16 1 +d6ff1 w S13 1 +d7000 w S17 1 +d7017 b N14 1 +d7067 w C17 1 +d70a6 b S17 1 +d70a6 w O3 1 +d70a7 b G18 1 +d7106 b F7 1 +d7110 b F4 1 +d7110 w R8 1 +d7146 b Q6 1 +d7146 w B16 1 +d7171 b S5 1 +d7180 b P2 1 +d7196 b H3 1 +d71b1 w R14 1 +d71c7 b R12 1 +d71d7 w G16 1 +d7207 w R5 1 +d7250 b C4 1 +d72b7 b G18 1 +d72d6 w N5 1 +d72d7 w R2 1 +d72f6 b N4 1 +d7317 b G4 F4 2 +d7317 w F4 1 +d7336 b E16 1 +d7371 w E18 1 +d7376 w D5 1 +d7396 w Q3 1 +d73b7 w N2 1 +d7436 b C14 G17 2 +d7437 b E17 1 +d7441 b E18 B14 2 +d7446 b D13 1 +d7451 w R4 1 +d7456 b P18 1 +d7476 b E13 1 +d7497 b E2 1 +d74d7 b E5 1 +d74f1 b S3 1 +d7511 w Q13 1 +d7536 b C4 B5 2 +d7536 w P13 1 +d7540 w R16 1 +d7547 b C17 C16 J17 H17 E18 D17 6 +d7576 w Q18 D6 2 +d7580 w D6 1 +d75a0 w Q7 1 +d75c0 w R17 1 +d75e0 b D5 1 +d7610 b H14 1 +d7627 b B16 1 +d7637 b P8 1 +d7651 b R9 O5 2 +d76b1 w P16 1 +d76b6 b D18 1 +d76d6 w O18 1 +d76f7 b L2 1 +d76f7 w T6 1 +d7770 b C18 1 +d7791 w E14 1 +d77a6 b S15 1 +d77b7 w C8 1 +d77e6 b P3 1 +d77e7 b E15 1 +d7856 w F9 1 +d7877 w F4 B3 2 +d78b7 w E12 1 +d78d6 w C7 1 +d78e1 b E15 1 +d78f7 b R5 1 +d7906 w E2 1 +d7910 w B16 C15 2 +d7947 w S2 1 +d7951 w B6 1 +d7980 b D17 1 +d7996 w R11 1 +d7997 w B5 1 +d79a0 w H14 1 +d79a6 b F17 1 +d79b6 w Q16 1 +d7a10 b N17 1 +d7a11 w F15 1 +d7a70 w P4 1 +d7a86 b C6 1 +d7aa0 w P14 O16 2 +d7aa6 b C15 1 +d7ac6 b O5 1 +d7ad1 w D7 C3 2 +d7b00 b H6 1 +d7b01 w N18 1 +d7b10 b R17 1 +d7b17 b S7 1 +d7b40 b P5 1 +d7b46 w P16 1 +d7b56 w R13 P15 2 +d7b66 w O14 1 +d7b80 b B6 1 +d7bd0 b H13 1 +d7be6 b B16 1 +d7c67 b R16 P16 2 +d7c86 b S3 1 +d7ce7 b F4 1 +d7d01 w A5 1 +d7d07 b E18 1 +d7d10 b S2 1 +d7d10 w C15 1 +d7d30 b O3 1 +d7d66 b B2 1 +d7d67 w F3 1 +d7d76 w O4 1 +d7d87 b S13 1 +d7da6 w Q16 1 +d7dc1 w D16 1 +d7e21 b O7 B3 2 +d7e47 b R14 1 +d7e70 b C18 D18 2 +d7e91 b G5 1 +d7ea1 w J3 1 +d7ec1 b D5 C3 C7 F4 H4 5 +d7ec1 w E4 D3 2 +d7ef7 w Q6 1 +d7f17 w F4 G3 2 +d7f56 b R12 1 +d7f66 b R2 1 +d7fa6 b Q13 1 +d7fb1 w O5 1 +d7fc7 b N3 1 +d7fe6 b E13 1 +d8007 b P17 1 +d8040 w M4 1 +d8051 b D17 1 +d8067 w G16 C15 2 +d8087 b O16 1 +d80a0 b Q6 1 +d80b1 w T15 1 +d80b6 b F17 1 +d80d7 w B4 1 +d80e7 b C3 1 +d80e7 w O5 1 +d80f6 b D13 1 +d8110 b R8 1 +d8140 w B15 1 +d8157 b R5 1 +d81e6 w F15 1 +d81f7 b M18 1 +d8226 w C2 1 +d8230 w C4 1 +d8271 w D4 C3 2 +d82e1 w E3 1 +d8311 w S14 1 +d8330 b C6 1 +d8357 b C3 1 +d8360 b D15 1 +d8377 b E5 1 +d83a0 w E4 1 +d83b0 b L4 1 +d83d0 w O5 1 +d8400 b O6 1 +d8401 b B3 1 +d8417 b H17 1 +d8420 w F15 1 +d8436 b C15 1 +d8456 b D8 1 +d8471 b D18 1 +d8490 b E15 1 +d84b0 w S16 R15 2 +d84c1 b C16 1 +d84c6 b P3 S3 2 +d84d0 w D5 1 +d84e1 b B17 1 +d8506 w Q14 1 +d8516 w F6 1 +d8576 w D5 E4 2 +d8577 b C3 1 +d8596 w E17 1 +d85d0 b E17 1 +d85d0 w Q6 1 +d85f7 b G16 F14 2 +d8600 w D6 1 +d8607 b E13 1 +d8621 b B16 1 +d8650 b R19 1 +d8671 b N4 1 +d8680 b B18 1 +d8697 w P2 1 +d86a0 b Q5 1 +d86b1 w O3 1 +d86c0 w F4 D5 2 +d86d0 w C16 1 +d86d6 b R4 P5 2 +d86e7 b C4 1 +d86f0 w C15 1 +d8716 w C13 1 +d8736 b F2 1 +d8776 b O5 1 +d87d0 b E2 1 +d87f0 w P2 1 +d8816 b B14 1 +d8821 b D16 1 +d8847 b Q9 1 +d88c7 w G16 1 +d88d7 b N8 1 +d8936 b G4 R15 2 +d8950 b H6 1 +d8971 b R5 1 +d8986 w C6 D6 2 +d89a6 b S14 1 +d89b7 w Q3 1 +d89d7 w C3 1 +d89e1 b C8 1 +d8a07 b C9 1 +d8a17 w H6 1 +d8a20 b Q4 1 +d8a20 w Q14 1 +d8a27 b O7 1 +d8a50 w Q6 P3 O5 M3 4 +d8a51 w S15 1 +d8a66 b B4 1 +d8ad7 w E18 1 +d8ae0 b C2 1 +d8af6 w F7 1 +d8b06 b E7 1 +d8b10 b P1 1 +d8b17 w F17 1 +d8b21 b P6 1 +d8b41 b Q17 1 +d8b50 w D14 B14 2 +d8b51 w P2 1 +d8b66 w R15 1 +d8b80 b Q2 1 +d8bb7 w D2 E4 2 +d8bf1 w R9 1 +d8c11 b C13 1 +d8c17 b F14 1 +d8c26 b F3 1 +d8c41 w N3 1 +d8c50 w G5 1 +d8ce7 w Q7 1 +d8cf0 w Q5 1 +d8d01 w C14 1 +d8d10 b P12 1 +d8d27 b E16 C13 2 +d8d27 w R8 1 +d8d31 w C2 1 +d8d40 w G3 1 +d8da1 w G16 1 +d8da7 b J4 1 +d8dc1 w Q4 1 +d8df0 b H17 1 +d8e21 b E14 1 +d8e50 b F15 1 +d8e91 b O17 1 +d8eb1 b Q6 1 +d8ec0 w Q6 1 +d8ee6 b C14 1 +d8ef1 b P3 1 +d8f47 w D18 1 +d8f80 w J3 1 +d8fa0 w C8 1 +d8fc7 w C5 1 +d9010 w O16 1 +d9036 b B14 1 +d9081 b S4 1 +d9097 w B17 1 +d90c6 b R15 R18 2 +d9117 b C6 1 +d9141 w R6 Q5 2 +d9156 b N3 1 +d9170 b Q1 1 +d91a1 w C15 1 +d91b6 b Q15 P16 2 +d91c1 b D13 1 +d91e0 w R17 N16 2 +d91f7 b B2 1 +d9207 w S13 1 +d9230 b H5 1 +d92b1 w E5 1 +d92f1 b Q16 1 +d92f1 w P17 O17 P16 Q15 4 +d92f6 b S14 1 +d9350 b P7 P2 R3 Q6 R6 P18 6 +d9357 b C16 1 +d9360 w B5 1 +d9366 b B4 1 +d9390 b P15 1 +d9486 b B12 1 +d94a1 b Q4 1 +d94b0 b G16 1 +d94b0 w E15 1 +d94d1 w P14 1 +d94e1 b S16 1 +d9530 w C2 1 +d9560 b E14 1 +d9580 w M17 1 +d95a7 b P14 1 +d95b0 b N3 1 +d95b6 b N4 1 +d95c1 b D13 1 +d95c7 b S13 1 +d95d6 w B6 1 +d9611 b R4 1 +d9620 b R5 1 +d9650 w H5 1 +d9677 w F2 1 +d96c6 b E14 F17 2 +d9701 b B15 1 +d9717 b D18 1 +d9720 w D5 1 +d9791 b P5 1 +d9796 b D3 1 +d97b6 w B6 1 +d9807 w F17 1 +d9837 b F16 1 +d98e1 w H15 1 +d98f7 w D2 1 +d9970 b O3 1 +d99a1 w B15 1 +d99c1 w P15 1 +d99c6 b D14 1 +d99d1 b P14 1 +d99d6 b Q14 1 +d99e1 b Q3 1 +d99e6 w B16 1 +d99e7 b C9 F5 G5 3 +d9a16 b T8 1 +d9a20 w C7 1 +d9a21 b Q15 1 +d9a47 b Q5 N3 2 +d9a56 w E3 1 +d9a90 w R17 1 +d9a91 b D6 1 +d9ab0 w C17 1 +d9ab7 w C18 1 +d9b00 b Q15 1 +d9b16 b R2 1 +d9b37 b E4 F4 2 +d9b57 b D9 1 +d9b87 w D6 1 +d9b97 b N14 C11 F15 G15 4 +d9b97 w D4 1 +d9bb1 w S17 1 +d9bf7 b Q17 1 +d9c01 b N17 1 +d9c40 w P8 1 +d9c96 b O16 P16 2 +d9ca6 w C3 1 +d9cf6 w S2 1 +d9d07 b G3 R2 2 +d9d16 w O17 1 +d9d21 w R16 1 +d9d30 b N8 M6 2 +d9d86 w P13 Q15 R14 3 +d9d90 b B17 1 +d9d96 w S17 1 +d9dd6 w P4 1 +d9de0 b C9 1 +d9de1 w C17 1 +d9df0 w B14 1 +d9e20 b B13 1 +d9e37 b E5 1 +d9e41 w B15 1 +d9e47 w S4 1 +d9e57 b D3 1 +d9e70 b S3 1 +d9e87 b R12 S15 S16 3 +d9eb0 w D6 1 +d9ec0 w R12 1 +d9ee1 b P16 1 +d9ee6 b D5 1 +d9f90 w H17 1 +d9fa7 w D16 1 +d9fb1 w R16 1 +d9ff0 w D4 1 +da001 w P6 1 +da006 b D13 1 +da010 b R2 1 +da096 b D13 1 +da0b7 w C18 1 +da0d1 w H4 1 +da0f0 w N14 1 +da0f1 b D4 1 +da0f6 b O3 P4 2 +da101 b H17 1 +da101 w C15 1 +da156 w E6 D7 F5 3 +da196 w O14 1 +da1a7 b Q3 1 +da1b0 b C15 R15 2 +da1b7 b G3 1 +da211 b P18 1 +da211 w C16 1 +da220 b P3 1 +da247 w P17 O17 O16 P16 4 +da267 w R9 R7 2 +da270 b D14 E17 F15 H17 4 +da276 b P14 1 +da277 w D18 1 +da2b6 b L17 1 +da2b7 w E7 1 +da2c7 b D15 1 +da2d7 b C12 1 +da2e0 b S4 1 +da2f1 w O3 1 +da2f6 w C13 C15 2 +da300 w D3 1 +da347 w P18 1 +da357 w F8 1 +da366 w D14 1 +da367 w R4 1 +da377 b R3 R4 L3 M3 P2 Q3 6 +da3a6 b P4 1 +da3b6 w E14 1 +da3e1 w E15 C15 2 +da437 w B3 1 +da440 w O17 1 +da450 b P4 1 +da471 w F18 1 +da490 b F15 1 +da4b7 b M17 1 +da4b7 w Q2 1 +da4e7 w L6 1 +da507 w G4 1 +da510 w Q6 1 +da530 b R5 D5 2 +da561 b N15 1 +da567 b E15 1 +da5a1 w F18 1 +da5c7 w B15 1 +da5d6 w P8 1 +da617 w G18 1 +da626 w R6 1 +da627 w S5 1 +da660 w P15 1 +da681 b S5 1 +da697 w N5 1 +da6a6 w S18 1 +da6d7 w Q17 1 +da6f0 w P4 1 +da701 b G16 1 +da727 b D5 1 +da730 w R5 1 +da751 b Q3 1 +da751 w S9 1 +da767 w H18 1 +da777 w C6 1 +da7d1 w B6 1 +da807 w P15 1 +da811 b E4 1 +da876 b B15 1 +da880 w E3 1 +da8a7 w Q15 1 +da8d0 b D6 1 +da906 b D3 1 +da916 w M3 1 +da927 w N5 1 +da951 w J3 1 +da9a1 b D2 O6 2 +da9d6 b Q2 1 +daa07 b R9 1 +daa37 w R6 P2 2 +daa41 w H13 1 +daa50 b B5 1 +daa61 b Q1 1 +daa67 b D6 1 +daa76 b C17 1 +daab1 w O14 1 +daab6 b S2 1 +daac6 w Q2 1 +dab00 w C3 1 +dab66 b R7 1 +dab71 b R18 1 +dab96 w D2 1 +dabf7 w D14 C13 2 +dac47 b F3 1 +dac50 w F17 1 +dac51 b D7 D4 2 +dac60 w Q13 1 +dac71 w Q5 1 +dac76 w R16 1 +dac90 w E14 1 +dacd7 w F16 R14 2 +dad40 b N16 1 +dad46 w O16 1 +dad50 b D12 1 +dad76 b F4 1 +dad90 b H3 1 +dada6 b C3 1 +dade7 b B15 1 +dae11 w Q9 1 +dae41 w C12 1 +dae60 w O3 Q4 Q3 3 +dae66 b R3 Q4 D2 3 +dae70 w D13 1 +dae87 b A17 1 +dae96 w N15 1 +daed0 w R4 1 +daf31 b F4 E15 2 +daf36 b S17 1 +daf41 w L4 1 +daf87 w B12 1 +dafb6 b E3 1 +dafc1 w Q4 R3 2 +dafc7 b C17 1 +dafe1 b C5 1 +daff0 w B4 1 +daff1 w Q12 1 +db006 b Q16 1 +db017 w R5 1 +db070 b F2 1 +db086 b R3 1 +db0f7 w M17 1 +db110 b D5 1 +db170 b Q14 1 +db181 w P13 1 +db191 b O17 1 +db1a0 w N15 1 +db1b1 w S18 1 +db1c0 b R16 1 +db1e1 w M4 1 +db1f0 b B18 1 +db230 w D13 1 +db247 b C5 1 +db260 w P15 1 +db266 b P16 Q14 2 +db2b7 b D7 1 +db2c1 w C17 1 +db2e1 w D17 1 +db311 b R8 1 +db317 w S17 1 +db326 b R5 R2 2 +db340 b Q2 S3 2 +db350 b R5 1 +db366 b F17 B9 2 +db370 w M2 1 +db381 b S14 1 +db3a1 w F3 1 +db3e1 b E5 1 +db407 w B4 1 +db437 b P5 R6 2 +db456 w F14 1 +db4f6 w C3 1 +db506 b B5 1 +db566 b N5 1 +db567 b Q14 1 +db586 b P2 1 +db5a0 w S14 1 +db5c1 w D3 1 +db5f0 b Q14 1 +db617 w E2 1 +db627 w E4 O14 2 +db630 b D15 1 +db660 b E13 1 +db671 w H16 1 +db687 w B8 1 +db691 w B3 1 +db6f1 w D5 1 +db710 b Q3 1 +db711 w Q3 1 +db727 w N18 1 +db741 b S13 1 +db760 w D14 1 +db780 b E19 1 +db797 b F4 1 +db7c1 w P14 1 +db7d7 b G4 1 +db7e6 w C17 1 +db800 b C5 1 +db851 b E5 1 +db880 w Q16 1 +db886 w G16 1 +db8a0 b B15 1 +db8c0 b C5 1 +db8e6 b O5 1 +db910 w N16 1 +db917 b C2 1 +db936 b O4 R4 2 +db957 b C4 C15 2 +db967 w S16 1 +db970 b G18 1 +db977 w G18 1 +db9a7 b Q7 1 +db9b6 w C12 1 +db9c6 w P7 1 +db9d0 w C5 1 +db9f6 b R2 1 +dba01 b O13 1 +dba07 w F18 1 +dba16 w S17 1 +dba61 w S4 1 +dba66 b A2 1 +dbaa0 w C6 1 +dbae1 w C4 1 +dbb11 b Q16 1 +dbb20 b P17 1 +dbb20 w F14 1 +dbb30 w E6 1 +dbba1 b M17 1 +dbbe0 w B5 1 +dbbf1 b B4 C3 C4 H3 4 +dbc07 b E5 1 +dbc20 b B2 1 +dbc20 w H3 1 +dbc27 b Q7 1 +dbc27 w E7 R14 2 +dbc30 b S13 1 +dbc50 w E16 1 +dbc66 w C5 C2 2 +dbc76 w C19 1 +dbc77 w Q13 1 +dbc80 b N5 1 +dbcd0 w N6 1 +dbcd6 w J3 1 +dbd01 b N2 1 +dbd21 b C18 1 +dbd27 w R4 1 +dbd60 w P6 1 +dbd80 b J3 1 +dbd81 b R4 1 +dbd87 w D8 1 +dbd90 b R16 Q17 2 +dbdb7 w P14 1 +dbdd6 b F17 1 +dbde0 b H16 1 +dbdf0 w F2 1 +dbec1 b S15 1 +dbed7 b C12 1 +dbf10 w F3 1 +dbf16 w D5 1 +dbf27 b S15 1 +dbf27 w B4 1 +dbf51 b P5 1 +dbf57 w P7 1 +dbf67 w C18 1 +dbf86 w R5 1 +dbfc0 w F18 1 +dbfd7 b P2 1 +dbff1 w O3 1 +dc086 w R4 Q5 Q9 3 +dc090 b O5 1 +dc097 w P7 1 +dc0e0 w P18 D12 2 +dc0e1 b E3 1 +dc0e7 w D18 1 +dc106 w N14 1 +dc117 b N5 1 +dc140 w D12 1 +dc141 w R7 1 +dc1b1 w D17 1 +dc1f1 w D9 1 +dc211 b C1 1 +dc236 b P4 1 +dc270 w H17 1 +dc271 w D13 1 +dc2e0 b E14 1 +dc340 w O4 1 +dc366 w B13 1 +dc370 b C4 1 +dc371 w C4 1 +dc376 w S17 1 +dc381 b Q15 1 +dc387 w E18 1 +dc3b1 w D2 B3 2 +dc3b6 w D1 1 +dc3e0 b R12 1 +dc3f6 b B18 1 +dc410 b R12 1 +dc421 w B3 1 +dc461 w C16 1 +dc467 b N2 M5 2 +dc467 w Q6 1 +dc476 b B14 1 +dc477 b H15 1 +dc477 w Q14 R13 Q17 3 +dc496 w D4 1 +dc4b1 b O18 1 +dc4d0 w P3 1 +dc4e6 b E15 1 +dc4e7 b E18 1 +dc507 b D4 1 +dc520 w R7 1 +dc540 w E2 1 +dc571 b P2 1 +dc5b0 b R5 1 +dc5b7 w N5 1 +dc5e0 b S15 1 +dc620 b N3 1 +dc636 b C16 1 +dc636 w C5 1 +dc640 b P3 G17 2 +dc651 b G17 1 +dc666 b C3 1 +dc687 w Q12 1 +dc6a7 w G2 1 +dc757 w D2 1 +dc7b7 b D5 F5 2 +dc7c0 b B4 1 +dc7e0 b O15 1 +dc821 b Q7 1 +dc826 b R5 1 +dc870 b G17 1 +dc870 w C15 1 +dc891 b E13 1 +dc8d1 w Q7 1 +dc8f6 b P2 1 +dc927 b D7 1 +dc997 b N16 M14 2 +dc9a6 b Q12 1 +dc9c1 w N5 1 +dc9c6 b D6 1 +dc9c7 w Q17 1 +dca47 b F2 1 +dca66 b C11 1 +dca70 w C13 1 +dca87 w P13 1 +dcb01 b G7 1 +dcb76 w Q5 D6 2 +dcb80 w R5 1 +dcb90 b E19 1 +dcbf6 b R18 1 +dcc11 w P18 1 +dcc20 b R5 1 +dcc46 w E15 1 +dcc57 w Q16 1 +dcc76 b S16 1 +dcca0 b O15 1 +dccb7 b C14 1 +dccc0 w G4 1 +dcd47 w B5 1 +dcd51 w N16 1 +dcd77 w D17 1 +dcd80 b C15 1 +dcdb0 w S17 1 +dcdc6 b T3 1 +dce01 b B15 1 +dce61 b M4 1 +dce77 b C17 1 +dce87 w Q17 1 +dceb0 b R13 1 +dcee7 w B5 1 +dcf20 w P18 1 +dcf40 w F16 1 +dcf71 b M16 1 +dcfb0 b E12 1 +dcfd0 w E17 1 +dcfd1 b O4 1 +dcfe0 b P16 1 +dcfe6 b P13 1 +dd011 w R17 1 +dd017 b R6 1 +dd040 b C1 1 +dd111 w P3 Q4 2 +dd116 b R5 1 +dd116 w D3 E4 J4 3 +dd126 w G18 1 +dd170 w N3 1 +dd191 b C15 F17 2 +dd191 w E18 1 +dd1b0 b O16 1 +dd1b6 b O2 1 +dd1b7 w F16 1 +dd1c0 b C18 1 +dd1d6 b R13 1 +dd200 w C5 1 +dd201 b S7 1 +dd217 b O6 1 +dd266 b G16 1 +dd270 w D16 1 +dd277 w B7 1 +dd2d0 w S17 1 +dd330 b E4 1 +dd341 b R3 1 +dd361 b Q8 1 +dd3a0 b D5 1 +dd3b0 w E14 1 +dd3b6 w R1 1 +dd3c7 b T17 1 +dd3f0 b G5 1 +dd3f1 b F4 1 +dd450 w G14 1 +dd451 w Q4 1 +dd486 b C14 1 +dd491 b C3 F5 C6 D6 4 +dd4e7 b T5 1 +dd506 w F15 1 +dd516 b F4 1 +dd521 w C7 1 +dd531 b G2 1 +dd537 w C12 1 +dd540 b E6 1 +dd561 w B18 1 +dd577 w C3 1 +dd581 b N4 1 +dd5c0 b D6 1 +dd5c0 w P3 1 +dd5e6 w J17 1 +dd5f6 w C16 1 +dd606 b G17 C11 2 +dd610 b O8 1 +dd660 w M3 1 +dd667 b S4 1 +dd671 w D18 1 +dd6b0 w R2 1 +dd721 b D18 1 +dd731 w S5 1 +dd7a6 w D6 1 +dd7b0 b R15 1 +dd7f1 w Q2 1 +dd7f6 b Q6 1 +dd816 w E15 1 +dd826 b B6 1 +dd856 b M2 1 +dd870 w O4 1 +dd880 w D19 1 +dd890 b C12 1 +dd8e1 w Q16 1 +dd926 b G3 1 +dd931 b P2 1 +dd966 w F15 1 +dd9a0 b P15 1 +dda30 w M15 1 +dda96 b O5 O3 2 +ddad0 b C6 1 +ddad1 w H17 1 +ddae0 b R4 1 +ddb17 w G2 1 +ddb26 b J18 1 +ddb47 b F12 1 +ddb66 w E15 1 +ddb81 w Q13 S14 2 +ddba1 b P13 1 +ddbc0 b C4 1 +ddbc7 b D3 1 +ddbe0 w D5 1 +ddbe7 b Q6 1 +ddc46 b D3 1 +ddc70 b E2 1 +ddc77 b G16 H14 2 +ddc80 b M3 1 +ddcd1 w Q2 1 +ddcd7 b M4 1 +ddce0 b D3 1 +ddcf7 w E2 1 +ddd10 b P14 1 +ddd20 b D14 1 +ddd36 b F15 1 +ddd47 w Q15 1 +ddd96 w H16 1 +dddb7 b C15 1 +dddd1 b N5 1 +dde10 b O14 1 +dde31 b D16 1 +dde36 b F2 1 +dde37 b O7 1 +dde47 b P2 1 +dde80 w P15 1 +ddea0 b C18 1 +ddee0 w G8 H6 2 +ddef1 b B6 1 +ddef7 b R17 1 +ddf07 w D17 1 +ddf10 w S18 1 +ddf66 b Q14 1 +ddf81 w P4 1 +ddf87 b A15 1 +ddfa7 b R15 Q16 2 +ddfd7 b B3 1 +ddfe7 b B17 1 +de071 w E4 1 +de0a7 b E2 1 +de0c6 w O4 1 +de116 b C18 1 +de126 w Q6 Q5 2 +de147 b D14 E18 2 +de1e1 b S16 1 +de1e7 b F16 1 +de1f1 w N2 1 +de1f6 w S17 1 +de1f7 w P14 1 +de210 b R9 1 +de240 b C17 1 +de241 w O13 1 +de250 b S15 1 +de256 w B6 1 +de267 w G15 1 +de2b6 b B16 D5 2 +de2c7 w C18 1 +de2f1 w P3 1 +de300 w L4 1 +de340 w M14 1 +de341 b D17 1 +de357 w F14 1 +de366 b B3 1 +de3a6 w E14 1 +de3a7 b R3 1 +de3b6 b L3 1 +de3b7 w R4 1 +de3e1 b D18 1 +de406 w N17 1 +de417 b C1 1 +de420 w O4 N4 2 +de450 w C15 1 +de460 b N4 1 +de470 w E3 1 +de487 b D5 1 +de491 b R15 1 +de4d7 w E6 1 +de507 b L4 1 +de527 b R11 O15 N15 3 +de530 b G15 1 +de5b1 w D16 1 +de626 w C15 1 +de630 b O14 1 +de631 b R5 1 +de637 w G3 1 +de651 w R7 1 +de656 w E5 1 +de666 b C17 1 +de6f6 w E4 1 +de761 b Q18 1 +de771 b D15 D14 E16 C15 4 +de790 b Q14 1 +de7d6 b D8 C6 2 +de7e1 w R6 1 +de836 b C15 1 +de850 w S13 1 +de857 b S16 C14 2 +de8a0 b N14 1 +de8d6 w D13 1 +de907 b R2 1 +de910 b B2 1 +de987 b S18 1 +de9b6 b C2 1 +de9b6 w O13 1 +de9d0 w S15 1 +de9e0 b S3 1 +de9e0 w P8 O17 2 +dea40 w O16 1 +dea41 w Q15 1 +dea90 b B3 1 +deb00 w S4 1 +deb06 w R17 1 +deb17 w F15 1 +deb40 b D2 F4 2 +deb47 w R18 1 +deb86 b B13 1 +deba0 b Q11 1 +deba7 b R15 1 +debb1 w Q3 1 +debe1 b E2 1 +debe1 w Q18 1 +dec06 b Q8 1 +dec17 b R15 1 +dec30 w E6 1 +dec51 w N16 M14 2 +dec66 w E16 D14 2 +dec70 w S3 S4 2 +dec76 b P2 1 +deca0 w Q18 Q16 2 +decd0 w F15 1 +decf6 b B5 1 +ded31 w C16 1 +ded91 b D15 1 +ded91 w C14 1 +dedb7 b G16 1 +dedf0 w G14 1 +dee37 b N14 1 +dee76 w B1 1 +deea1 w N16 1 +deea6 w B1 C7 2 +deed0 w G4 1 +deee0 b C13 1 +def00 w G3 C6 2 +def01 w S18 1 +def06 w H18 1 +def27 w R17 1 +def30 w R3 1 +def41 b R13 1 +def66 w P18 1 +def70 w Q7 1 +def86 b N17 1 +def90 w B7 1 +defc6 b G14 1 +defd7 b P12 1 +df017 w Q6 1 +df027 b O1 1 +df037 b N4 1 +df040 b L17 1 +df046 b Q5 1 +df057 b B4 1 +df077 w B13 1 +df091 b E2 1 +df096 w B12 1 +df0a7 b O5 R3 2 +df0b7 b O5 1 +df0c0 w Q4 1 +df0f6 w P16 1 +df140 b F16 1 +df146 b F3 1 +df167 b C17 1 +df1f7 b B17 1 +df216 w J17 1 +df217 w E13 1 +df247 b F7 1 +df250 w D18 F16 2 +df266 w S3 1 +df2b6 w O14 1 +df2c6 b N16 1 +df2f7 b J4 1 +df321 b C4 1 +df321 w P3 1 +df397 w R17 Q13 2 +df3a1 w Q16 Q14 2 +df3d1 w C17 1 +df417 w N5 1 +df437 w F4 1 +df451 w C6 1 +df466 w A2 1 +df476 b P17 1 +df490 b E16 1 +df4a7 w F9 1 +df510 b M16 1 +df516 w S6 1 +df531 w C11 1 +df556 b M3 1 +df566 b G15 1 +df580 b E4 1 +df581 b T16 1 +df5c6 w Q14 1 +df5c7 w P18 1 +df5f1 b N5 1 +df600 b S6 1 +df606 b S11 1 +df626 w C17 1 +df677 w H18 1 +df6c0 w R17 1 +df6d6 w D3 1 +df701 w H4 1 +df707 b R17 1 +df711 w M17 1 +df726 w R18 1 +df731 b R3 1 +df757 b F4 1 +df777 w P18 1 +df7e1 b P2 1 +df7f0 w E15 G15 R3 3 +df811 w H5 1 +df817 w B17 P17 2 +df821 b P4 1 +df836 w P13 1 +df846 w R7 L3 2 +df851 w R17 1 +df870 w C11 1 +df881 b E4 1 +df897 b C4 1 +df8f0 w E16 1 +df906 w R4 P4 2 +df941 b Q4 1 +df941 w E8 1 +df947 w O6 1 +df960 w M4 1 +df987 w P16 1 +df9b1 w F5 1 +df9c0 b R3 1 +df9f1 w Q16 1 +df9f7 b R7 1 +dfa00 w B15 1 +dfa31 w E13 1 +dfa37 b Q18 1 +dfaa6 w Q13 1 +dfab0 w G3 1 +dfac6 b C15 C18 2 +dfad1 b B7 1 +dfaf0 b P12 1 +dfaf6 b E15 1 +dfb06 b P15 1 +dfb51 w C6 1 +dfb60 b O13 1 +dfba1 w F4 1 +dfbb1 w Q6 1 +dfbc1 b O11 1 +dfbe1 w S2 1 +dfbf1 b O14 1 +dfc11 b D4 1 +dfc17 b C2 1 +dfc17 w B17 1 +dfc37 w F16 1 +dfc81 w C9 1 +dfce0 b O7 1 +dfce7 w R19 1 +dfd46 w B5 F14 2 +dfd81 b N4 1 +dfd81 w R6 1 +dfdc6 b C5 1 +dfdd6 b R5 1 +dfde6 w F4 1 +dfdf7 w F14 1 +dfe00 b S4 1 +dfe00 w O5 1 +dfe10 b E16 1 +dfe17 w P18 1 +dfe70 b M17 1 +dfe81 b P18 1 +dfe86 w Q7 1 +dfe90 b H15 G15 E17 E15 4 +dff06 w R5 1 +dff10 b C5 1 +dff26 w P17 1 +dff60 b C15 1 +dff71 b D12 1 +dff87 w F15 1 +dff91 w P4 1 +e0037 b H17 E18 D18 3 +e00c1 w S18 1 +e00f7 w C3 1 +e0126 w F4 1 +e0131 w B16 1 +e0187 w R16 1 +e0190 w F7 C4 2 +e0196 w B2 1 +e0216 b D17 1 +e0227 b Q9 1 +e0266 b R8 1 +e02a1 b D7 1 +e02b6 b F16 1 +e02c7 b P2 1 +e02e1 b F16 1 +e0301 b D19 1 +e0321 w P17 1 +e0337 b O3 1 +e0341 b N4 1 +e0350 b O14 1 +e0350 w D2 1 +e0360 b Q7 1 +e0361 b E6 1 +e0377 b D4 1 +e0387 w O3 Q4 2 +e03c0 w R3 1 +e0400 b N5 1 +e0407 w R16 1 +e0411 w P14 1 +e0461 w R3 1 +e0486 w D18 1 +e0487 b P6 1 +e04a0 w E17 1 +e04c1 b M4 1 +e04c7 b H14 1 +e04e6 b G5 G6 2 +e04f0 b F15 1 +e0506 b E15 1 +e0530 b Q5 1 +e0540 w B3 1 +e0541 w O4 P5 2 +e0560 b C17 1 +e05c0 w N6 1 +e0607 b O16 1 +e0656 b P18 S18 2 +e06a7 b R3 Q8 2 +e06a7 w P16 1 +e06d1 b E4 1 +e0716 b D2 1 +e0737 b D3 1 +e0767 b Q5 1 +e0776 w B17 1 +e0780 w E4 1 +e07b7 b F17 1 +e07f0 b N2 1 +e0826 b H16 1 +e0827 b R8 1 +e0831 b D8 1 +e0836 w R16 P16 2 +e0857 b C17 1 +e0881 w O14 1 +e0886 b B4 1 +e08a0 w F7 1 +e08a7 w R13 1 +e08b7 w M18 1 +e08c6 b N17 1 +e08c7 w Q14 P13 2 +e08e1 b E17 1 +e08f7 b Q7 1 +e0911 b F4 B3 2 +e0911 w O4 1 +e0971 w C3 1 +e09c1 b R16 1 +e09e0 w R15 1 +e0a07 w N15 1 +e0ab6 b P16 1 +e0ac6 b P3 1 +e0ae7 b N4 1 +e0b01 w R15 1 +e0b30 w E4 1 +e0b46 w E15 1 +e0b87 w N18 1 +e0ba0 w G5 1 +e0bc6 b G15 1 +e0bc7 b C13 1 +e0bc7 w M6 1 +e0bd6 w Q16 1 +e0bf1 w P15 1 +e0c10 w C15 1 +e0c16 w N17 1 +e0c17 b P1 1 +e0c31 b D6 1 +e0c31 w O2 1 +e0c46 b P4 1 +e0c91 w C5 1 +e0cc0 b P14 1 +e0cd1 b F4 1 +e0cd6 w Q15 1 +e0d11 b Q6 1 +e0d11 w Q7 Q6 2 +e0d97 w S5 1 +e0da6 b E3 1 +e0db0 b E4 1 +e0df1 b O8 Q6 2 +e0df7 b C17 1 +e0e16 w D15 1 +e0e46 w M2 1 +e0e50 w P4 1 +e0e80 w E17 1 +e0ea0 w Q3 1 +e0ea1 b D2 1 +e0ee0 b S17 1 +e0ee7 b D4 1 +e0ef1 b R18 F4 2 +e0f06 w R15 1 +e0f10 w Q2 C15 2 +e0f36 w R16 1 +e0f41 b Q15 1 +e0f60 b R5 1 +e0f70 w M5 N5 P3 P5 4 +e0fd1 b F3 1 +e0ff0 b B17 1 +e1021 w R12 1 +e1076 b C17 1 +e10a1 w D5 1 +e10d0 w E18 1 +e1100 b E16 1 +e1117 b S7 1 +e1126 w E17 1 +e1137 w R16 1 +e1146 w R9 1 +e11c7 b N3 1 +e1247 w C4 1 +e1277 b R3 1 +e1290 b E17 1 +e12a1 b C5 1 +e12a6 b C3 1 +e12a7 w B14 1 +e12b0 w P4 1 +e12d7 w Q16 1 +e12e6 b N4 1 +e1330 w B17 1 +e1370 w F2 1 +e1377 b C12 1 +e13a6 w C13 1 +e1417 w G4 1 +e1427 b R2 1 +e1440 w E2 1 +e1441 b G6 1 +e1447 b O3 1 +e1451 w Q9 1 +e1466 b Q15 1 +e1487 w C17 1 +e14d1 b R4 1 +e14d1 w R3 Q6 Q4 P4 O5 P5 P7 Q7 O3 N3 N4 11 +e1526 w E3 1 +e1537 w E7 1 +e1561 w G14 1 +e1570 b D12 1 +e1581 b R18 1 +e1586 b B13 1 +e15b0 b E14 1 +e15c6 w G16 1 +e15e0 w N4 1 +e1606 b Q14 1 +e1666 b L3 1 +e1696 w C16 1 +e16b6 w Q7 1 +e16c6 b R14 1 +e16f1 b G4 G17 2 +e16f7 b Q7 1 +e1706 w R3 1 +e1740 w C3 C8 2 +e1741 w N2 1 +e1751 b B4 1 +e1757 b E6 1 +e1760 b Q3 1 +e1766 w C8 1 +e1771 w P15 R15 2 +e1791 b D2 1 +e17a0 b D2 1 +e17a1 b C18 1 +e17a6 b M17 1 +e17c0 w D6 1 +e1841 b G3 1 +e1851 w B15 1 +e1876 w T3 1 +e18d0 b C3 1 +e18e1 w T5 1 +e1980 b F2 1 +e1996 w R9 1 +e19b6 b Q5 1 +e19d6 w Q13 1 +e19e0 b Q17 1 +e19e6 b D15 1 +e19f6 b N2 1 +e1a07 b N18 1 +e1a20 b B4 D6 2 +e1a26 b S15 1 +e1a30 b O14 1 +e1a56 b H4 1 +e1a76 b R16 1 +e1a97 b F6 1 +e1aa1 w P3 1 +e1ac1 b M2 1 +e1ac7 w C4 1 +e1ad6 b R6 1 +e1af1 b C3 C2 2 +e1b11 b F6 1 +e1b11 w L3 N3 2 +e1b30 w C7 1 +e1b37 b E14 1 +e1ba0 w E6 1 +e1bd6 b D3 1 +e1c16 w N3 1 +e1c30 w C3 1 +e1c46 w P14 1 +e1c87 b P18 1 +e1cc6 b R19 1 +e1ce1 w C16 1 +e1d16 b P2 1 +e1d21 b R18 O18 2 +e1d31 b S16 1 +e1d47 w Q2 1 +e1d50 w O17 1 +e1d51 b S15 1 +e1d71 w C5 1 +e1d77 b D18 1 +e1d80 b P2 1 +e1d87 w N17 1 +e1da0 b G18 G15 2 +e1db7 b F13 1 +e1dc7 b R14 1 +e1dd1 b N16 1 +e1df1 w S7 1 +e1e30 b C2 1 +e1e51 b H18 1 +e1eb0 b O16 R17 2 +e1ec0 w E14 1 +e1ed7 b D2 1 +e1ee0 b Q11 1 +e1f00 w L3 1 +e1f26 w J18 1 +e1f46 b C7 1 +e1f61 b Q8 1 +e1fb0 w D4 1 +e1ff7 b R4 Q2 2 +e1ff7 w R8 1 +e2037 w S15 1 +e2056 b D5 1 +e2071 b Q14 1 +e20a0 b D14 1 +e20b1 b P4 1 +e20b1 w J4 1 +e20b6 b C4 1 +e20b6 w P4 1 +e20e7 b J18 1 +e2100 w E18 1 +e2117 b P12 1 +e2147 b O18 1 +e2157 b R15 1 +e2157 w M5 1 +e2187 b C3 1 +e21d7 w B16 1 +e21e0 b Q7 1 +e2200 b P18 1 +e2207 b H16 1 +e2231 b H17 1 +e2237 b P15 1 +e2286 b D15 1 +e22d1 b E4 1 +e22e0 b H16 1 +e22f1 w R6 1 +e22f6 w G16 1 +e2300 w P16 1 +e2301 b R16 1 +e2310 w E5 D7 2 +e2311 b B4 1 +e2327 b R6 1 +e2351 b D12 1 +e2356 w H5 1 +e2357 b D4 1 +e2361 b C18 1 +e2361 w N4 1 +e2387 b D16 1 +e2396 b E5 1 +e2397 w O6 1 +e23e7 w E14 1 +e2420 b H3 1 +e2426 w Q5 1 +e2430 b G15 1 +e2456 b P13 1 +e2460 w D18 1 +e2490 w D13 1 +e24b1 b N16 Q16 2 +e24b1 w C12 1 +e24b6 w R3 1 +e24c6 b R17 1 +e2516 b S3 1 +e2536 b N5 1 +e2540 w D14 1 +e2556 b C11 E13 2 +e2580 w E17 F14 2 +e2586 w C17 1 +e25a1 b C5 1 +e25b1 b S4 1 +e25d1 b S7 1 +e25e0 b D15 1 +e2621 b N2 1 +e2630 b T16 1 +e2650 b O17 1 +e2697 w C15 1 +e26b0 w O4 O3 2 +e26c1 b C15 1 +e26d1 w R14 1 +e26d7 w B3 B6 2 +e26e1 w D15 D14 2 +e26f1 w E18 1 +e2706 b D6 1 +e2740 b C1 1 +e2771 w G15 1 +e27c1 w M17 1 +e27c7 w R6 Q4 2 +e2821 b B12 1 +e2837 b Q7 1 +e2861 b N4 1 +e2881 b E3 1 +e2890 b P3 1 +e28f6 w S3 1 +e2911 w C3 D8 2 +e2936 w S4 1 +e2946 w R15 R18 2 +e2947 b D8 1 +e2976 w C17 1 +e2980 b Q3 1 +e2997 w P16 O15 2 +e29c1 w E6 1 +e2a10 w R4 1 +e2a40 w G16 1 +e2a76 w G16 1 +e2a90 b P4 1 +e2ac6 w Q5 O4 2 +e2af7 b C16 1 +e2af7 w C17 D16 E16 F15 F16 G16 6 +e2b17 w A4 1 +e2b31 w D4 1 +e2b46 b C17 1 +e2b50 w Q2 1 +e2b56 w E3 1 +e2b70 w O16 1 +e2b86 b Q7 1 +e2be0 w H17 1 +e2c06 b E17 1 +e2c16 b D12 1 +e2c61 b Q5 1 +e2c66 w H3 1 +e2c90 b E6 1 +e2cd0 b M5 1 +e2d07 w C12 1 +e2d56 w O15 1 +e2d61 b B13 1 +e2d66 w D13 1 +e2d81 w R17 1 +e2d97 b R17 1 +e2da0 w N2 1 +e2dc0 b P3 1 +e2e01 w O5 1 +e2e21 b R17 1 +e2e41 b B5 1 +e2e91 b E3 1 +e2eb1 b P18 1 +e2ef7 w Q2 1 +e2f07 b R15 1 +e2f41 b D5 1 +e2f47 b M4 1 +e2f77 w G4 1 +e2f80 w Q15 R13 2 +e2fa7 w B14 1 +e3017 w S5 1 +e3020 b F3 1 +e3027 b M3 1 +e3080 b O14 1 +e3096 b R12 1 +e30a1 w P16 1 +e30c1 w C17 1 +e3106 b R18 G3 2 +e3121 b E3 1 +e3146 w P18 1 +e3156 w R12 1 +e3167 w E18 1 +e3171 w D3 1 +e3197 w B5 1 +e31a7 b Q7 Q4 2 +e31b7 w R16 1 +e31c1 b Q14 1 +e31d1 b P14 1 +e3201 w N2 1 +e3206 b B14 1 +e3257 w Q3 1 +e3267 w P3 O3 O4 P4 4 +e3296 w D3 1 +e32a7 b O3 O2 2 +e32c7 w G4 1 +e32d7 b D16 1 +e32d7 w R4 1 +e32e1 b R16 1 +e3317 w T17 1 +e3341 b H18 1 +e3371 w D17 1 +e33a7 b Q5 P5 N4 R3 4 +e33a7 w P4 1 +e33b0 w D15 1 +e33f6 w R16 Q15 Q11 3 +e3436 b E4 1 +e3441 b P14 1 +e3476 w O5 1 +e3486 b P15 1 +e34a0 b C16 1 +e34a0 w S6 1 +e34c7 w G15 1 +e34e6 b F14 1 +e3500 w L17 1 +e3521 b S4 1 +e3550 b N3 1 +e3566 b M3 1 +e35b1 w R8 1 +e3610 b E6 1 +e3616 b H16 1 +e36a7 b D3 1 +e36d1 b Q18 1 +e36f7 b S2 1 +e3710 w C5 1 +e3731 w C8 1 +e3737 w B2 1 +e3751 b Q6 1 +e3761 w F16 1 +e37c7 w F18 1 +e37e1 w N3 1 +e37f7 w L15 1 +e3807 w C4 1 +e3816 w O7 1 +e3820 w D13 1 +e3830 w F17 1 +e3837 b P17 1 +e3840 w L4 1 +e3870 w E4 E3 2 +e3890 b R18 1 +e38a0 w O15 1 +e38d0 b F4 1 +e38e0 b D6 1 +e38e6 w C17 1 +e3920 w T5 1 +e3926 w D5 1 +e3960 b S19 1 +e3981 b B4 1 +e39d1 w R11 R13 2 +e3a60 b S5 1 +e3a77 b S19 1 +e3a97 b Q16 1 +e3aa1 b M4 1 +e3ab0 w C4 D4 C7 E5 C3 5 +e3b10 b R7 1 +e3b36 b O5 1 +e3b77 b S16 1 +e3b80 b E4 1 +e3b87 b B6 1 +e3bf7 w Q15 1 +e3c00 b E3 1 +e3c21 b B15 F18 2 +e3c26 b N15 F6 2 +e3c41 w R8 1 +e3c51 w F5 1 +e3c66 w G14 1 +e3cd0 b O4 1 +e3cd6 b N5 1 +e3d01 w B5 1 +e3d07 b Q15 1 +e3d07 w P18 1 +e3d17 b G2 1 +e3d20 b H17 1 +e3d50 w D12 1 +e3d56 w E5 1 +e3d61 w R12 1 +e3da1 b O16 S17 2 +e3df0 w E3 1 +e3e00 b P6 1 +e3e26 b P4 O3 2 +e3e40 w O3 1 +e3e56 b M3 1 +e3e66 b C6 1 +e3e80 w G15 1 +e3e86 w R12 1 +e3ed1 w D16 1 +e3f01 w C15 1 +e3f06 w Q19 1 +e3f20 w D4 1 +e3f26 b R16 1 +e3f30 w D4 1 +e3f47 b S2 1 +e3f71 w G16 1 +e3f81 b P4 1 +e3fa1 w B17 1 +e3fd0 b D15 1 +e3fd1 b B4 1 +e3fd1 w F3 1 +e3fd6 w F4 1 +e3fd7 w B6 1 +e3fe1 w O15 1 +e4006 w L6 1 +e4047 w R2 1 +e4050 b D15 1 +e4071 w B4 1 +e4086 b R6 1 +e40d6 b E16 1 +e40d7 w N4 1 +e40e7 b C13 1 +e4101 b D14 1 +e4101 w R2 1 +e4156 w E2 1 +e4167 w C15 1 +e4171 b B5 D17 2 +e4177 b O5 Q6 2 +e41e0 w Q7 1 +e4200 w R17 1 +e4207 b O4 1 +e4221 b R17 1 +e4247 b S9 1 +e4247 w C4 1 +e4251 b T16 1 +e4260 w H6 1 +e42e6 w D14 1 +e42e7 b S14 1 +e42f1 w C13 1 +e4321 b F4 B4 2 +e4350 w C2 1 +e4387 b G17 1 +e43a0 w M4 1 +e43a7 b C7 1 +e43b7 b C11 1 +e4400 b Q14 P16 2 +e4401 b B13 1 +e4421 w R14 1 +e4427 b S2 1 +e4427 w P8 1 +e4450 b G12 1 +e4496 w N18 1 +e44a0 w E12 N17 2 +e44d0 w R3 R8 2 +e4511 w E16 1 +e4537 w C3 1 +e4540 b Q9 1 +e45b7 b Q3 1 +e45b7 w B2 1 +e4606 w D18 1 +e4607 w D4 1 +e4666 w P3 1 +e4670 w B15 1 +e4676 b E3 1 +e46e0 b D6 D7 2 +e4720 b L3 1 +e4740 w S18 1 +e4757 w P15 1 +e47a1 w S3 1 +e47e0 b D12 1 +e47f7 b L3 1 +e4800 w P15 H6 2 +e4810 b C6 1 +e4816 b C5 1 +e4840 w E14 1 +e4856 w H3 1 +e48a1 b R12 1 +e4946 w D6 1 +e4956 b B17 1 +e4986 w G5 B6 2 +e49c6 b B6 1 +e4a00 w O18 1 +e4a11 b C6 1 +e4a17 w N14 1 +e4a40 b H5 G5 E3 E5 4 +e4a47 b B17 1 +e4a60 b E7 1 +e4a70 w S18 1 +e4a90 b F4 1 +e4aa7 b D15 1 +e4ae7 w S8 1 +e4b01 b S12 1 +e4b07 w P12 1 +e4b26 b B15 1 +e4b27 b S15 1 +e4b27 w P17 R14 2 +e4b50 w D6 1 +e4b81 b Q4 1 +e4b96 w C14 1 +e4bd0 b P17 O14 M5 M6 4 +e4be1 w E16 1 +e4c37 b N17 1 +e4c41 w R16 1 +e4c66 b O18 1 +e4c66 w Q14 1 +e4c91 w R6 1 +e4c96 b O6 1 +e4ca7 b G16 1 +e4cc6 b Q18 1 +e4d07 b R14 1 +e4d40 b O15 1 +e4d56 w F16 1 +e4d57 w N17 1 +e4d60 b G4 1 +e4d67 w C11 F15 2 +e4d76 w B3 1 +e4d81 b Q16 1 +e4da7 w G4 1 +e4dc0 w D12 1 +e4de1 b P17 1 +e4df1 b C18 1 +e4e37 w Q5 1 +e4e40 w P2 R3 Q6 R6 4 +e4e46 w P3 1 +e4e76 w P17 1 +e4e87 b B3 1 +e4eb0 b B18 1 +e4eb6 b S16 1 +e4ed1 b H4 1 +e4ee0 w E5 1 +e4ef0 w N16 1 +e4f66 b Q8 1 +e4f70 w C12 1 +e4f76 b R14 1 +e4ff6 b D16 1 +e5057 w B5 1 +e5086 w Q14 1 +e5097 w D15 1 +e50b1 b D3 1 +e50f6 b D7 N15 N14 3 +e5157 b F6 1 +e5171 b S12 1 +e5191 w S4 1 +e5196 w C17 1 +e51d7 w R16 1 +e51e6 w G18 1 +e51f6 w B18 1 +e5216 w R2 1 +e5241 b G16 1 +e5247 b P2 1 +e5267 w F18 1 +e5280 w R3 1 +e52b1 w O17 1 +e52d0 w N14 1 +e52d6 b P8 1 +e5311 w Q15 1 +e5320 b Q5 1 +e5321 b L16 1 +e5330 b F3 E6 2 +e5346 b E16 1 +e5346 w E16 1 +e5357 b A15 1 +e5380 b J17 1 +e5386 b R2 1 +e53a0 w F15 1 +e53a7 b R3 1 +e5416 b D7 1 +e5416 w D13 1 +e5441 b B5 1 +e5460 b D3 1 +e54c7 w O14 1 +e54f1 w P5 1 +e5516 w B18 1 +e5530 b P18 1 +e5550 b F16 1 +e5556 b M5 1 +e5570 w Q15 1 +e5581 w C17 Q16 H16 3 +e55e6 w F15 1 +e5600 b E13 G16 2 +e5601 b R2 1 +e5601 w D3 1 +e5607 b B17 E11 D17 3 +e5650 b C17 1 +e5656 b P6 1 +e56a0 b T4 1 +e56a1 b C3 1 +e56b0 b R18 1 +e56b7 b E16 1 +e5761 w B7 1 +e5787 b G16 1 +e57c6 w O7 1 +e5810 w P4 1 +e5886 w Q6 1 +e58b0 b H5 1 +e58f0 w S17 1 +e5901 w M15 1 +e59a0 b P3 R14 2 +e59a0 w Q14 R14 R13 O16 M17 M16 L16 7 +e59b1 b R12 1 +e5a07 b B18 1 +e5a41 w E14 1 +e5a51 w R13 1 +e5a56 b O11 1 +e5a60 w R14 1 +e5a67 b B4 1 +e5ab6 b B5 1 +e5ab7 w B18 1 +e5ad0 b P17 1 +e5af7 b O7 1 +e5b10 b M5 1 +e5b26 b N17 S15 2 +e5b27 w R6 1 +e5b50 w G17 1 +e5b57 b E3 1 +e5b97 w F4 1 +e5bb1 w Q16 P17 R15 O16 Q14 5 +e5bc7 b C5 1 +e5bf0 w R13 1 +e5c16 b O4 1 +e5c17 b N3 1 +e5c30 w P1 1 +e5c57 w E2 1 +e5cb0 w C15 1 +e5cc0 w O4 1 +e5cd7 w D16 1 +e5ce6 b S13 1 +e5ce6 w B6 1 +e5d47 b P15 1 +e5d67 b D13 1 +e5da1 b C8 1 +e5da7 b Q11 1 +e5db1 w C7 1 +e5dc1 b B6 1 +e5df0 b P5 1 +e5df6 b C2 1 +e5e10 b G4 1 +e5e27 b E2 1 +e5e36 b D6 1 +e5e56 w R8 1 +e5e97 b H16 1 +e5eb6 b O3 1 +e5ed0 b O7 1 +e5f10 w O16 O17 2 +e5f37 w D14 1 +e5f51 w Q2 1 +e5f57 b D17 1 +e5f57 w D17 1 +e5f61 w E17 1 +e5f70 b G17 1 +e5f81 w S16 1 +e5f91 w D7 D4 2 +e5f96 b O18 1 +e5fa1 b D14 1 +e5fd0 b D6 1 +e5ff7 b O4 1 +e6010 w G3 1 +e6040 w Q12 1 +e6086 w G16 1 +e6087 w B4 1 +e6096 b R2 1 +e6096 w J18 1 +e60a7 w F17 1 +e60b7 b O12 1 +e60f7 w C4 Q4 2 +e6100 b F4 G4 2 +e6117 b D17 1 +e6127 w O4 1 +e6131 w S17 P17 O16 N17 N18 5 +e6137 w C13 1 +e6157 w D3 1 +e6171 b L14 1 +e6177 b F14 1 +e6187 w B16 R17 2 +e61a1 w R2 1 +e61f0 b H3 1 +e6206 w G12 R18 2 +e6211 b O14 1 +e6227 b G2 1 +e6230 w E16 1 +e6246 b R16 S15 2 +e6257 b M3 1 +e6270 w D2 1 +e6277 b D16 1 +e6286 b G5 1 +e6287 b Q12 1 +e62c1 b C17 B15 D13 3 +e62e7 b C3 1 +e6300 b N4 1 +e6310 b D14 1 +e6320 b P2 1 +e6371 b P2 1 +e63a1 b Q2 1 +e6406 w H18 1 +e6410 b C17 1 +e6416 w R6 1 +e6431 w S15 1 +e6440 w O4 1 +e6441 w R8 1 +e6450 b O4 1 +e6470 b A5 1 +e6486 b R4 1 +e6487 w C7 1 +e6497 w D13 E17 2 +e64d6 b C4 1 +e64f7 w Q18 1 +e6500 w B16 1 +e6506 b P16 1 +e6521 w G18 1 +e6530 w F4 F3 2 +e6551 w Q14 1 +e65c1 b C4 1 +e6676 b P5 1 +e66b0 w B2 1 +e66c0 b C5 1 +e66f1 w D17 1 +e6736 b O16 O17 R15 P15 R13 Q13 Q12 7 +e6736 w R17 P17 O16 N16 N17 5 +e6767 b T16 1 +e6781 w D4 1 +e67a7 w P7 1 +e6806 b M6 1 +e6807 b S16 1 +e6807 w P4 1 +e6841 w O18 1 +e6850 b Q2 1 +e6850 w L13 1 +e6877 w E6 1 +e68a1 b C17 1 +e68b7 b N4 1 +e68e7 w S17 1 +e68f1 b P15 1 +e68f7 b D14 1 +e6911 w D16 1 +e6941 b R2 B17 2 +e6976 w R17 1 +e6980 b P5 1 +e6987 w R5 S6 S5 3 +e69a0 b E2 1 +e69a0 w F4 1 +e69e6 b F5 1 +e6a30 w L4 1 +e6a40 b E8 1 +e6a50 w H15 G15 E17 E15 4 +e6a57 w R8 1 +e6a87 w R8 1 +e6a97 b D2 1 +e6ab0 b N6 Q3 2 +e6ab7 w R18 1 +e6ac0 b Q3 1 +e6ac7 w D3 1 +e6ae6 w R12 1 +e6b01 b P16 1 +e6b16 b D2 1 +e6b50 w R7 1 +e6b90 b D7 1 +e6bb6 b R13 1 +e6c46 b N6 1 +e6c66 w P17 1 +e6cb6 b O5 1 +e6cc0 b S6 1 +e6cd0 b S17 1 +e6d11 w B18 1 +e6d17 w B17 1 +e6d40 b F14 1 +e6db1 w C17 1 +e6df6 b R2 1 +e6df7 b P3 O3 P4 Q5 4 +e6df7 w Q4 1 +e6e01 w P7 G2 2 +e6e17 w F13 1 +e6e97 w O2 1 +e6eb7 w D3 1 +e6ec0 w S4 1 +e6ec1 b F17 1 +e6ec6 w B12 1 +e6ed7 w D16 1 +e6f00 b G6 D3 2 +e6f06 b D3 1 +e6f10 w E14 1 +e6f40 b P14 1 +e6f40 w M17 1 +e6f71 b B16 1 +e6f71 w L4 1 +e6f77 b F5 1 +e6fa7 b G16 1 +e6ff0 b C7 1 +e7017 w Q7 1 +e7037 b M15 1 +e7047 w N3 1 +e7056 b C17 1 +e7060 b N4 1 +e7081 w E14 1 +e70a0 b E13 1 +e70a6 w R6 1 +e70a7 b N5 1 +e70e0 b B16 1 +e70e7 w Q3 1 +e70f6 b D15 1 +e71f7 w B15 1 +e7226 b N16 1 +e7257 w S4 1 +e7287 w R3 1 +e72b1 b S5 1 +e72e6 b H2 1 +e72e7 w F8 1 +e72f0 w E7 1 +e7311 b G4 H15 2 +e7326 b E2 1 +e7340 w S5 1 +e7357 w C18 1 +e7391 b E17 F17 F16 E16 4 +e73b6 b R3 1 +e73b6 w M3 1 +e73b7 w Q15 1 +e73d7 w F17 1 +e7436 b S4 1 +e7436 w E6 1 +e7470 b D12 1 +e7491 b S17 1 +e74a1 w J5 C4 2 +e7510 w Q17 1 +e7537 b P6 1 +e7546 b D14 1 +e7567 b P14 1 +e7581 b B16 1 +e75e7 w R17 S17 2 +e75f6 b G3 D2 2 +e7671 w R16 1 +e7680 b P7 1 +e7720 b Q12 1 +e7757 w S16 1 +e7770 w C3 1 +e77a1 w N14 1 +e77a6 b S6 1 +e77e1 w G3 1 +e7841 b Q16 Q18 2 +e7856 w S17 1 +e78a0 w O3 1 +e78f1 b C2 1 +e7910 w D15 1 +e7931 b C2 1 +e7956 w R17 1 +e7970 b O17 1 +e79c1 b E5 1 +e7a06 b O7 1 +e7a50 w D7 1 +e7a51 w D15 1 +e7a76 w P15 1 +e7a87 b Q3 1 +e7a90 w B4 1 +e7ae0 b E15 1 +e7ae7 b B7 1 +e7b11 b B16 1 +e7b20 b E6 F4 2 +e7b41 b E15 1 +e7ba6 w D3 1 +e7bc1 w C9 1 +e7bd6 b D14 1 +e7bf1 b B3 1 +e7c00 w F17 1 +e7c01 w G16 1 +e7c10 b H14 1 +e7c36 w F5 1 +e7c37 b C14 1 +e7c40 w P12 1 +e7c76 w O7 1 +e7c80 b F5 1 +e7c87 b C3 1 +e7d07 b B2 1 +e7d20 b R12 1 +e7d60 w Q17 1 +e7dd7 b H4 1 +e7e06 w O13 1 +e7e07 b S1 1 +e7e16 w N16 1 +e7e27 b A18 1 +e7e66 w R3 1 +e7e97 w C18 F18 2 +e7ea6 w Q6 1 +e7ea7 b B15 1 +e7eb0 b P4 N3 2 +e7f10 b P7 1 +e7f26 w F3 F4 2 +e7f41 b G6 1 +e7f41 w D8 1 +e7f46 b E15 1 +e7f57 w R17 1 +e7f61 b R18 1 +e7fa0 w C2 1 +e7fa1 b D17 D15 2 +e7fc0 b P5 D6 2 +e7fc6 b P2 1 +e8040 b D5 1 +e8047 w O3 1 +e8070 w O15 1 +e8071 b Q12 M16 2 +e8071 w D16 1 +e8096 w S7 1 +e80c1 b E5 1 +e80f1 w P14 O14 P17 O17 4 +e80f6 b P5 1 +e8106 b F12 1 +e8136 b E16 1 +e8161 w P11 1 +e8177 w C4 1 +e81c7 b L18 1 +e81e1 b B15 1 +e8206 b P5 1 +e8210 b E2 1 +e8216 w E16 1 +e82b6 w S15 1 +e82d7 b C7 1 +e82e1 w P6 1 +e82e7 w S3 1 +e82f1 w B16 1 +e8300 w F6 1 +e8301 b G4 1 +e8330 w O16 1 +e8371 b N16 1 +e8390 w B16 D16 2 +e83a1 b S15 1 +e83b6 w N9 1 +e83d1 w Q5 1 +e83d7 w O18 1 +e83e7 b B16 1 +e8407 w C11 C13 2 +e8436 w O4 1 +e8441 b D16 1 +e8456 b E15 1 +e8487 b D17 1 +e8487 w B7 1 +e84b1 w B5 1 +e8507 b N16 O14 2 +e8507 w O18 1 +e8546 w S3 1 +e8556 w N17 1 +e8557 b Q4 O4 2 +e8560 b E4 1 +e8567 w P3 1 +e8570 b R3 1 +e8580 w C8 1 +e8656 b Q3 1 +e8657 w C17 D13 2 +e8666 w C12 1 +e8676 w D15 1 +e8681 w C4 1 +e86a6 w C7 E5 2 +e86c0 b B5 D5 2 +e86e1 b E5 1 +e8781 w R15 1 +e87c1 w N16 1 +e87f6 b M3 1 +e8810 w M6 1 +e8821 b G4 1 +e8836 w O17 1 +e8837 w P18 1 +e8847 b O3 1 +e8851 w R17 1 +e8857 b Q12 C12 2 +e8857 w N14 N13 2 +e88a1 b E18 1 +e88a6 w B6 1 +e88c7 w E3 E15 2 +e88e6 b Q5 1 +e88e7 w G17 1 +e8900 w S16 1 +e8911 b F13 1 +e8917 b O16 N15 2 +e8940 b D14 1 +e8940 w P8 B5 2 +e8966 w F16 1 +e8970 w H4 1 +e89a1 w C2 1 +e89b6 b C3 1 +e89c6 b Q16 1 +e89c7 w N13 1 +e89e1 b R18 1 +e8a07 w O2 1 +e8a77 b B19 1 +e8ae0 w O16 N16 2 +e8af7 w C13 1 +e8b06 b F15 1 +e8b67 w N16 1 +e8b71 w D12 1 +e8bc7 w Q3 1 +e8be0 w H16 1 +e8bf0 b F16 1 +e8c20 w R17 1 +e8c60 w O4 1 +e8c77 b R16 1 +e8c96 w O4 1 +e8ca6 w R14 Q15 2 +e8cb0 b E2 1 +e8ce7 b Q17 1 +e8d10 b P4 1 +e8d27 w C18 1 +e8df6 b P6 S17 2 +e8e71 w R2 1 +e8e96 w R2 1 +e8ea1 w Q3 1 +e8eb6 w F16 1 +e8ed7 b P6 1 +e8f37 w C14 1 +e8f40 b P4 1 +e8f70 b M16 1 +e8f77 b F18 1 +e8f87 w S3 1 +e8f90 w E2 1 +e8fc6 w O4 1 +e8fe1 w T15 1 +e8ff1 w M4 1 +e9001 w D13 C17 2 +e9050 w D16 1 +e9051 b D2 1 +e9061 b J16 1 +e9066 w D8 1 +e9067 w E18 1 +e9076 w P18 1 +e90b7 w D14 1 +e90c1 b C16 1 +e90c6 w B4 1 +e90f0 w N18 1 +e9150 b E2 1 +e9156 b Q2 1 +e9171 b C17 1 +e9187 w O5 1 +e91a0 w C9 1 +e91a6 b D15 1 +e91b7 w R17 1 +e91c7 w O17 1 +e91e0 b D4 1 +e91e0 w D5 1 +e9290 b Q17 1 +e92c7 b E14 1 +e92d6 b C5 1 +e92f0 w P13 1 +e9311 w Q13 O14 2 +e9331 b R2 1 +e9390 b E13 1 +e93a1 b R3 1 +e93c0 b E16 1 +e93c1 w D13 1 +e93e0 w E2 1 +e93f1 b Q18 1 +e9431 b C15 G18 Q3 3 +e9436 b D7 1 +e9447 b D4 1 +e9447 w E13 1 +e9450 b N16 1 +e9456 b J6 1 +e9461 w F14 1 +e9486 w S16 1 +e9497 w B8 1 +e94a6 b C17 1 +e94e1 b Q14 1 +e94e7 b R17 Q12 2 +e9520 b E8 1 +e9551 w J3 E6 E7 3 +e9577 w H3 1 +e9590 w E15 1 +e95a0 w D5 1 +e95b6 w Q16 1 +e95d7 b E7 1 +e95d7 w Q2 1 +e95f0 b F6 1 +e9611 b D14 1 +e9637 b R6 1 +e9650 w E18 1 +e96a1 w D3 1 +e96c0 w C14 1 +e9700 w N15 1 +e9747 b F16 E15 2 +e9747 w R12 F17 2 +e9756 w E6 1 +e9781 b Q7 Q4 2 +e97c0 b O16 1 +e97d0 b P18 1 +e97e6 w B4 E4 2 +e9810 b S13 1 +e9810 w Q15 1 +e9840 w D17 1 +e9877 w E3 F3 F4 E4 4 +e9897 w E16 1 +e98b0 b P5 1 +e98c0 w S3 1 +e98e6 b B9 1 +e98e7 w G17 1 +e98f6 w R18 1 +e9950 b P16 1 +e9957 b N5 1 +e9987 b E2 1 +e9997 b S17 P17 2 +e99a0 b N5 1 +e9a11 w T14 1 +e9a61 b C4 1 +e9a87 w E16 1 +e9ae0 b L4 1 +e9ae1 w C9 C7 2 +e9b11 b F16 1 +e9b37 b P2 1 +e9b70 w B13 1 +e9bd6 b P17 1 +e9be1 b Q13 1 +e9be6 w C13 1 +e9c01 b D14 1 +e9c06 w F5 1 +e9c36 w R7 1 +e9c67 b Q1 1 +e9c81 b Q4 1 +e9c91 w B2 1 +e9cb0 b C11 1 +e9cb0 w D13 1 +e9cd6 w E7 1 +e9ce0 w Q6 P4 2 +e9ce7 b O17 1 +e9d27 b A14 L4 2 +e9d37 b S13 1 +e9d37 w D2 1 +e9d50 b N17 1 +e9d56 w B18 1 +e9db7 w E15 1 +e9dc0 b R4 R17 2 +e9de0 b M3 1 +e9e01 b Q2 1 +e9e16 w P15 1 +e9e17 b S3 1 +e9e31 w C13 1 +e9e36 b N3 P3 2 +e9e46 b C14 1 +e9e61 w M3 1 +e9e66 b B5 1 +e9e86 b Q15 1 +e9e90 b Q18 O16 2 +e9eb7 b F17 1 +e9ed1 w C17 D17 C11 C12 B15 C16 6 +e9f11 w C9 1 +e9f17 w A16 E7 2 +e9f67 b D3 1 +e9f67 w Q18 1 +e9f71 b C3 1 +e9f80 w S14 1 +e9fc1 b M3 1 +ea030 b F7 1 +ea036 b R4 1 +ea037 w H5 R16 2 +ea0a6 w C14 D14 2 +ea0b1 b G5 1 +ea0b6 w T4 1 +ea0c1 w L18 1 +ea0c6 b P16 1 +ea0c7 b F3 1 +ea0e0 b P18 1 +ea121 b D7 1 +ea1d0 b C3 1 +ea1d7 w N3 1 +ea1e1 w E1 1 +ea216 b S2 1 +ea217 b O16 R17 2 +ea261 b H14 1 +ea286 w Q6 1 +ea290 w E18 1 +ea2c1 b R14 Q16 2 +ea2d7 b D13 1 +ea2f0 b P13 1 +ea2f1 w B6 1 +ea316 b E4 1 +ea320 b R7 1 +ea326 w S14 1 +ea327 w C2 1 +ea367 b J17 1 +ea3a0 b E8 1 +ea3c0 w N3 1 +ea3e6 w P15 1 +ea421 b F16 1 +ea4b6 b C5 1 +ea4d7 b P3 1 +ea4e1 b C15 1 +ea4f0 w E16 1 +ea507 w O5 1 +ea521 b M14 1 +ea556 b R8 1 +ea586 b G4 1 +ea5b6 b R3 P3 O4 N4 N3 5 +ea5b6 w O4 O3 R5 P5 R7 Q7 Q8 7 +ea5c1 b O18 1 +ea5e1 b C18 1 +ea5f1 b Q16 1 +ea610 b F16 1 +ea620 b P3 1 +ea621 b P17 O17 O16 P16 S12 5 +ea667 b P4 1 +ea677 w G3 1 +ea690 b D15 D14 G17 H17 J17 G16 H16 J16 D7 9 +ea690 w D15 J17 C14 C13 C12 D13 D14 7 +ea6e1 b C16 1 +ea6f1 b D17 1 +ea700 w D14 1 +ea706 b O14 1 +ea741 w L16 1 +ea747 w G4 1 +ea787 b D4 E3 C5 F4 D6 5 +ea7a1 w Q3 1 +ea7c1 w F5 1 +ea806 w D18 1 +ea811 w C17 1 +ea816 w B7 1 +ea817 w B4 1 +ea821 b B6 1 +ea826 b B3 1 +ea840 b M17 1 +ea857 w R12 1 +ea871 w P17 1 +ea890 w P14 1 +ea897 b C11 1 +ea8e6 w P3 O5 2 +ea8f6 w Q2 1 +ea906 b Q4 1 +ea916 b O13 1 +ea936 b M3 1 +ea961 w J17 1 +ea970 w A6 1 +ea9b0 b S3 1 +eaa06 b P14 1 +eaa17 b Q16 O16 2 +eaa60 b F2 S18 2 +eaa76 w N16 1 +eaaa7 w Q11 1 +eab46 w D7 1 +eab50 b H6 1 +eab76 b Q14 1 +eab97 b N6 1 +eabd6 b R5 R2 2 +eabe6 w G18 1 +eac27 b G2 1 +eac41 b F17 1 +eac41 w F16 E15 2 +eac51 b S18 1 +eac70 b C13 1 +eac70 w D14 C14 2 +eac76 w O6 1 +eacc6 w R17 1 +eace6 w P18 1 +eacf0 w F2 1 +ead06 b T4 1 +ead16 b R5 S5 2 +ead41 b F8 1 +ead61 b E2 1 +ead86 w O4 1 +eada7 w E16 1 +eadd1 w M18 1 +eadf6 b Q4 1 +eae10 b C2 1 +eae30 b Q13 1 +eae37 b M3 1 +eae47 w S2 1 +eae71 w C17 1 +eae81 w Q6 P7 2 +eae90 b C12 1 +eaf31 w B5 1 +eaf66 b Q15 1 +eaf67 b D2 1 +eafa7 b P6 1 +eafb1 w B18 1 +eafc6 w D5 1 +eafd6 w E2 1 +eafe0 b N14 1 +eafe1 b H4 1 +eafe6 b S5 1 +eb011 b D4 R17 S15 Q13 4 +eb020 b M3 1 +eb026 w P14 1 +eb027 w G15 1 +eb076 b P2 1 +eb0a7 b F18 1 +eb0b1 b Q17 1 +eb146 w C7 1 +eb156 b R8 1 +eb156 w R16 1 +eb171 b F2 1 +eb181 w Q17 1 +eb1a1 b S14 1 +eb1b6 w S6 1 +eb210 w B18 1 +eb236 w S16 1 +eb271 w G5 1 +eb277 b D16 F16 2 +eb280 b O3 1 +eb286 w O3 1 +eb287 b N14 1 +eb2b6 w B3 1 +eb2c0 b N17 1 +eb2d0 b S5 1 +eb2f0 b O4 1 +eb2f1 b S14 1 +eb330 b F14 1 +eb361 w Q8 1 +eb3b6 b R8 1 +eb3d7 b M15 1 +eb400 w P5 1 +eb421 b S6 1 +eb427 w E15 1 +eb431 b O18 1 +eb431 w L17 1 +eb476 b P16 1 +eb497 b O3 Q5 2 +eb4c7 b R8 1 +eb4d6 b S14 1 +eb510 w E15 G17 2 +eb516 b P16 1 +eb521 b B12 1 +eb577 w Q13 1 +eb587 b O15 1 +eb587 w E6 1 +eb597 b F14 1 +eb5b1 b N7 1 +eb5d6 b G17 E15 2 +eb5d6 w P5 P4 2 +eb606 w R14 1 +eb660 w C3 G4 2 +eb670 b Q5 1 +eb680 b R11 1 +eb697 b E2 1 +eb6f1 b H14 1 +eb731 b D17 1 +eb750 w P14 1 +eb776 w R4 1 +eb787 w E18 1 +eb7a6 w R12 1 +eb7d1 w S15 1 +eb817 b D11 1 +eb827 b Q4 1 +eb836 w J17 1 +eb856 b E12 1 +eb887 b D14 1 +eb8a6 b Q17 1 +eb8a7 b S14 1 +eb8d0 b S16 1 +eb920 w Q8 1 +eb927 b P6 1 +eb946 b F13 Q17 2 +eb950 w M15 M14 2 +eb971 b E8 1 +eb996 w G5 1 +eb9a7 b B6 1 +eb9c0 b C14 1 +eb9c1 b D3 1 +eb9d7 b A2 1 +eba60 b Q17 1 +eba61 b S8 1 +eba81 b Q13 1 +ebaa0 w Q14 Q13 2 +ebaa7 b J3 1 +ebad0 b O16 1 +ebae0 w R15 1 +ebae1 w B15 1 +ebae7 w F3 1 +ebb57 w D11 1 +ebbd6 b R17 1 +ebc00 w C3 1 +ebc06 b S13 1 +ebc50 b C18 1 +ebc56 b Q14 1 +ebc60 w Q6 1 +ebc96 w O16 1 +ebcc0 w M15 N15 P17 P15 4 +ebd20 w E17 1 +ebd37 w S17 1 +ebd41 w S5 1 +ebd86 w S14 1 +ebd96 b O6 1 +ebda0 w R4 1 +ebdb6 b C5 1 +ebde6 b O18 1 +ebe30 b F15 1 +ebe47 b B16 1 +ebe51 b D11 1 +ebea0 b N17 1 +ebea7 w Q15 1 +ebeb6 b R5 1 +ebec6 w R5 1 +ebf10 b O16 O17 N17 Q14 R12 Q12 Q11 7 +ebf10 w O14 O17 2 +ebf21 b D8 1 +ebf31 b N18 1 +ebf31 w Q15 1 +ebf40 b O16 N16 2 +ebf66 b D6 D7 2 +ebf81 b F16 1 +ebf87 w N16 1 +ebfb6 w C2 1 +ebfd1 w C5 1 +ebff6 w D14 1 +ec010 b N3 1 +ec010 w B12 1 +ec066 w R4 1 +ec087 w C7 1 +ec0c1 b R18 1 +ec0f0 b Q12 1 +ec0f6 b T2 D17 2 +ec106 w M3 1 +ec116 b C17 1 +ec181 b R17 1 +ec197 w C16 1 +ec1b1 b P18 1 +ec230 b O2 1 +ec247 w S15 1 +ec260 b D3 1 +ec286 w O3 1 +ec297 w D13 E17 2 +ec2b0 b C6 1 +ec2e7 b C4 1 +ec301 w D3 E4 C3 3 +ec336 b P18 1 +ec346 b P6 1 +ec367 b P5 O3 2 +ec380 b P6 1 +ec381 w R12 1 +ec387 b O15 1 +ec390 w H14 1 +ec3b6 b D3 1 +ec3d6 b P15 Q16 2 +ec3f0 w F16 1 +ec400 w O1 1 +ec476 b Q13 1 +ec487 b P14 1 +ec491 w C12 1 +ec4c6 b F17 E16 2 +ec4d1 b S16 1 +ec4e0 b N16 1 +ec500 w C4 C14 2 +ec507 b D6 C7 2 +ec530 w D6 E6 2 +ec536 w B14 1 +ec547 b C17 1 +ec556 w D6 1 +ec587 w M3 1 +ec5a7 b Q19 1 +ec5c0 b G14 D12 2 +ec647 b D5 E4 2 +ec696 w P4 1 +ec6d0 w D17 E14 2 +ec731 b D17 C17 2 +ec741 b H16 1 +ec761 b P18 1 +ec776 w D6 1 +ec787 w D3 1 +ec7c6 w G3 B5 2 +ec7d7 w E4 1 +ec7f7 w N7 1 +ec850 w E16 1 +ec897 b Q16 Q14 2 +ec8a1 w Q4 1 +ec8c6 b C3 1 +ec906 b C17 E16 2 +ec911 b B11 1 +ec971 w L3 1 +ec9a0 w P15 1 +ec9c1 w D16 1 +ec9d1 b G15 1 +ec9e7 b P2 1 +eca31 w F3 1 +eca47 w Q8 1 +eca50 w R2 1 +eca51 w F2 1 +eca66 w C5 1 +eca71 b C14 1 +eca71 w P18 1 +ecac0 b Q8 1 +ecac0 w F7 1 +ecad7 b H3 1 +ecad7 w L4 1 +ecae0 w R9 1 +ecaf6 w B17 1 +ecb10 w C3 1 +ecb17 b D17 1 +ecb17 w P4 G4 2 +ecb46 w D3 1 +ecb57 w G13 1 +ecb77 w Q2 1 +ecbb1 b P2 1 +ecbb7 w C3 1 +ecbd7 b B16 1 +ecc57 b T3 1 +ecc81 b F14 1 +ecca0 b O11 1 +eccb7 b S17 1 +eccd0 b O2 1 +ecce0 b F16 1 +eccf1 b H6 1 +ecd06 w N15 1 +ecd16 b N16 1 +ecd17 b F7 1 +ecd47 b C12 B15 B16 3 +ecd71 w S14 1 +ecd91 w S16 1 +ecda1 b R6 1 +ecdb7 b Q3 1 +ecdd0 b J3 1 +ece30 w B13 1 +ece31 b G4 1 +ece37 w C16 1 +ece71 w R8 S5 S4 3 +ece90 w D13 D16 E14 3 +ecea7 w D3 C16 2 +eceb6 w S16 1 +ecec1 b Q18 1 +eced7 w E14 C17 2 +ecf07 b N18 1 +ecf17 w G16 1 +ecf21 b P5 1 +ecf51 w M5 1 +ecf56 w B17 1 +ecf90 w S18 1 +ecfc6 w M3 1 +ed017 b R16 Q18 2 +ed056 b S9 1 +ed066 b F4 1 +ed077 b O17 1 +ed0b7 b F4 E5 2 +ed106 b G4 1 +ed106 w F4 1 +ed120 w F16 1 +ed121 w R6 1 +ed1a6 w R6 1 +ed1f0 w J13 F3 2 +ed200 b E4 1 +ed210 b R17 1 +ed227 w C13 1 +ed231 b P3 1 +ed241 w B3 1 +ed246 b G16 1 +ed247 w D14 C18 2 +ed256 b C16 1 +ed2a6 w C15 1 +ed2a7 w B5 1 +ed2e1 b Q18 1 +ed2f0 b S17 1 +ed306 b P2 1 +ed316 b C3 1 +ed357 w G7 1 +ed360 b O4 1 +ed367 w Q18 1 +ed376 w D6 D3 2 +ed3b1 w E15 1 +ed3b6 w D8 1 +ed3f1 w Q3 1 +ed410 b J14 1 +ed420 w R15 1 +ed441 b P15 1 +ed461 w E14 F16 2 +ed481 b D16 1 +ed487 w E3 1 +ed4b0 b O4 R3 2 +ed4b6 w M15 1 +ed4c0 w E13 1 +ed4f6 b Q6 1 +ed550 b E7 1 +ed551 w D4 1 +ed566 b P17 1 +ed567 w S15 1 +ed571 b E18 1 +ed606 w C5 1 +ed610 w N5 1 +ed646 b F11 1 +ed666 w E14 1 +ed676 w C19 1 +ed697 b B16 1 +ed6b6 b R14 1 +ed6c6 b C4 1 +ed6e0 b R3 1 +ed706 b R17 1 +ed717 w F4 1 +ed777 b O17 1 +ed7f0 b F13 1 +ed886 w P14 1 +ed8c1 b C15 1 +ed8c6 b D15 1 +ed956 b B4 1 +ed980 w P13 1 +ed9d1 b P5 O5 R3 N3 4 +ed9e0 b E13 1 +ed9e7 w R3 1 +ed9f6 b O4 1 +eda20 b C11 1 +eda30 w B3 1 +eda80 b P3 1 +edb20 w C13 1 +edb30 w R5 1 +edb80 w D12 1 +edb81 b Q4 1 +edb90 w O14 1 +edbc0 b C4 1 +edbc6 b P13 1 +edbc7 b F18 1 +edbd7 w N6 1 +edc10 w C5 1 +edc17 b D6 E7 2 +edc26 b B3 1 +edc66 b F5 C6 2 +edc87 b Q14 1 +edc90 w R7 1 +edcc0 w D11 1 +edcc7 w B3 1 +edd21 w P16 1 +edd26 b C2 1 +edd40 w F4 1 +edd47 b B3 1 +edda1 w S9 1 +edda7 b F3 1 +eddb0 b C13 E15 F2 3 +eddd7 b B13 1 +ede07 w D15 D14 E16 C15 4 +ede30 w D3 1 +ede46 w L17 1 +ede47 b D7 1 +ede47 w P13 1 +ede60 b D3 C4 2 +ede61 w C3 1 +ede67 b D16 1 +ede91 b B15 1 +edeb7 b B13 1 +edee6 b P4 1 +edee6 w P4 1 +edef0 w D5 1 +edf31 b E3 1 +edf37 b J2 1 +edf41 b E18 1 +edfa0 b R17 1 +edfb1 b Q13 Q16 2 +edfc7 b R6 1 +ee056 b R2 1 +ee067 b R7 1 +ee076 b J17 1 +ee090 w F7 1 +ee096 b F15 G16 E14 3 +ee0c7 b B16 1 +ee110 w D8 1 +ee126 w J14 1 +ee150 b M16 1 +ee196 b P6 1 +ee197 b E6 1 +ee1a1 w S15 1 +ee1a7 b Q13 1 +ee1e1 b R3 1 +ee1e7 w C13 1 +ee1f6 w P4 1 +ee211 w B18 1 +ee230 w S2 1 +ee266 w Q2 1 +ee2a7 w R2 O2 2 +ee2c0 b R6 1 +ee2c1 b R17 1 +ee2d7 b M14 1 +ee2e0 w Q3 1 +ee307 w C16 1 +ee340 w E6 1 +ee341 b O4 N4 2 +ee341 w N4 1 +ee361 w E7 1 +ee367 b F5 1 +ee396 w D7 1 +ee397 w Q19 1 +ee3c1 b R5 1 +ee3d6 w R13 R15 2 +ee3f0 w F3 1 +ee446 w Q5 1 +ee450 w E4 1 +ee451 b B15 1 +ee461 w S6 1 +ee471 b D6 D7 2 +ee497 b P14 1 +ee4c7 w G2 1 +ee4d0 w Q14 1 +ee520 b S6 1 +ee521 w F17 1 +ee537 b C17 D17 C14 3 +ee571 b F5 B4 2 +ee577 b R14 1 +ee597 w Q17 1 +ee5a1 w R11 1 +ee5d6 b R4 1 +ee5e0 w Q12 1 +ee617 w B16 1 +ee636 w N15 1 +ee657 b E14 1 +ee6a7 b J4 1 +ee6b0 w P17 S17 2 +ee6c6 b C4 1 +ee701 w S16 1 +ee727 b S7 1 +ee741 b D3 1 +ee776 b C17 1 +ee776 w R8 1 +ee791 w O3 1 +ee7c6 b P16 1 +ee7c6 w Q3 1 +ee7d1 b B4 1 +ee7e1 w B17 1 +ee7f6 w B15 1 +ee810 b S4 1 +ee857 w R16 1 +ee861 b D5 1 +ee887 b J18 1 +ee887 w F6 1 +ee8a6 b D4 1 +ee8e6 w Q5 1 +ee956 w N4 1 +ee966 w S12 1 +ee971 w N2 1 +ee997 w Q14 1 +ee9c0 b B3 1 +eea27 b M14 1 +eea37 w M4 1 +eea51 w Q17 1 +eea60 b F16 1 +eea70 w P3 1 +eea81 b S4 1 +eeab7 b R18 1 +eead0 w Q15 1 +eead6 b F4 1 +eeae6 w N4 1 +eeaf7 b H14 1 +eeb06 b B14 1 +eeb36 w R4 Q3 2 +eeb50 b R17 1 +eeb91 w G3 1 +eeb96 b D3 1 +eeba6 w E18 1 +eeba7 b R17 1 +eebc7 b F12 1 +eebc7 w Q4 1 +eebe1 w P16 1 +eebe7 b C6 1 +eec26 w D6 1 +eec47 b D15 1 +eec66 w Q12 1 +eec67 b F2 1 +eeca7 w S17 1 +eecd1 b D9 1 +eed00 w P16 P17 2 +eed10 b N3 1 +eed11 w L3 1 +eed16 b C16 1 +eed27 b O4 1 +eed30 b P15 1 +eed41 b G5 1 +eed47 b O16 1 +eed67 w D18 1 +eed71 b F15 1 +eed97 b F18 1 +eeda0 w B2 1 +eeda1 w S11 1 +eede0 w C3 D7 2 +eee66 w O7 1 +eee67 w P6 1 +eeea6 w F6 1 +eeeb6 b F16 1 +eeeb6 w S3 1 +eeee6 b G16 1 +eef20 b S16 1 +eef30 w Q6 D13 2 +eef41 w A17 1 +eef81 b N17 1 +eefa7 b E13 1 +eefd1 b Q4 1 +eefe6 w D14 1 +eefe7 w C18 1 +ef017 b P18 1 +ef036 w C4 1 +ef040 b D8 1 +ef081 b E5 1 +ef096 b C17 1 +ef097 w S3 1 +ef0b6 w C16 1 +ef0b7 w N3 1 +ef0c0 w F4 1 +ef0e0 w E3 1 +ef126 b F6 1 +ef157 w D5 C3 D6 C6 H4 F5 6 +ef200 w D6 B6 2 +ef207 b R14 1 +ef280 w P6 1 +ef297 b Q6 P5 2 +ef2c6 b R7 1 +ef2d6 b C15 C18 2 +ef321 w P2 1 +ef327 w D14 1 +ef340 b C4 D3 2 +ef341 b B8 1 +ef361 b E16 D17 2 +ef367 w C17 1 +ef370 b G4 1 +ef387 b Q3 1 +ef3b1 w R18 1 +ef3e0 b Q8 1 +ef3e7 w R5 1 +ef406 w N2 1 +ef420 w M4 1 +ef427 b R15 1 +ef431 b C7 1 +ef436 b C11 1 +ef440 w G3 E18 2 +ef446 b L18 1 +ef466 b G14 1 +ef476 w N4 1 +ef486 w Q7 1 +ef4a6 w G3 C18 2 +ef4c6 w S15 1 +ef501 w O15 1 +ef531 b G6 1 +ef537 w P6 1 +ef5a0 w E14 1 +ef610 w P3 1 +ef676 b E4 1 +ef676 w N5 1 +ef6f1 w P16 1 +ef706 b N17 1 +ef707 b R4 1 +ef710 w J17 1 +ef770 b R12 1 +ef787 b R4 1 +ef7b6 b D17 1 +ef7e1 w D6 C7 2 +ef7f6 w Q6 1 +ef810 b R17 1 +ef837 b Q11 1 +ef870 w Q15 1 +ef896 b F4 1 +ef8d1 b B15 1 +ef8d6 w M3 1 +ef8e6 w P15 1 +ef8f0 w L18 1 +ef906 b F4 1 +ef910 w R17 1 +ef950 w S3 1 +ef991 b C17 1 +ef996 w H15 1 +ef9a7 b Q16 1 +ef9a7 w B5 1 +ef9b1 b S14 1 +ef9d6 w E13 1 +efa11 b S17 1 +efa20 w R14 1 +efa90 b Q18 P17 2 +efac1 b S3 1 +efb27 b E2 1 +efb40 w G4 D4 F5 3 +efb71 w P2 1 +efb76 w N16 1 +efb81 w E18 1 +efba1 b G16 1 +efbb7 b N6 1 +efbd1 w H6 1 +efbd7 w P14 1 +efc27 b Q12 1 +efc46 w F4 1 +efc57 b R13 R12 2 +efc67 w E18 1 +efc70 w N4 1 +efc91 w N16 O14 2 +efcb1 w Q13 1 +efce1 b B17 J17 2 +efce6 w M7 1 +efcf6 b B3 1 +efd30 w H5 1 +efd37 w P17 1 +efd47 b G14 1 +efd80 b B15 1 +efd91 w C4 P17 P18 Q16 4 +efda7 w D3 1 +efde0 b C11 1 +efdf6 w C3 1 +efe20 b C4 D4 2 +efe26 b O18 1 +efe27 w N3 1 +efe30 b G17 1 +efe36 w R19 1 +efec7 w C2 1 +efee0 b S7 1 +efee6 b C4 D3 2 +efef6 w R5 1 +eff27 b S6 1 +eff36 w E3 1 +eff47 w C16 1 +efff6 b F14 1 +f0006 w C2 1 +f0007 w P5 1 +f0041 w O16 1 +f0086 w P16 1 +f00d1 w N15 F18 2 +f00f0 b D15 1 +f00f1 w P14 1 +f0130 w G4 1 +f0147 w M4 1 +f0156 b C3 1 +f0167 b B15 1 +f0167 w E17 C14 2 +f0177 w E13 1 +f0181 b N7 1 +f01e0 w P16 1 +f01f7 b Q4 Q6 2 +f0216 w G17 1 +f0220 b F3 1 +f0246 w T18 1 +f0251 b F15 1 +f0261 b H16 1 +f0280 b M17 1 +f02a7 w R18 1 +f02e1 w R9 1 +f02f0 b C5 1 +f02f6 b B4 1 +f0336 b E15 1 +f0336 w D13 1 +f0370 w P18 Q17 2 +f0386 b P15 1 +f0397 b D17 N2 2 +f03c6 b D14 1 +f0470 b Q16 1 +f0471 b J4 1 +f04a0 w P6 1 +f04a1 b D7 1 +f04b0 w C3 C5 2 +f04c0 w E15 1 +f04c7 w G17 S6 2 +f04e6 w F16 O4 N4 3 +f0500 b H14 1 +f0510 b O12 1 +f0510 w O6 1 +f0546 b C5 1 +f0557 w P17 1 +f0586 b H18 1 +f0597 b C6 1 +f05b0 b P16 1 +f05b6 b R3 1 +f05b7 b R15 1 +f05c0 b P14 1 +f05c1 b M15 1 +f05c6 b O3 1 +f05d7 w G3 1 +f0601 b J3 G3 2 +f0611 w F17 1 +f0627 b R6 1 +f0637 w Q18 1 +f0641 w C5 1 +f0687 b O16 1 +f06b7 b C17 1 +f06f6 b R3 1 +f0706 b N4 1 +f0746 b N4 1 +f0786 w P5 1 +f07a7 b B6 1 +f07d7 b F15 C17 2 +f07e0 b E17 1 +f07e0 w Q16 1 +f07e7 b B13 1 +f07f0 b R9 O5 Q6 3 +f0826 w Q12 1 +f0831 b Q3 1 +f0847 w N5 1 +f08a7 b C16 1 +f08a7 w A13 1 +f0907 w C17 C18 2 +f0a20 w G16 1 +f0a26 b G2 1 +f0ab0 w S15 1 +f0af7 w Q16 1 +f0b51 b E17 1 +f0b60 w G6 1 +f0b90 b J17 1 +f0b96 b R14 1 +f0ba1 w O18 1 +f0c51 b F4 1 +f0c67 b C17 1 +f0c86 w B5 1 +f0ca6 b B16 1 +f0ce7 w P9 1 +f0cf0 b S15 R17 O16 O17 4 +f0cf1 w R16 1 +f0d01 w O6 1 +f0d07 w C18 1 +f0d17 b F16 G15 2 +f0d17 w B15 1 +f0db1 w O2 1 +f0dd0 w C6 1 +f0de6 w F6 1 +f0df1 b O17 1 +f0df1 w O16 P15 2 +f0df6 w F13 1 +f0e00 b G5 1 +f0e20 b C15 H15 2 +f0e26 b O15 O17 2 +f0e27 b J3 1 +f0e30 w R3 1 +f0e70 b F17 1 +f0e87 b C2 1 +f0e91 w M3 1 +f0ea0 w G17 1 +f0ed0 w C6 1 +f0ef7 b D17 B16 2 +f0ef7 w R16 1 +f0f01 b S3 1 +f0f26 b E15 1 +f0f60 b C6 C3 2 +f0fa1 b O7 1 +f0fa1 w M4 1 +f0fd6 b C3 1 +f0ff0 b P3 1 +f0ff6 w C9 1 +f1007 b E6 F4 2 +f1050 b B14 1 +f1051 b C12 1 +f1066 b Q13 1 +f1097 b Q15 1 +f10d1 b T17 1 +f10d1 w Q17 1 +f10e6 w P4 1 +f10f6 w G18 1 +f10f7 w O5 1 +f1101 w L4 1 +f1106 w Q16 1 +f1120 w S15 1 +f1167 w C14 1 +f1176 w G16 1 +f1190 w R6 1 +f11d0 b E17 1 +f11d7 b B16 1 +f11e0 w N3 1 +f11e6 b S2 1 +f1200 b S7 P7 2 +f1216 w G15 1 +f1217 w S7 1 +f1227 w F18 1 +f1267 w G17 1 +f12c0 w F3 1 +f12c1 w F17 1 +f12e1 b P13 1 +f1310 b F7 1 +f1316 b Q4 1 +f1317 b N17 1 +f1327 w D16 1 +f1346 b R18 1 +f1347 b B14 L16 2 +f1361 w D17 1 +f13a0 w N15 1 +f13a6 b P16 1 +f13e6 b Q6 S5 2 +f13f6 b E15 1 +f1400 w C18 1 +f1417 w Q7 1 +f1441 b D15 1 +f1467 b O18 1 +f1467 w D6 1 +f1497 b S17 1 +f14d0 w F4 1 +f1550 b Q5 1 +f1566 b B5 1 +f1596 w C16 1 +f15d0 w G17 1 +f1641 b P3 1 +f1687 w S3 1 +f16b6 b N3 1 +f16d7 w N3 1 +f16e0 b E5 1 +f16e0 w O16 Q15 2 +f16e7 b P3 1 +f16f0 w P6 1 +f1707 w G18 1 +f1731 w N4 1 +f1736 b M7 1 +f1737 w D12 1 +f1751 b H5 1 +f1797 w Q4 A7 2 +f17a7 w H3 1 +f17b7 b P16 Q15 2 +f17e1 w R17 1 +f17f6 b M3 1 +f1876 b C15 1 +f1887 w P17 1 +f1891 w M5 1 +f18a6 w R6 1 +f18e6 b R17 1 +f18f1 b R16 1 +f18f7 b N18 D16 2 +f18f7 w C2 1 +f1907 b D12 1 +f1911 w C9 F5 G5 3 +f1916 w R13 1 +f1921 b B13 1 +f1927 b N16 R17 2 +f1940 b O2 1 +f1957 w D17 1 +f1966 b D13 1 +f1976 b S5 1 +f1977 b R15 1 +f1997 w R4 1 +f19c0 w R14 1 +f19e7 b R14 1 +f19e7 w D5 1 +f1a10 w O3 1 +f1a30 b F13 1 +f1a66 b B15 1 +f1a71 b N3 1 +f1a96 w P4 Q5 2 +f1ae6 b C5 1 +f1af6 b F15 1 +f1b00 b A16 1 +f1b20 b C18 1 +f1b41 b S16 1 +f1b60 b N3 R6 B17 3 +f1b81 w F15 1 +f1b87 b O17 F3 2 +f1b90 w R3 1 +f1bb0 w S6 1 +f1bf1 w S5 1 +f1c17 w H18 1 +f1c30 w S18 1 +f1c46 b O4 1 +f1c66 b G2 1 +f1c90 b G3 1 +f1c97 w C5 1 +f1ca0 b D4 1 +f1cb6 b O15 N16 P14 3 +f1cc6 w C2 1 +f1cc7 b D9 1 +f1cd1 w C19 1 +f1cd7 b D3 1 +f1ce1 b H2 1 +f1d07 b S18 1 +f1d10 w E3 1 +f1d57 w D4 1 +f1d61 b P15 1 +f1d77 b D18 1 +f1d86 w S5 1 +f1e10 w C17 C15 2 +f1e81 b Q8 A13 2 +f1e86 w G15 1 +f1e91 w B2 1 +f1e97 w C12 1 +f1eb1 w C6 1 +f1ec7 w M2 1 +f1f10 b C16 1 +f1f31 w O7 1 +f1f37 w E5 1 +f1f46 w G6 1 +f1f51 b P3 1 +f1f70 b H16 1 +f1f86 b Q7 1 +f1f86 w L7 1 +f1fb0 w F17 1 +f1fb6 w D17 1 +f1fe0 w R17 P17 2 +f2016 w C8 1 +f2041 b D18 1 +f2057 w E13 1 +f2066 w Q18 1 +f2071 w S18 1 +f2086 b C8 1 +f20c0 w C4 D4 2 +f20f0 b B5 1 +f20f1 w N2 1 +f20f7 b C6 1 +f2141 w Q9 1 +f2180 b G7 1 +f2197 b B5 1 +f21c0 b M5 1 +f21c1 b S3 1 +f21c1 w Q14 1 +f21c7 w H17 1 +f21d6 b D15 E16 2 +f21e0 w E16 1 +f21e7 w R3 1 +f2256 w E17 1 +f2277 b D16 1 +f2287 b P5 1 +f22d1 w R16 1 +f22f7 b Q16 1 +f2310 b Q18 1 +f2336 w Q15 1 +f2340 w Q17 R16 2 +f2346 w B15 1 +f2376 w O2 O6 2 +f2391 b O12 1 +f23a1 b P14 1 +f23a6 w Q5 1 +f23b6 w E14 Q8 2 +f23c7 b D9 1 +f23e1 b C6 D4 2 +f2407 w M16 1 +f2417 b B4 1 +f2427 b C16 1 +f2431 w B3 1 +f2457 w R11 R13 2 +f2486 b R5 1 +f24c7 b G18 1 +f24d6 b B16 1 +f24d7 b P5 1 +f24e0 b Q11 1 +f2530 w N15 1 +f2596 b D3 1 +f25c6 b D3 1 +f25f0 w C3 1 +f2646 w L18 1 +f2670 w M15 1 +f2677 b B16 1 +f2696 b R17 R15 Q14 Q13 R13 5 +f2696 w Q14 R14 P17 P15 N17 N16 M16 7 +f26b1 b D18 1 +f26d0 b O3 1 +f26e0 b E2 1 +f2707 b P2 D11 2 +f2741 b D14 1 +f2751 b H3 1 +f2776 w S9 1 +f2790 w E2 1 +f2797 w S3 1 +f27a0 w Q18 1 +f27d1 w C17 1 +f27e6 w P3 1 +f2837 w R5 R6 Q6 Q5 4 +f2846 b D2 1 +f2896 b R14 N17 2 +f28a6 w G16 1 +f28e1 b D12 1 +f2966 b S9 1 +f2970 b N4 1 +f29b1 w Q16 1 +f29f0 w G14 1 +f29f6 w E3 1 +f2a36 w P2 1 +f2a67 w C15 1 +f2a70 w F15 1 +f2b11 b Q7 1 +f2b20 b M16 1 +f2b37 b S3 1 +f2bb6 w C13 1 +f2bf1 w C12 1 +f2bf7 b C16 1 +f2c17 b B14 1 +f2c26 w D14 1 +f2c46 b P4 1 +f2c50 b N5 1 +f2c61 b E13 1 +f2c76 w F18 1 +f2c77 w C5 1 +f2c80 w C15 1 +f2c96 b C7 C5 2 +f2c96 w F2 1 +f2cc6 w R17 1 +f2cf7 b C3 1 +f2d11 b E2 1 +f2d47 b S16 1 +f2d97 b E16 1 +f2db0 w B3 1 +f2e07 b B15 1 +f2e17 w Q15 1 +f2e27 w P14 1 +f2e37 b F7 1 +f2e46 w B12 1 +f2e77 w R3 1 +f2e81 w Q17 1 +f2e87 w C2 1 +f2eb1 b Q3 1 +f2ec0 b B17 1 +f2ee6 w D8 C6 2 +f2f10 b E8 1 +f2f31 b Q8 1 +f2f46 w E6 C6 2 +f2f51 w M15 1 +f2f67 b Q16 1 +f2f86 w F4 1 +f2fd7 w D3 1 +f2ff1 w E16 1 +f3046 b S6 1 +f3071 w C17 1 +f3076 b C1 1 +f3096 b B5 B2 2 +f30a0 b S4 Q6 2 +f30c0 b A14 1 +f30e0 b C16 1 +f30f1 b C16 1 +f30f1 w C17 D16 E16 F15 F16 5 +f3111 b Q13 1 +f3160 w O6 1 +f3181 b B11 1 +f31e7 b F16 1 +f31f0 w Q14 1 +f31f7 w S6 1 +f3220 b E18 1 +f3221 b R5 1 +f3227 b D17 1 +f3241 w C15 1 +f3246 b P13 1 +f3246 w D3 1 +f3290 w C14 1 +f32a0 b B6 1 +f32a7 w T17 1 +f32d6 b F4 1 +f32e7 b P9 1 +f3300 b Q16 1 +f3356 b O4 O3 R5 P5 R7 Q7 Q8 7 +f3356 w R3 P3 O4 N4 N3 5 +f3380 w C18 1 +f3381 b G3 H2 2 +f3381 w D18 1 +f3386 b E13 F13 2 +f3387 b G2 1 +f3390 b J4 1 +f33c1 b C3 1 +f33c1 w C6 1 +f33f6 b F2 1 +f3441 w R16 1 +f3481 b O5 1 +f3497 b Q2 1 +f34b6 b E14 1 +f34c1 w R17 S17 Q12 3 +f34f7 b E16 1 +f3547 w C13 1 +f3556 b O2 D7 2 +f3557 b M15 1 +f3557 w D14 1 +f3561 w Q16 1 +f3581 b F18 1 +f35a1 w Q14 1 +f35c6 b C16 1 +f35c7 b D2 1 +f35e6 w Q6 1 +f3606 b R9 1 +f3610 b C2 1 +f3640 b G5 D7 2 +f3660 b D8 1 +f3661 w D13 1 +f3667 b Q6 1 +f3696 w N4 1 +f3697 w C2 1 +f36a7 w N3 1 +f36d1 w O15 1 +f36f1 b F4 1 +f3710 b G3 1 +f3711 b R16 P16 2 +f3770 b O4 1 +f3790 w P12 O12 2 +f37a0 b D11 1 +f37a6 w C13 1 +f37b0 b B15 C16 2 +f37c0 b R12 1 +f37c0 w P5 1 +f37e1 w R8 1 +f37e7 w F4 1 +f3801 w Q16 1 +f3821 b G18 1 +f3831 b J2 1 +f3840 b R17 1 +f3856 b O5 1 +f3860 w B16 1 +f38b6 b A3 1 +f38b6 w D14 1 +f38b7 b Q2 1 +f38c7 b Q6 1 +f38d1 b N14 1 +f38d7 w O15 1 +f38f1 w M14 1 +f3900 w E8 1 +f3940 b L15 1 +f3946 w Q13 1 +f3961 b R17 1 +f39b7 w R3 1 +f39f0 b O4 O3 N3 Q6 R8 Q8 Q9 7 +f39f0 w O3 1 +f3a26 b C13 E18 2 +f3a41 b E4 F5 2 +f3b16 b N6 1 +f3b20 w J17 1 +f3b31 w E4 1 +f3b86 b R19 R3 2 +f3bb7 b E5 1 +f3bb7 w C16 1 +f3be7 w G15 1 +f3bf6 w D15 1 +f3c40 w B17 1 +f3c47 w P4 1 +f3cc7 b C15 1 +f3ce1 w O5 1 +f3d21 b P17 1 +f3d80 b P16 1 +f3d81 w B3 1 +f3db0 b O9 1 +f3db0 w E12 1 +f3db1 b Q7 1 +f3de0 b R7 1 +f3df1 b D4 1 +f3e06 w R15 1 +f3e16 w R9 1 +f3e26 b R3 1 +f3e31 w R2 1 +f3e41 w B3 1 +f3e50 b B5 1 +f3e51 b R2 1 +f3e61 w F13 1 +f3ed7 b F15 1 +f3f00 w N6 1 +f3f41 w C6 1 +f3f76 w Q3 1 +f3f81 b S7 1 +f3f86 b R7 1 +f3fb1 b P3 O2 P2 3 +f3fc1 b E17 1 +f3fe6 b B4 1 +f4007 b S17 1 +f4010 b O19 1 +f4030 b O18 1 +f4037 w Q1 1 +f4047 w R15 1 +f4050 w N2 1 +f4061 b P17 1 +f4086 b D14 1 +f40a6 w L2 1 +f40b0 w O7 1 +f40c0 b E18 1 +f40d0 b E11 1 +f4116 w Q7 1 +f4137 w S3 L3 2 +f4187 w N4 1 +f4197 b E17 1 +f41c1 b C16 1 +f41d7 w P15 1 +f4201 w G14 1 +f4210 b G15 1 +f4217 b C14 E16 2 +f4221 w D17 1 +f4241 b Q2 1 +f42a0 w F3 E6 2 +f42a7 b E7 1 +f42c6 w E17 1 +f42d0 b F9 1 +f4310 w D7 1 +f4341 b C18 1 +f4376 w D17 1 +f4386 b S7 1 +f43d6 w S17 1 +f43f1 b E8 1 +f43f6 w S14 1 +f4497 b D17 1 +f44e0 b R9 1 +f44e6 w N16 1 +f4500 w P17 1 +f4507 w E18 1 +f4521 w C17 1 +f4527 b R15 1 +f4567 b E3 E2 D4 3 +f4626 w E3 1 +f4660 w E3 1 +f46b7 b H5 1 +f46f6 w J17 1 +f46f7 b G15 1 +f4706 w F11 1 +f4751 w E16 1 +f47a0 w G5 1 +f47b6 b C12 1 +f47b6 w P16 1 +f47c0 w E5 1 +f47f7 w P5 1 +f4801 w E7 S7 2 +f4817 b N6 1 +f4820 b S4 1 +f4831 b B16 1 +f4851 b N4 R5 2 +f4857 b D13 1 +f4877 b G17 1 +f4880 w R3 1 +f4881 w C7 1 +f4897 b S6 1 +f48e1 w C15 1 +f48e6 b P4 1 +f48f7 b Q9 F3 2 +f4927 b C16 1 +f4946 b Q4 1 +f4976 w E17 1 +f4980 b S16 1 +f4a81 w R9 O5 N5 3 +f4a86 b E5 1 +f4a96 b Q17 1 +f4ab0 w R17 1 +f4ac1 b D13 1 +f4ac6 b P16 1 +f4ae7 w B5 1 +f4af0 b D13 1 +f4b01 b Q7 1 +f4b06 w R15 1 +f4b16 b M3 1 +f4b41 b O14 N13 2 +f4b96 w T12 1 +f4be7 b O7 C3 2 +f4c01 b N15 1 +f4c06 b B17 1 +f4c07 b E4 1 +f4c17 b R5 1 +f4c40 b J16 1 +f4c51 w S4 1 +f4c70 w B18 1 +f4c76 b R13 R15 2 +f4c86 w S15 1 +f4c96 b Q15 1 +f4ce1 b P14 1 +f4ce7 b Q2 1 +f4d10 b C5 1 +f4d86 w Q6 1 +f4d90 b E4 1 +f4d96 b Q3 1 +f4da0 w C14 G4 2 +f4dc0 b C16 1 +f4dc0 w S16 1 +f4dc7 w B3 1 +f4de1 w Q4 1 +f4df7 w O4 N3 2 +f4f00 b L6 1 +f4f07 b H3 1 +f4f37 b C18 1 +f4f40 w R15 1 +f4f56 b D4 1 +f4f56 w B4 D17 E16 3 +f4f70 w Q6 S6 2 +f4f87 b F16 1 +f4f90 w F17 1 +f4fc1 w C4 1 +f4fd0 b E16 1 +f5000 w E17 1 +f5011 w D14 1 +f5031 b C2 1 +f5046 b O3 1 +f50c6 b P13 1 +f50d1 w A2 1 +f50e1 w H13 1 +f5100 w D17 1 +f5107 b S1 1 +f5161 w E8 1 +f5171 w B11 1 +f5176 w N2 1 +f5180 w M5 1 +f5196 b P5 1 +f51c6 w C7 1 +f51d7 w Q11 1 +f51f1 w C18 1 +f5200 b O15 1 +f5206 w G4 1 +f5207 b R17 1 +f5280 b D15 J17 C14 C13 C12 D13 D14 7 +f5280 w D15 D14 G17 H17 J17 G16 H16 J16 8 +f5286 w S8 1 +f5290 w C7 1 +f52a7 b C16 1 +f52c6 b E17 1 +f52d0 b P5 1 +f52d6 b P17 1 +f52e0 w C9 1 +f5321 b B4 1 +f5331 w J15 1 +f5341 b E16 1 +f5341 w Q5 1 +f53c0 w D17 C16 2 +f5417 w Q18 1 +f5426 b P4 1 +f5427 b F5 1 +f5480 b D17 C16 2 +f5496 b O5 1 +f54c0 b S5 1 +f54d7 w C7 1 +f54e7 b B5 1 +f5557 w B4 1 +f5571 b D8 1 +f5576 b N4 1 +f55a6 w D6 1 +f55c7 w E13 1 +f55d1 b Q17 1 +f55d7 b O16 1 +f55f7 b D4 C3 2 +f5607 w B15 1 +f5620 b Q2 1 +f5631 w C3 H4 2 +f5640 w R7 1 +f56a7 w O2 1 +f56c0 b O15 1 +f56d0 b N14 Q17 2 +f5726 b R11 1 +f5771 b C5 1 +f5791 w E17 1 +f57b1 w F5 1 +f57d7 w F15 1 +f57f7 b D14 E15 2 +f5817 w S14 1 +f5836 b Q16 1 +f5847 b C14 1 +f5871 b O6 1 +f5896 b N17 1 +f58b1 w L17 N17 2 +f58b7 w R17 1 +f5921 w C6 F7 2 +f5927 b S18 1 +f5931 b Q6 E18 2 +f5946 w F6 1 +f5961 w B3 E3 F4 G3 G2 5 +f5981 w B13 1 +f59e0 w P18 1 +f59e1 b Q4 1 +f5a87 b R4 1 +f5ac1 w F4 G3 2 +f5af1 b E15 F15 C17 G17 4 +f5b51 b P15 1 +f5b57 b D5 D6 2 +f5b71 w S5 1 +f5ba6 w Q16 1 +f5bb6 b P17 1 +f5be7 w D16 1 +f5c00 w G17 1 +f5c06 b Q14 1 +f5c16 w E3 1 +f5c40 w N6 1 +f5c61 b O3 1 +f5c70 w N15 1 +f5c90 b C15 1 +f5c91 b G13 1 +f5cb0 w S15 1 +f5ce0 b B5 1 +f5cf7 w O5 1 +f5d07 b G15 1 +f5d30 w O17 1 +f5d31 w D16 Q17 2 +f5d50 w D14 1 +f5d51 w C3 1 +f5d96 b Q3 1 +f5db6 w B13 1 +f5de6 b C16 1 +f5e01 w G15 1 +f5e16 b Q17 1 +f5e20 w C11 1 +f5e21 b F7 1 +f5e36 w R5 S5 2 +f5eb7 b O16 P15 2 +f5f40 w R14 1 +f5f50 b O9 1 +f5f60 b H5 1 +f5f66 w C18 1 +f5f76 b R3 1 +f5fe6 w M3 1 +f6001 b Q2 1 +f6006 w O18 1 +f6017 b N2 1 +f6021 w F2 1 +f6050 b D5 1 +f60b0 b Q15 R15 2 +f60e7 b R16 1 +f6100 w E16 1 +f6116 w F2 M17 C11 3 +f6121 b B5 O16 2 +f6140 b O16 1 +f6160 b N18 1 +f6167 b Q15 Q14 2 +f6181 b N2 1 +f61a7 w B16 1 +f61b6 b R14 1 +f6210 w N3 1 +f6236 b Q7 1 +f6237 b B6 1 +f6240 b D2 1 +f6280 b N16 1 +f6287 w G15 1 +f62a6 w N6 1 +f62b7 b Q14 1 +f62c7 b M18 1 +f62d1 b E13 1 +f62e0 w D18 1 +f6320 b R18 1 +f6376 w B4 1 +f6396 w F15 R3 2 +f63a6 w Q2 1 +f63a7 w P8 1 +f63b7 b C14 1 +f63c1 b H2 1 +f63c1 w C7 1 +f6400 w N5 1 +f6437 b R5 1 +f6440 w O4 1 +f6446 b Q3 1 +f64f1 w O13 1 +f6500 w F5 1 +f6506 w P2 1 +f6516 b Q14 Q13 2 +f6520 b P16 1 +f6536 b O16 1 +f6541 b S17 S14 2 +f6550 b H4 1 +f6551 b T7 1 +f6591 b R2 1 +f6596 b O2 1 +f65b1 w H14 1 +f65c1 b F6 1 +f65f1 b E7 1 +f6611 b E16 1 +f6671 w G18 1 +f6690 w N14 1 +f66a7 w R3 1 +f66c1 w Q4 1 +f66e1 b S5 1 +f66f7 b C17 1 +f6710 b Q17 R16 2 +f6717 w D14 1 +f6720 b B4 1 +f6727 b C17 1 +f6771 w P15 O15 R17 3 +f6796 w J7 1 +f6797 w C3 1 +f67a6 b C15 1 +f6801 b O5 N4 P3 3 +f6806 w T18 1 +f6826 w H18 1 +f6836 b S15 1 +f6836 w E15 1 +f6841 b R15 1 +f68a7 b O3 1 +f68b1 w C8 1 +f68c1 b P13 1 +f68c6 b D2 1 +f68e1 b P6 1 +f68e7 b Q7 1 +f68f1 w R8 1 +f6927 b R17 1 +f6940 b P3 1 +f6947 w Q7 P18 2 +f6971 w B2 1 +f69b0 w F16 1 +f69b6 b S4 R18 2 +f69e1 b C2 1 +f69f0 b Q14 1 +f6a30 w E14 F16 2 +f6a31 b N4 1 +f6a40 w D4 1 +f6a87 w Q3 1 +f6ab1 b R14 1 +f6ab7 w R4 1 +f6ac0 w P3 1 +f6ad6 w B3 1 +f6b16 b B15 1 +f6b26 b F3 E4 2 +f6b37 b G3 1 +f6b37 w Q4 1 +f6b51 b E18 1 +f6b70 w Q4 D3 C4 3 +f6b86 w N16 1 +f6b90 b P18 1 +f6ba1 w J17 1 +f6ba6 b C19 1 +f6bb0 w O15 1 +f6bd1 w C13 1 +f6bf0 b F17 1 +f6c01 w E2 1 +f6c61 b D15 1 +f6c86 b B13 1 +f6ca6 b B16 1 +f6ca7 b Q5 1 +f6cf1 b S14 1 +f6d30 b P4 1 +f6d61 w Q13 1 +f6d70 b D15 1 +f6d77 w N5 1 +f6d86 b O3 Q13 2 +f6da1 b G17 1 +f6dc7 b R3 1 +f6dd6 b S3 1 +f6de0 b F4 1 +f6e51 b O13 1 +f6e71 w R5 1 +f6e90 w Q14 1 +f6ee0 w S15 1 +f6f01 w C6 B6 2 +f6f36 w Q16 1 +f6f50 b O5 1 +f6f80 b C14 D16 C16 3 +f6fc7 w B4 1 +f6fe0 w J4 1 +f7001 w E4 1 +f7010 b Q19 1 +f7060 b F16 G16 2 +f7061 b E8 1 +f7066 b B17 1 +f7067 w F11 1 +f7071 w O18 1 +f7087 w S13 1 +f70f7 w C5 1 +f7137 b P6 O4 2 +f7166 w E8 1 +f7181 w G16 F18 2 +f71f7 w P15 O15 R17 N17 4 +f7201 w O12 1 +f7236 b R11 1 +f7240 w E8 1 +f7280 w P15 1 +f7286 w E15 1 +f72c1 b Q3 1 +f7306 b E14 1 +f7311 w J18 1 +f7317 b Q3 1 +f7336 b Q2 1 +f7376 b R18 B6 2 +f73a0 b D9 1 +f7410 w Q12 1 +f7430 b S3 S4 2 +f7436 b R2 1 +f7447 b S3 1 +f7477 w E2 1 +f7481 w F18 1 +f7496 w Q3 1 +f7497 b Q9 1 +f74c1 b H4 1 +f74c6 w A17 1 +f74d1 b N15 1 +f74d6 b C12 1 +f74f0 b B15 1 +f7500 w G3 1 +f7517 w D18 1 +f7597 w B3 1 +f75a0 w Q14 1 +f75a1 w G18 1 +f75f0 w Q13 1 +f75f7 w C3 C2 2 +f7621 w C3 1 +f76f1 b C2 1 +f7710 b G16 1 +f7710 w D14 1 +f7711 w P2 1 +f7736 b Q14 1 +f7750 b P4 1 +f7756 b R17 1 +f7766 b N3 1 +f7770 b E4 1 +f77b7 b O15 1 +f7816 b D6 D5 2 +f7836 w B4 1 +f7861 b O5 R3 2 +f7886 b Q17 1 +f7886 w O6 1 +f78e0 b O4 1 +f78e6 b R16 1 +f7900 w Q6 1 +f7927 b N16 Q16 N18 M15 4 +f7941 b D3 1 +f7951 w S5 1 +f7966 w S12 1 +f7996 b D6 1 +f79d0 w D17 1 +f79d7 w S3 1 +f79e6 b C7 1 +f79f6 w C3 1 +f7a01 b C2 1 +f7a51 w M17 1 +f7a96 w E16 1 +f7ab7 b P8 1 +f7ac0 w D14 1 +f7ae1 b N3 1 +f7b36 b E2 1 +f7b37 b E6 1 +f7b50 b S17 S16 2 +f7b67 b S2 1 +f7b70 w P17 1 +f7b77 b Q8 1 +f7b80 w D2 C6 2 +f7b87 w P17 1 +f7ba6 b E17 1 +f7bb0 b H5 H6 2 +f7bb7 w P15 1 +f7bf0 b R3 M3 2 +f7bf6 b S17 1 +f7c26 b C6 1 +f7c37 w B3 1 +f7c41 w D5 1 +f7c60 w Q3 R4 2 +f7c61 w R3 1 +f7c90 w E3 1 +f7cb7 b Q8 1 +f7cd0 b P17 1 +f7d00 w R2 1 +f7d11 w D4 1 +f7d26 b P16 1 +f7d26 w P16 1 +f7d31 b B18 1 +f7d36 b Q8 1 +f7d36 w M4 1 +f7d50 b E2 1 +f7d71 b B4 1 +f7d80 b E16 1 +f7da0 w E16 1 +f7db7 w B16 1 +f7e20 w D3 1 +f7e21 w R4 1 +f7e51 b L4 1 +f7e86 w R3 B3 2 +f7ea0 b F17 1 +f7ed1 b Q13 1 +f7ed6 b S8 1 +f7f16 w C15 1 +f7f71 b S5 1 +f7f87 w R4 1 +f7fa1 b C16 1 +f7fc1 b Q3 B3 2 +f7fe6 w S3 1 +f7ff0 w G13 1 +f7ff6 b E3 1 +f80b6 w B8 1 +f80b7 w C5 1 +f80e0 b C3 D7 2 +f80f1 b T1 1 +f8107 b E15 1 +f8110 w C6 1 +f8127 b F17 1 +f8140 b P3 1 +f8180 w D18 1 +f81a6 w C7 1 +f8221 b D15 1 +f8221 w E16 E15 D13 C17 4 +f8281 b R17 1 +f8291 b C14 1 +f8291 w D14 E15 2 +f8310 b E6 1 +f8317 b F16 G17 2 +f8326 b F16 1 +f8357 b P12 1 +f8366 w E3 B3 2 +f8380 w O4 1 +f83a1 b R18 1 +f83b1 b F4 G4 2 +f83c7 w C2 B15 2 +f83d1 b D8 1 +f8400 w S16 1 +f8426 b O16 1 +f8476 b F4 1 +f84a0 b D15 1 +f84a1 b D18 1 +f8517 w D17 1 +f8530 w Q5 1 +f8540 b F14 1 +f8577 b P18 1 +f8596 w S3 E2 2 +f8597 w G15 1 +f85c0 b M5 1 +f85d0 w F4 1 +f85d7 w J14 1 +f8627 b N5 1 +f8637 w B18 1 +f8641 w F4 1 +f8667 w Q8 1 +f8676 b C4 1 +f8681 w H16 1 +f8686 w E8 1 +f86a7 b C3 C4 F3 3 +f86e1 b B3 1 +f8727 b S13 1 +f8756 w Q15 1 +f8767 w D12 H16 2 +f8780 b F17 1 +f8797 w O18 1 +f87b1 w R17 1 +f8800 w P18 P16 2 +f8840 w Q16 1 +f8851 b G15 1 +f8856 w B15 1 +f8861 w O17 1 +f8870 b E17 1 +f88f0 w R5 1 +f88f6 w Q18 1 +f88f7 w R3 O4 O3 Q8 P6 5 +f8906 b Q8 R6 2 +f8967 w B5 F2 2 +f8977 w R3 P2 N4 3 +f89a0 w B15 1 +f8a00 w E8 E15 2 +f8a01 w Q14 1 +f8a26 w F14 1 +f8a31 b O14 1 +f8a41 b R17 1 +f8a56 b R16 B6 2 +f8a81 b D16 1 +f8a86 w F4 1 +f8b07 b D6 1 +f8b31 w C11 C13 2 +f8b40 w N14 1 +f8b51 w Q17 1 +f8b80 w B16 1 +f8b87 w E5 1 +f8bc6 w P14 1 +f8bd0 w C4 1 +f8c07 w O17 1 +f8c11 w F12 1 +f8c21 b E3 1 +f8c31 w S11 1 +f8c41 w C14 1 +f8c77 b D15 1 +f8c87 w E16 1 +f8c91 b N18 1 +f8c97 w D14 D13 2 +f8cb7 w R4 1 +f8d20 b Q5 1 +f8d27 b C3 1 +f8d37 w D7 1 +f8d60 w E14 1 +f8d61 b E17 1 +f8d66 b R17 1 +f8db1 b P14 1 +f8db1 w R18 1 +f8db7 b R6 1 +f8dc1 w B16 1 +f8df0 w E3 1 +f8df7 b R17 1 +f8e26 w C18 1 +f8e41 b R3 O4 O3 Q8 P6 5 +f8e50 w Q17 1 +f8e57 w B15 1 +f8e67 b R2 1 +f8e67 w J16 1 +f8e77 b G3 1 +f8e87 b O4 1 +f8eb1 b R4 1 +f8f07 b H17 1 +f8f46 w C17 1 +f8f56 w M15 1 +f8f70 b R3 1 +f8f80 w B5 1 +f8fa1 b C2 1 +f8fc0 w N12 1 +f8fc7 w S7 1 +f8fd6 b S14 1 +f8ff0 w Q8 1 +f9011 b L4 1 +f9050 w M3 1 +f9086 w D17 E16 J16 3 +f90e7 w D14 1 +f9101 w L17 1 +f9111 b S2 1 +f91d0 b P6 1 +f91d7 b P14 R17 2 +f91f6 b R8 1 +f9206 b P15 Q15 2 +f9216 b R13 1 +f9220 b R6 1 +f9227 w S15 1 +f9246 b C5 C2 2 +f9246 w D3 1 +f9267 w R3 S3 Q3 3 +f9277 b D3 B4 2 +f92b7 b Q13 1 +f92c0 w D16 1 +f92e1 b F13 1 +f92f1 b R3 1 +f92f6 w O2 1 +f9317 b F13 1 +f9327 b Q17 1 +f9380 b C4 1 +f93f1 b D3 1 +f9410 w S5 1 +f94a6 w S14 1 +f94a7 b C17 1 +f94c0 w H15 1 +f94f0 b A6 1 +f94f1 b R6 1 +f9500 w C14 1 +f9536 w B18 1 +f9541 w S5 1 +f9551 b N6 1 +f9561 b B6 1 +f9570 b G6 1 +f9580 b B15 1 +f95c0 b D12 1 +f95e6 w Q5 1 +f9601 w R18 1 +f9610 w E6 1 +f9640 w P6 1 +f9646 b F4 O17 2 +f9666 w P18 1 +f9680 w C4 D3 2 +f96d6 b M4 1 +f9716 w P3 1 +f9730 w F6 1 +f9737 b R6 1 +f9756 b F16 1 +f9761 b Q3 1 +f9787 b C12 1 +f9791 b B4 1 +f97b1 b S17 1 +f97b1 w Q13 R11 2 +f97e6 w F16 1 +f97f6 w F3 1 +f9807 w C4 1 +f98b7 w J3 1 +f98c6 w D14 D13 2 +f98d1 b S5 1 +f98e1 w E18 1 +f98e6 b D18 1 +f98f1 b C17 D13 2 +f9960 w G17 1 +f9966 w R3 1 +f9986 b P3 1 +f99b0 b P2 1 +f99c0 w R8 O4 2 +f9a27 b S9 1 +f9a31 w L17 1 +f9a50 b P8 1 +f9a60 b D3 1 +f9ae7 w O16 1 +f9af6 b P13 1 +f9b20 w J16 1 +f9b21 b G7 1 +f9b31 b S3 1 +f9b50 w C9 1 +f9b97 w G18 1 +f9ba6 w O14 1 +f9ba7 b P12 1 +f9ba7 w R16 1 +f9be0 w B5 1 +f9bf0 w E17 1 +f9c11 b B16 1 +f9c21 b D7 1 +f9c46 b L3 1 +f9cb0 w O15 1 +f9cb7 w D4 1 +f9cf7 b L16 1 +f9d20 b R17 1 +f9d30 w D2 F4 2 +f9d57 b Q16 1 +f9d66 w Q17 1 +f9d81 b R2 1 +f9d86 w G5 1 +f9dc6 b D5 1 +f9dc6 w D5 1 +f9dd0 w B5 C3 F4 F3 4 +f9df0 b D6 C6 2 +f9df6 w R2 1 +f9e06 w R17 1 +f9e46 w O5 1 +f9e47 b C8 1 +f9e51 b P5 1 +f9e56 w C14 1 +f9e70 b P4 1 +f9e96 w E5 1 +f9ec1 w F3 1 +f9f11 w C15 1 +f9f61 w Q8 1 +f9fb1 b S15 1 +f9fd0 w N6 1 +f9fe1 w R2 1 +fa001 b F17 1 +fa011 b N4 1 +fa047 w E3 1 +fa051 b C17 1 +fa0d6 w R17 1 +fa0e0 b P3 1 +fa101 b P3 O3 O4 P4 4 +fa120 w R17 1 +fa191 b D16 1 +fa191 w C15 C14 D15 E16 4 +fa1c1 w R14 Q15 2 +fa1f1 w Q8 1 +fa227 b C3 1 +fa230 b C2 1 +fa250 w E16 1 +fa256 b P13 O13 2 +fa271 w R2 1 +fa2a6 b R3 1 +fa2b0 w H4 1 +fa2b6 b R14 1 +fa2b7 w F1 1 +fa2d6 w P15 1 +fa310 b N14 1 +fa357 b P6 1 +fa367 b S3 1 +fa3b0 w B17 1 +fa3d0 w M6 1 +fa407 b R17 1 +fa416 w Q5 1 +fa426 w H2 1 +fa4e6 w D16 1 +fa4f7 w Q5 P6 2 +fa517 w S5 1 +fa526 w P15 1 +fa527 b R6 1 +fa530 w O4 1 +fa547 w Q4 1 +fa561 w E18 1 +fa576 w O16 1 +fa577 w D14 1 +fa581 b C16 C17 2 +fa596 w J17 1 +fa5c6 b R3 1 +fa617 w D17 1 +fa621 b C4 1 +fa6c0 w P14 1 +fa6e1 w H6 1 +fa6f0 b S2 1 +fa6f1 b D6 1 +fa710 b O4 N4 2 +fa750 w P12 1 +fa756 b D18 1 +fa766 b S16 1 +fa7c0 b D3 1 +fa801 b F2 1 +fa807 b C3 1 +fa817 b C13 1 +fa826 w R15 1 +fa840 w N5 1 +fa841 b R17 O16 O17 Q12 P14 5 +fa8c1 b Q7 1 +fa8e0 b H4 1 +fa8e6 w R2 1 +fa8f1 b T18 1 +fa930 b C18 1 +fa951 w C4 1 +fa987 w O4 1 +fa9a1 w Q9 1 +fa9b7 b Q3 1 +fa9e1 w C16 1 +faa07 w M15 1 +faa17 w R4 1 +faa27 w Q6 1 +faa91 b C17 1 +faaa7 w O5 1 +faac1 w Q3 1 +faac7 b B17 1 +faad0 b L4 1 +faae7 w F15 1 +faaf1 b Q17 1 +fab10 b B14 1 +fab26 b P17 Q16 2 +fab30 w O17 1 +fab97 w F15 1 +faba6 w P3 1 +fabd0 w R2 1 +fabe6 w S3 1 +fac00 b E8 1 +fac10 b D8 1 +fac20 b Q15 1 +fac27 w B7 1 +fac80 w S3 1 +facd0 b Q5 1 +fad37 w D5 1 +fad61 b G4 1 +fad67 b R14 Q15 2 +fad96 b Q13 1 +fada0 w B2 1 +fada1 b S13 1 +fada6 b O12 1 +fadb0 b R4 Q3 2 +fadc0 b E2 1 +fade0 b O4 1 +fade1 b G15 1 +fae01 b C5 1 +fae06 w H3 1 +fae17 b D2 1 +fae46 b D18 1 +fae50 b C15 1 +fae60 b Q15 1 +fae77 w R13 1 +fae80 b C6 1 +fae91 b E6 1 +fae97 w C2 1 +faea0 b P4 1 +faec7 b R17 1 +faee7 w D8 1 +fafb7 b R14 1 +fafd0 b G17 E15 2 +faff0 w E17 1 +faff7 w S4 1 +fb007 w Q12 1 +fb026 w E2 1 +fb036 w G16 1 +fb046 w B13 1 +fb056 w Q12 R14 2 +fb060 w R3 1 +fb077 w G15 1 +fb080 b E16 1 +fb0c0 b C4 1 +fb107 b C16 1 +fb136 w O15 1 +fb180 b C5 1 +fb180 w Q15 1 +fb1b1 w Q5 1 +fb1f0 w P3 1 +fb1f1 b B16 1 +fb201 b S8 1 +fb207 w R18 1 +fb221 b S2 1 +fb231 b R19 1 +fb256 w Q14 1 +fb2a7 b D9 1 +fb2c1 b B4 1 +fb2d0 b E17 C6 2 +fb2d1 w T4 1 +fb2e6 w N17 1 +fb331 b R3 1 +fb347 b M3 1 +fb350 w Q3 1 +fb351 w S2 1 +fb367 w R16 1 +fb371 b S15 1 +fb387 b P1 1 +fb397 w J17 1 +fb3c1 w D15 Q12 2 +fb416 w E16 1 +fb427 w D18 1 +fb480 b N4 1 +fb480 w O3 1 +fb486 w H15 1 +fb4a6 w R15 1 +fb4c0 w H4 1 +fb4d1 w P7 1 +fb506 w R7 1 +fb526 w Q14 1 +fb570 w J16 1 +fb596 w J3 1 +fb5a6 b R19 1 +fb611 b P14 1 +fb621 b D6 1 +fb636 w E4 1 +fb650 b H2 1 +fb671 b R12 1 +fb686 b D17 E16 J16 3 +fb696 b R4 1 +fb6c0 b E12 1 +fb6c0 w P4 R3 2 +fb6d1 b C5 1 +fb701 b D2 1 +fb771 w P5 R6 2 +fb781 b R4 1 +fb7f7 w G3 1 +fb810 b S18 1 +fb846 w F14 1 +fb847 b F17 1 +fb850 w S4 1 +fb857 b O2 1 +fb867 w N15 1 +fb886 w C11 1 +fb8a0 w A6 1 +fb8a1 b R16 1 +fb8b6 b S16 1 +fb8c7 b L3 1 +fb8d1 w F2 1 +fb926 w C4 E4 2 +fb957 b D3 1 +fb967 b C18 1 +fb987 w O3 1 +fb997 w E5 1 +fba16 b H17 1 +fba36 w E5 1 +fba61 b R17 1 +fba80 w F9 1 +fbaa7 b Q13 1 +fbac6 w R14 1 +fbad0 b B18 1 +fbaf0 w N17 1 +fbaf6 b S3 1 +fbb00 w N4 1 +fbb07 w R7 1 +fbb20 w D5 1 +fbb31 b B16 1 +fbb41 w P3 1 +fbb96 w O15 O17 2 +fbc51 b S14 1 +fbc77 w R3 1 +fbce7 b H16 1 +fbcf6 w G16 1 +fbd36 w R4 1 +fbd41 b E14 1 +fbd66 b P13 1 +fbd81 b O14 1 +fbd86 b N16 1 +fbda7 w R16 1 +fbdc0 w R9 1 +fbdc1 b Q3 1 +fbdd1 b S6 1 +fbe01 w A17 1 +fbe06 w B2 1 +fbe36 w Q3 1 +fbe50 w P6 1 +fbe61 b P15 1 +fbeb1 w N14 1 +fbed0 b F4 1 +fbef1 w R4 1 +fbf37 b C14 1 +fbf47 w R2 1 +fbf56 w C12 1 +fbf77 b Q3 1 +fbf90 b P17 1 +fbf96 b R8 1 +fbfa0 b R18 1 +fbfb1 b F16 1 +fbfd0 b O3 1 +fbfd6 w S4 1 +fbfe1 w H18 1 +fbff7 b M5 1 +fbff7 w N16 1 +fc011 w D18 1 +fc026 b S5 1 +fc077 w N16 1 +fc080 b D6 1 +fc0f0 b F17 1 +fc111 w A16 1 +fc120 w B3 1 +fc131 w C14 1 +fc151 w Q7 Q4 2 +fc171 b B17 C3 2 +fc190 b O17 1 +fc1a1 w H17 1 +fc1a7 b C8 1 +fc1d6 b R15 1 +fc210 b Q12 1 +fc221 w G16 1 +fc266 b C15 1 +fc2c6 b R16 1 +fc2c6 w O16 1 +fc311 w R7 1 +fc381 w Q16 1 +fc387 w B3 1 +fc411 w B12 1 +fc430 b R18 1 +fc431 w S18 1 +fc471 b S5 1 +fc476 b S19 1 +fc480 w H16 1 +fc4a0 w A3 1 +fc4b6 w O4 1 +fc4b7 w Q14 1 +fc4c1 w R3 1 +fc501 b O16 1 +fc536 w N17 1 +fc537 w S5 O2 2 +fc590 b E13 1 +fc5a0 b O2 1 +fc5b1 w C3 1 +fc616 w D2 1 +fc626 w S17 1 +fc640 w R14 1 +fc647 b R3 1 +fc680 b F3 1 +fc681 w D9 1 +fc686 w D14 1 +fc6a6 b R15 P14 2 +fc6c7 w B7 P15 2 +fc6d0 b M5 1 +fc6e1 b S7 1 +fc6e6 b P7 1 +fc6e7 w D6 D7 2 +fc716 b G6 1 +fc741 w F14 1 +fc7d1 b S17 1 +fc7e0 w R2 1 +fc7e7 b D18 1 +fc817 w S16 1 +fc827 w Q4 1 +fc830 b S14 1 +fc841 w H5 1 +fc860 b N15 1 +fc8d1 w M6 1 +fc8e7 b S14 1 +fc8f6 b D14 1 +fc906 w B14 1 +fc907 b H3 1 +fc936 w P17 O15 2 +fc941 b Q18 1 +fc941 w Q14 1 +fc950 w Q13 1 +fc957 b G17 1 +fc991 w D3 1 +fc9a7 b N4 Q4 2 +fc9d0 w O5 1 +fc9d6 w R3 1 +fc9f0 b R14 1 +fca10 w E4 1 +fca50 b E14 1 +fca70 w O4 1 +fca77 b Q4 P3 R5 O4 Q6 5 +fcac6 b C16 1 +fcaf0 w G4 1 +fcb26 b B6 1 +fcb26 w O4 1 +fcb71 w C17 1 +fcb91 w E18 1 +fcbb6 w M16 1 +fcbd0 w C3 1 +fcbd1 b A3 1 +fcbf7 w R15 1 +fcc26 w R5 1 +fcc51 w D7 1 +fcc61 w D13 1 +fccb7 w N16 1 +fccc0 b J6 1 +fccc1 b S17 1 +fcd07 b Q18 1 +fcd37 w C5 1 +fcd51 w C4 1 +fcd56 w P4 1 +fcd67 b R18 1 +fcd80 w C18 F3 2 +fcd90 w B5 1 +fcd91 w C4 1 +fcdc0 b O16 O18 2 +fcdc7 b R17 M16 2 +fcde0 w J17 1 +fce17 b R18 1 +fce37 w O16 N17 2 +fce90 b Q3 1 +fce90 w P3 1 +fcea1 w E2 1 +fcec7 b Q3 1 +fced6 b E15 1 +fcf06 w E4 1 +fcf26 b S15 1 +fcf40 w B3 1 +fcf50 w E4 1 +fcf70 w Q16 D12 2 +fcf71 b R15 1 +fd037 b M17 1 +fd037 w E17 1 +fd0a1 b B15 1 +fd120 w E13 1 +fd147 b M17 1 +fd157 b E2 1 +fd1e0 w C17 1 +fd1e6 w E13 1 +fd216 w O6 1 +fd220 b L3 1 +fd2a1 w C12 Q13 2 +fd2e6 w P14 1 +fd301 b Q7 P3 2 +fd317 b O4 N5 2 +fd376 b O16 P3 2 +fd377 w P17 1 +fd3a6 b R16 1 +fd3a6 w D7 1 +fd3e7 b T15 1 +fd436 w C12 1 +fd440 b R16 1 +fd441 b G4 1 +fd450 w E15 B2 2 +fd456 b E5 1 +fd467 b O13 1 +fd467 w B16 1 +fd4a1 w Q4 O4 2 +fd4c1 w E14 1 +fd4e7 w O2 1 +fd500 b E18 1 +fd500 w O17 1 +fd501 b D8 1 +fd510 w P2 1 +fd531 w C18 1 +fd536 w F6 1 +fd547 w Q16 1 +fd567 w Q2 1 +fd591 b R7 1 +fd5a7 b M15 1 +fd5b7 w D19 1 +fd5c0 b E12 1 +fd5e0 w F15 1 +fd5f1 w O13 1 +fd601 b R18 1 +fd606 w S15 1 +fd610 w S15 1 +fd616 w F2 1 +fd617 w S9 1 +fd636 w O14 1 +fd651 w N2 1 +fd730 w O12 1 +fd750 w G18 1 +fd760 b E7 G4 2 +fd7a6 w Q13 1 +fd7f1 w F4 B5 2 +fd811 w F3 1 +fd817 w E15 1 +fd827 w Q13 1 +fd836 b R18 1 +fd851 w T2 1 +fd856 b E5 1 +fd876 w Q3 1 +fd877 w Q2 1 +fd880 w L17 1 +fd8e1 b B13 1 +fd927 b G5 1 +fd931 b P15 1 +fd936 w F3 1 +fd980 w E6 1 +fd9b7 w C7 1 +fd9c1 b Q18 1 +fd9c6 b P3 R3 2 +fda50 b B18 1 +fda80 b C13 B15 2 +fdad6 w G3 1 +fdaf0 b O17 1 +fdb00 b F16 1 +fdb06 b H17 1 +fdb37 w R2 1 +fdb41 b E15 1 +fdb96 b Q5 1 +fdb97 b Q11 1 +fdba7 w E2 1 +fdbc7 w E14 1 +fdbe1 w A2 1 +fdc06 w S17 1 +fdc27 w D17 1 +fdc36 w M3 1 +fdc47 b G14 1 +fdc97 w E6 1 +fdcd6 b A3 1 +fdd67 b R13 1 +fdd71 w O17 1 +fdd87 w C3 1 +fdd97 b C2 F17 E6 3 +fdda1 w Q14 1 +fddc7 b F13 1 +fddd1 w S16 1 +fdde0 b E6 P13 2 +fddf6 b P6 1 +fde21 b B4 1 +fde40 b M17 1 +fde66 w S5 1 +fde86 b C6 1 +fde97 w E18 1 +fdea6 b B12 1 +fdf37 w D17 1 +fdf50 w R5 1 +fdf60 w Q4 1 +fdf61 b Q2 1 +fdf66 b C3 1 +fdf76 w C4 1 +fdf80 b G15 1 +fdfd1 b R3 1 +fdfd7 w F14 1 +fdff6 w P15 1 +fe001 w P3 1 +fe011 b B3 1 +fe060 b O18 1 +fe0a6 b E2 1 +fe0b1 w E3 1 +fe0d7 w Q4 1 +fe0e7 b F2 1 +fe136 b C14 D15 2 +fe156 b Q12 1 +fe171 b S16 1 +fe186 b P16 1 +fe191 w E14 F14 E17 F17 4 +fe196 b Q18 1 +fe1d6 w R14 1 +fe1e0 b M17 1 +fe1e0 w P13 1 +fe1f7 b F3 E4 2 +fe217 w C15 G18 2 +fe241 w O2 1 +fe2a6 w C8 1 +fe326 b R16 1 +fe327 w S17 1 +fe361 b F18 1 +fe370 w R3 1 +fe3f1 b G18 1 +fe470 w D5 G5 2 +fe480 b P8 1 +fe4a0 w F16 G16 E3 3 +fe4a6 w Q14 1 +fe4b6 w G17 E17 2 +fe4b7 b Q17 1 +fe4d6 b N3 1 +fe4d6 w Q5 1 +fe506 w R1 1 +fe510 w R8 1 +fe530 w B5 C4 2 +fe566 w R16 1 +fe570 w E2 C3 D6 C6 4 +fe576 w P2 1 +fe590 b E4 1 +fe5b1 w R15 1 +fe5d7 b G12 1 +fe5e0 w S15 R17 O16 O17 4 +fe5e1 w P6 1 +fe620 b C16 D16 C13 E15 C17 5 +fe631 b E18 1 +fe661 w G18 1 +fe687 w D14 1 +fe6d6 w Q18 1 +fe701 w Q18 1 +fe731 b B3 1 +fe777 b C6 1 +fe7b7 b Q16 1 +fe7d7 w Q17 1 +fe7e7 w O8 1 +fe800 b E3 1 +fe801 b Q12 1 +fe817 w C18 1 +fe876 b R3 1 +fe8e1 w Q15 C7 2 +fe8e7 w M16 1 +fe8f6 b P16 1 +fe906 w Q14 1 +fe950 w P6 1 +fe977 w D6 1 +fe9a6 b F4 1 +fe9b1 b Q9 1 +fe9f0 w A16 1 +fea07 b S17 1 +fea37 w F12 1 +fea56 b Q4 1 +fea57 w B6 1 +fea66 w B16 C18 2 +feaa0 w O17 1 +feaa7 w B16 1 +feab7 b C6 1 +fead0 b R4 1 +feae6 w F3 1 +feae7 w Q6 1 +feb91 b P7 1 +febb6 w R16 J2 2 +febd1 b R13 1 +fec26 w D4 1 +fec51 b S4 1 +fec66 b L17 O15 R14 3 +fec67 b J16 1 +fec70 w F16 1 +fecc0 b O3 1 +fecd1 b F16 B17 2 +fed06 b D7 1 +fed31 b E18 1 +feda0 b D2 1 +fedc6 w M4 O3 2 +fedd0 w E4 1 +fedf7 w H2 1 +fee66 b Q14 S5 2 +fee91 b N17 1 +feec7 b R4 1 +feed0 w D12 1 +feed6 w D6 1 +feed7 b B4 1 +fef07 b S18 1 +fef10 b D2 E3 2 +fef21 b E17 B13 2 +fef31 b R13 1 +fef77 b O5 1 +fef80 w Q17 1 +fef81 b O4 1 +fefc0 w H4 1 +fefd1 b Q6 1 +feff1 b R16 1 +feff7 b R2 1 +ff010 b O3 1 +ff010 w G15 1 +ff011 w R15 Q16 2 +ff020 b M15 1 +ff021 w S4 1 +ff036 b C16 1 +ff047 b E2 1 +ff056 b E6 1 +ff087 b G2 1 +ff097 w S4 1 +ff0b0 w D15 1 +ff0e1 b O16 1 +ff106 w C9 1 +ff107 b S8 1 +ff117 w F8 1 +ff137 b P4 P5 Q7 R3 4 +ff137 w Q5 1 +ff1d0 w S15 1 +ff200 w R13 O17 2 +ff276 b C17 C15 D14 D13 C13 5 +ff276 w D14 C14 E17 E15 G17 G16 H16 7 +ff2c0 b F15 1 +ff301 w P16 P14 2 +ff327 w G17 1 +ff330 b R17 1 +ff331 w B9 1 +ff361 w H15 1 +ff366 w B16 1 +ff370 b R7 1 +ff391 b P4 1 +ff397 w P13 1 +ff3a7 b S18 1 +ff3b6 w F14 1 +ff3c7 w R15 1 +ff3d0 w G16 1 +ff3d7 b D17 1 +ff3e0 b R15 1 +ff3f0 b O2 1 +ff411 w F3 1 +ff4b7 w P16 1 +ff4c7 w C8 1 +ff4e6 b C6 1 +ff4f0 w O2 1 +ff511 b R17 1 +ff516 w E12 1 +ff527 b D4 1 +ff556 w P4 1 +ff567 b Q17 1 +ff5c1 b R16 1 +ff5d0 b F16 1 +ff601 b B4 1 +ff610 w B5 1 +ff620 w E8 E7 C5 E5 4 +ff651 b O6 1 +ff667 b O3 1 +ff691 w O15 1 +ff6b1 b B3 1 +ff6c7 b R3 1 +ff6e7 w S2 1 +ff6f7 w F13 1 +ff711 w C11 1 +ff727 b S5 1 +ff727 w P3 R6 2 +ff730 b N4 1 +ff737 w C4 1 +ff750 w R6 1 +ff761 w R6 1 +ff777 b C7 1 +ff787 b P5 1 +ff7b0 b C3 1 +ff7b1 w R15 1 +ff7e6 b D1 P2 2 +ff7f6 b C17 1 +ff820 w G17 1 +ff877 w R17 1 +ff886 b E15 1 +ff896 w Q8 1 +ff8a0 w C4 1 +ff8d6 w R2 1 +ff8e0 b L17 1 +ff921 w D17 1 +ff927 b R6 1 +ff936 w S17 1 +ff950 w P16 1 +ff980 b G3 1 +ff991 b D16 1 +ff9c6 b D12 1 +ff9f1 w B16 1 +ffa06 w F2 1 +ffa07 b S4 E14 C17 3 +ffa16 b D14 D17 2 +ffa30 b P6 1 +ffa66 b D15 1 +ffa77 w B4 1 +ffaa6 b B3 1 +ffac0 w Q6 1 +ffad0 b O3 1 +ffae6 b C6 1 +ffb00 b O8 1 +ffb40 b R16 Q16 2 +ffba7 b D2 1 +ffbb6 b M18 1 +ffbf6 b Q7 1 +ffc06 b Q14 1 +ffc06 w B2 1 +ffc80 w C14 1 +ffcb0 b C4 1 +ffcc6 b H4 1 +ffce0 w D7 D4 E6 3 +ffce6 b R13 1 +ffcf7 w Q16 1 +ffd20 w D18 C19 2 +ffd31 b D14 1 +ffd51 b B2 1 +ffd57 b N4 1 +ffd60 b Q2 1 +ffdc1 b N4 1 +ffdd0 b R12 1 +ffe00 b R3 1 +ffe16 w D2 1 +ffe61 w G2 1 +ffea6 w O17 1 +ffec0 b E2 C3 D6 C6 4 +fff01 w D14 1 +fff41 w D3 1 +fff50 w R9 1 +fff57 w D18 1 +fffb1 b C3 1 +fffb7 b O5 1 +fffd1 w D7 1 +fffe7 w D6 1 +ffff0 b G14 1 +ffff7 b E17 1 diff --git a/jni/pachi/media/pachi-small.png b/jni/pachi/media/pachi-small.png new file mode 100644 index 0000000000000000000000000000000000000000..5ac9c0459af35d0af7bcb5ffff54a09a57cd498c GIT binary patch literal 42206 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%0A}yLT(&mx*$-a!r-zR=)@Qzc!-o2%De$OJ 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%0D&_6ZKd9x~T=+3I~g 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�sTtI4AgT$$_i4(w|@k2TfZ9RW< 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

o&D0=`^ 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 z≫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

*a}mmy3p77S8c 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

DCwnJ(Y6 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 z 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 C<1GRJ literal 0 HcmV?d00001 diff --git a/jni/pachi/montecarlo/Makefile b/jni/pachi/montecarlo/Makefile new file mode 100644 index 0000000..6047f55 --- /dev/null +++ b/jni/pachi/montecarlo/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=montecarlo.o + +all: montecarlo.a +montecarlo.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/montecarlo/internal.h b/jni/pachi/montecarlo/internal.h new file mode 100644 index 0000000..cf77977 --- /dev/null +++ b/jni/pachi/montecarlo/internal.h @@ -0,0 +1,32 @@ +#ifndef PACHI_MONTECARLO_INTERNAL_H +#define PACHI_MONTECARLO_INTERNAL_H + +#include "debug.h" +#include "move.h" + +struct playout_policy; + +/* Internal MonteCarlo structures */ + + +/* Internal engine state. */ +struct montecarlo { + int debug_level; + int gamelen; + floating_t resign_ratio; + int loss_threshold; + struct playout_policy *playout; +}; + +#define MCDEBUGL(n) DEBUGL_(mc->debug_level, n) + + +/* Per-move playout statistics. */ +struct move_stat { + int games; + int wins; +}; + +void board_stats_print(struct board *board, struct move_stat *moves, FILE *f); + +#endif diff --git a/jni/pachi/montecarlo/montecarlo.c b/jni/pachi/montecarlo/montecarlo.c new file mode 100644 index 0000000..4dd655d --- /dev/null +++ b/jni/pachi/montecarlo/montecarlo.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include + +#include "board.h" +#include "engine.h" +#include "joseki/base.h" +#include "move.h" +#include "playout/moggy.h" +#include "playout/light.h" +#include "montecarlo/internal.h" +#include "montecarlo/montecarlo.h" +#include "playout.h" +#include "timeinfo.h" + + +/* This is simple monte-carlo engine. It plays MC_GAMES random games from the + * current board and records win/loss ratio for each first move. The move with + * the biggest number of winning games gets played. */ +/* Note that while the library is based on New Zealand rules, this engine + * returns moves according to Chinese rules. Thus, it does not return suicide + * moves. It of course respects positional superko too. */ + +/* Pass me arguments like a=b,c=d,... + * Supported arguments: + * debug[=DEBUG_LEVEL] 1 is the default; more means more debugging prints + * games=MC_GAMES number of random games to play + * gamelen=MC_GAMELEN maximal length of played random game + * playout={light,moggy}[:playout_params] + */ + + +#define MC_GAMES 40000 +#define MC_GAMELEN 400 + + +/* FIXME: Cutoff rule for simulations. Currently we are so fast that this + * simply does not matter; even 100000 simulations are fast enough to + * play 5 minutes S.D. on 19x19 and anything more sounds too ridiculous + * already. */ +/* FIXME: We cannot handle seki. Any good ideas are welcome. A possibility is + * to consider 'pass' among the moves, but this seems tricky. */ + + +void +board_stats_print(struct board *board, struct move_stat *moves, FILE *f) +{ + fprintf(f, "\n "); + int x, y; + char asdf[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; + for (x = 1; x < board_size(board) - 1; x++) + fprintf(f, "%c ", asdf[x - 1]); + fprintf(f, "\n +-"); + for (x = 1; x < board_size(board) - 1; x++) + fprintf(f, "-----"); + fprintf(f, "+\n"); + for (y = board_size(board) - 2; y >= 1; y--) { + fprintf(f, "%2d | ", y); + for (x = 1; x < board_size(board) - 1; x++) + if (moves[y * board_size(board) + x].games) + fprintf(f, "%0.2f ", (floating_t) moves[y * board_size(board) + x].wins / moves[y * board_size(board) + x].games); + else + fprintf(f, "---- "); + fprintf(f, "| "); + for (x = 1; x < board_size(board) - 1; x++) + fprintf(f, "%4d ", moves[y * board_size(board) + x].games); + fprintf(f, "|\n"); + } + fprintf(f, " +-"); + for (x = 1; x < board_size(board) - 1; x++) + fprintf(f, "-----"); + fprintf(f, "+\n"); +} + + +static coord_t * +montecarlo_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + struct montecarlo *mc = e->data; + + if (ti->dim == TD_WALLTIME) { + fprintf(stderr, "Warning: TD_WALLTIME time mode not supported, resetting to defaults.\n"); + ti->period = TT_NULL; + } + if (ti->period == TT_NULL) { + ti->period = TT_MOVE; + ti->dim = TD_GAMES; + ti->len.games = MC_GAMES; + } + struct time_stop stop; + time_stop_conditions(ti, b, 20, 40, 3.0, &stop); + + /* resign when the hope for win vanishes */ + coord_t top_coord = resign; + floating_t top_ratio = mc->resign_ratio; + + /* We use [0] for pass. Normally, this is an inaccessible corner + * of board margin. */ + struct move_stat moves[board_size2(b)]; + memset(moves, 0, sizeof(moves)); + + int losses = 0; + int i, superko = 0, good_games = 0; + for (i = 0; i < stop.desired.playouts; i++) { + assert(!b->superko_violation); + + struct board b2; + board_copy(&b2, b); + + coord_t coord; + board_play_random(&b2, color, &coord, NULL, NULL); + if (!is_pass(coord) && !group_at(&b2, coord)) { + /* Multi-stone suicide. We play chinese rules, + * so we can't consider this. (Note that we + * unfortunately still consider this in playouts.) */ + if (DEBUGL(4)) { + fprintf(stderr, "SUICIDE DETECTED at %d,%d:\n", coord_x(coord, b), coord_y(coord, b)); + board_print(b, stderr); + } + continue; + } + + if (DEBUGL(3)) + fprintf(stderr, "[%d,%d color %d] playing random game\n", coord_x(coord, b), coord_y(coord, b), color); + + struct playout_setup ps = { .gamelen = mc->gamelen }; + int result = play_random_game(&ps, &b2, color, NULL, NULL, mc->playout); + + board_done_noalloc(&b2); + + if (result == 0) { + /* Superko. We just ignore this playout. + * And play again. */ + if (unlikely(superko > 2 * stop.desired.playouts)) { + /* Uhh. Triple ko, or something? */ + if (MCDEBUGL(0)) + fprintf(stderr, "SUPERKO LOOP. I will pass. Did we hit triple ko?\n"); + goto pass_wins; + } + /* This playout didn't count; we should not + * disadvantage moves that lead to a superko. + * And it is supposed to be rare. */ + i--, superko++; + continue; + } + + if (MCDEBUGL(3)) + fprintf(stderr, "\tresult for other player: %d\n", result); + + int pos = is_pass(coord) ? 0 : coord; + + good_games++; + moves[pos].games++; + + losses += result > 0; + moves[pos].wins += 1 - (result > 0); + + if (unlikely(!losses && i == mc->loss_threshold)) { + /* We played out many games and didn't lose once yet. + * This game is over. */ + break; + } + } + + if (!good_games) { + /* No moves to try??? */ + if (MCDEBUGL(0)) { + fprintf(stderr, "OUT OF MOVES! I will pass. But how did this happen?\n"); + board_print(b, stderr); + } +pass_wins: + top_coord = pass; top_ratio = 0.5; + goto move_found; + } + + foreach_point(b) { + if (b->moves < 3) { + /* Simple heuristic: avoid opening too low. Do not + * play on second or first line as first white or + * first two black moves.*/ + if (coord_x(c, b) < 3 || coord_x(c, b) > board_size(b) - 4 + || coord_y(c, b) < 3 || coord_y(c, b) > board_size(b) - 4) + continue; + } + + floating_t ratio = (floating_t) moves[c].wins / moves[c].games; + /* Since pass is [0,0], we will pass only when we have nothing + * better to do. */ + if (ratio >= top_ratio) { + top_ratio = ratio; + top_coord = c == 0 ? pass : c; + } + } foreach_point_end; + + if (MCDEBUGL(2)) { + board_stats_print(b, moves, stderr); + } + +move_found: + if (MCDEBUGL(1)) + fprintf(stderr, "*** WINNER is %d,%d with score %1.4f (%d games, %d superko)\n", coord_x(top_coord, b), coord_y(top_coord, b), top_ratio, i, superko); + + return coord_copy(top_coord); +} + + +struct montecarlo * +montecarlo_state_init(char *arg, struct board *b) +{ + struct montecarlo *mc = calloc2(1, sizeof(struct montecarlo)); + + mc->debug_level = 1; + mc->gamelen = MC_GAMELEN; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "debug")) { + if (optval) + mc->debug_level = atoi(optval); + else + mc->debug_level++; + } else if (!strcasecmp(optname, "gamelen") && optval) { + mc->gamelen = atoi(optval); + } else if (!strcasecmp(optname, "playout") && optval) { + char *playoutarg = strchr(optval, ':'); + if (playoutarg) + *playoutarg++ = 0; + if (!strcasecmp(optval, "moggy")) { + mc->playout = playout_moggy_init(playoutarg, b, joseki_load(b->size)); + } else if (!strcasecmp(optval, "light")) { + mc->playout = playout_light_init(playoutarg, b); + } else { + fprintf(stderr, "MonteCarlo: Invalid playout policy %s\n", optval); + } + } else { + fprintf(stderr, "MonteCarlo: Invalid engine argument %s or missing value\n", optname); + } + } + } + + if (!mc->playout) + mc->playout = playout_light_init(NULL, b); + mc->playout->debug_level = mc->debug_level; + + mc->resign_ratio = 0.1; /* Resign when most games are lost. */ + mc->loss_threshold = 5000; /* Stop reading if no loss encountered in first 5000 games. */ + + return mc; +} + + +struct engine * +engine_montecarlo_init(char *arg, struct board *b) +{ + struct montecarlo *mc = montecarlo_state_init(arg, b); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "MonteCarlo"; + e->comment = "I'm playing in Monte Carlo. When we both pass, I will consider all the stones on the board alive. If you are reading this, write 'yes'. Please bear with me at the game end, I need to fill the whole board; if you help me, we will both be happier. Filling the board will not lose points (NZ rules)."; + e->genmove = montecarlo_genmove; + e->data = mc; + + return e; +} diff --git a/jni/pachi/montecarlo/montecarlo.h b/jni/pachi/montecarlo/montecarlo.h new file mode 100644 index 0000000..ee9e614 --- /dev/null +++ b/jni/pachi/montecarlo/montecarlo.h @@ -0,0 +1,8 @@ +#ifndef PACHI_MONTECARLO_MONTECARLO_H +#define PACHI_MONTECARLO_MONTECARLO_H + +#include "engine.h" + +struct engine *engine_montecarlo_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/move.c b/jni/pachi/move.c new file mode 100644 index 0000000..a3a2021 --- /dev/null +++ b/jni/pachi/move.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "board.h" +#include "move.h" + + +/* The S_OFFBOARD margin is not addressable by coordinates. */ + +static char asdf[] = "abcdefghjklmnopqrstuvwxyz"; + +char * +coord2bstr(char *buf, coord_t c, struct board *board) +{ + if (is_pass(c)) { + return "pass"; + } else if (is_resign(c)) { + return "resign"; + } else { + /* Some GTP servers are broken and won't grok lowercase coords */ + snprintf(buf, 4, "%c%d", toupper(asdf[coord_x(c, board) - 1]), coord_y(c, board)); + return buf; + } +} + +/* Return coordinate in dynamically allocated buffer. */ +char * +coord2str(coord_t c, struct board *board) +{ + char buf[256]; + return strdup(coord2bstr(buf, c, board)); +} + +/* Return coordinate in statically allocated buffer, with some backlog for + * multiple independent invocations. Useful for debugging. */ +char * +coord2sstr(coord_t c, struct board *board) +{ + static char *b; + static char bl[10][4]; + static int bi; + b = bl[bi]; bi = (bi + 1) % 10; + return coord2bstr(b, c, board); +} + +/* No sanity checking */ +coord_t * +str2coord(char *str, int size) +{ + if (!strcasecmp(str, "pass")) { + return coord_pass(); + } else if (!strcasecmp(str, "resign")) { + return coord_resign(); + } else { + char xc = tolower(str[0]); + return coord_init(xc - 'a' - (xc > 'i') + 1, atoi(str + 1), size); + } +} diff --git a/jni/pachi/move.h b/jni/pachi/move.h new file mode 100644 index 0000000..a2b6dc6 --- /dev/null +++ b/jni/pachi/move.h @@ -0,0 +1,107 @@ +#ifndef PACHI_MOVE_H +#define PACHI_MOVE_H + +#include +#include +#include + +#include "util.h" +#include "stone.h" + +typedef int coord_t; + +#define coord_xy(board, x, y) ((x) + (y) * board_size(board)) +#define coord_x(c, b) ((b)->coord[c][0]) +#define coord_y(c, b) ((b)->coord[c][1]) +/* TODO: Smarter way to do this? */ +#define coord_dx(c1, c2, b) (coord_x(c1, b) - coord_x(c2, b)) +#define coord_dy(c1, c2, b) (coord_y(c1, b) - coord_y(c2, b)) + +static coord_t pass = -1; +static coord_t resign = -2; +#define is_pass(c) (c == pass) +#define is_resign(c) (c == resign) + +#define coord_is_adjecent(c1, c2, b) (abs(c1 - c2) == 1 || abs(c1 - c2) == board_size(b)) +#define coord_is_8adjecent(c1, c2, b) (abs(c1 - c2) == 1 || abs(abs(c1 - c2) - board_size(b)) < 2) + +/* Quadrants: + * 0 1 + * 2 3 (vertically reversed from board_print output, of course!) + * Middle coordinates are included in lower-valued quadrants. */ +#define coord_quadrant(c, b) ((coord_x(c, b) > board_size(b) / 2) + 2 * (coord_y(c, b) > board_size(b) / 2)) + +/* dyn allocated */ +static coord_t *coord_init(int x, int y, int size); +static coord_t *coord_copy(coord_t c); +static coord_t *coord_pass(void); +static coord_t *coord_resign(void); +static void coord_done(coord_t *c); + +struct board; +char *coord2bstr(char *buf, coord_t c, struct board *board); +/* Return coordinate string in a dynamically allocated buffer. Thread-safe. */ +char *coord2str(coord_t c, struct board *b); +/* Return coordinate string in a static buffer; multiple buffers are shuffled + * to enable use for multiple printf() parameters, but it is NOT safe for + * anything but debugging - in particular, it is NOT thread-safe! */ +char *coord2sstr(coord_t c, struct board *b); +coord_t *str2coord(char *str, int board_size); + + +struct move { + coord_t coord; + enum stone color; +}; + + + +static inline coord_t * +coord_init(int x, int y, int size) +{ + coord_t *c = calloc2(1, sizeof(coord_t)); + *c = x + y * size; + return c; +} + +static inline coord_t * +coord_copy(coord_t c) +{ + coord_t *c2 = calloc2(1, sizeof(coord_t)); + memcpy(c2, &c, sizeof(c)); + return c2; +} + +static inline coord_t * +coord_pass() +{ + return coord_copy(pass); +} + +static inline coord_t * +coord_resign() +{ + return coord_copy(resign); +} + +/* No sanity checking */ +static inline coord_t +str2scoord(char *str, int size) +{ + if (!strcasecmp(str, "pass")) { + return pass; + } else if (!strcasecmp(str, "resign")) { + return resign; + } else { + char xc = tolower(str[0]); + return xc - 'a' - (xc > 'i') + 1 + atoi(str + 1) * size; + } +} + +static inline void +coord_done(coord_t *c) +{ + free(c); +} + +#endif diff --git a/jni/pachi/mq.h b/jni/pachi/mq.h new file mode 100644 index 0000000..3532f6c --- /dev/null +++ b/jni/pachi/mq.h @@ -0,0 +1,85 @@ +#ifndef PACHI_MQ_H +#define PACHI_MQ_H + +/* Move queues; in fact, they are more like move lists, usually used + * to accumulate equally good move candidates, then choosing from them + * randomly. But they are also used to juggle group lists (using the + * fact that coord_t == group_t). */ + +#include "move.h" +#include "random.h" + +#define MQL 512 /* XXX: On larger board this might not be enough. */ +struct move_queue { + unsigned int moves; + coord_t move[MQL]; + /* Each move can have an optional tag or set of tags. + * The usage of these is user-dependent. */ + unsigned char tag[MQL]; +}; + +/* Pick a random move from the queue. */ +static coord_t mq_pick(struct move_queue *q); + +/* Add a move to the queue. */ +static void mq_add(struct move_queue *q, coord_t c, unsigned char tag); + +/* Cat two queues together. */ +static void mq_append(struct move_queue *qd, struct move_queue *qs); + +/* Check if the last move in queue is not a dupe, and remove it + * in that case. */ +static void mq_nodup(struct move_queue *q); + +/* Print queue contents on stderr. */ +static void mq_print(struct move_queue *q, struct board *b, char *label); + + +static inline coord_t +mq_pick(struct move_queue *q) +{ + return q->moves ? q->move[fast_random(q->moves)] : pass; +} + +static inline void +mq_add(struct move_queue *q, coord_t c, unsigned char tag) +{ + assert(q->moves < MQL); + q->tag[q->moves] = tag; + q->move[q->moves++] = c; +} + +static inline void +mq_append(struct move_queue *qd, struct move_queue *qs) +{ + assert(qd->moves + qs->moves < MQL); + memcpy(&qd->tag[qd->moves], qs->tag, qs->moves * sizeof(*qs->tag)); + memcpy(&qd->move[qd->moves], qs->move, qs->moves * sizeof(*qs->move)); + qd->moves += qs->moves; +} + +static inline void +mq_nodup(struct move_queue *q) +{ + for (unsigned int i = 1; i < 4; i++) { + if (q->moves <= i) + return; + if (q->move[q->moves - 1 - i] == q->move[q->moves - 1]) { + q->tag[q->moves - 1 - i] |= q->tag[q->moves - 1]; + q->moves--; + return; + } + } +} + +static inline void +mq_print(struct move_queue *q, struct board *b, char *label) +{ + fprintf(stderr, "%s candidate moves: ", label); + for (unsigned int i = 0; i < q->moves; i++) { + fprintf(stderr, "%s ", coord2sstr(q->move[i], b)); + } + fprintf(stderr, "\n"); +} + +#endif diff --git a/jni/pachi/network.c b/jni/pachi/network.c new file mode 100644 index 0000000..5c2b6ec --- /dev/null +++ b/jni/pachi/network.c @@ -0,0 +1,232 @@ +/* Utility functions to redirect stdin, stdout, stderr to sockets. */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#ifdef ANDROID +#include +#endif +#endif + +#include "debug.h" +#include "util.h" + +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +#define BSIZE 4096 + +static inline void +die(char *msg) +{ + perror(msg); + exit(42); +} + +/* Create a socket, bind to it on the given port and listen. + * This function is restricted to server mode (port has + * no hostname). Returns the socket. */ +int +port_listen(char *port, int max_connections) +{ + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + die("socket"); + + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(atoi(port)); + server_addr.sin_addr.s_addr = INADDR_ANY; + + const char val = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) + die("setsockopt"); + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) + die("bind"); + if (listen(sock, max_connections) == -1) + die("listen"); + return sock; +} + +/* Returns true if in private address range: 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 */ +static bool +is_private(struct in_addr *in) +{ + return (ntohl(in->s_addr) & 0xff000000) >> 24 == 10 + || (ntohl(in->s_addr) & 0xfff00000) >> 16 == 172 * 256 + 16 + || (ntohl(in->s_addr) & 0xffff0000) >> 16 == 192 * 256 + 168; +} + +/* Waits for a connection on the given socket, and returns the file descriptor. + * Updates the client address if it is not null. + * WARNING: the connection is not authenticated. As a weak security measure, + * the connections are limited to a private network. */ +int +open_server_connection(int socket, struct in_addr *client) +{ + assert(socket >= 0); + for (;;) { + struct sockaddr_in client_addr; + int sin_size = sizeof(struct sockaddr_in); + int fd = accept(socket, (struct sockaddr *)&client_addr, (socklen_t *)&sin_size); + if (fd == -1) { + die("accept"); + } + if (is_private(&client_addr.sin_addr)) { + if (client) + *client = client_addr.sin_addr; + return fd; + } + close(fd); + } +} + +/* Opens a new connection to the given port name, which must + * contain a host name. Returns the open file descriptor, + * or -1 if the open fails. */ +static int +open_client_connection(char *port_name) +{ + char hostname[BSIZE]; + strncpy(hostname, port_name, sizeof(hostname)); + char *port = strchr(hostname, ':'); + assert(port); + *port++ = '\0'; + + struct hostent *host = gethostbyname(hostname); + if (!host) + return -1; + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + die("socket"); + struct sockaddr_in sin; + memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length); + sin.sin_family = AF_INET; + sin.sin_port = htons(atoi(port)); + + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + close(sock); + return -1; + } + return sock; +} + +/* Allow connexion queue > 1 to avoid race conditions. */ +#define MAX_CONNEXIONS 5 + +struct port_info { + int socket; + char *port; +}; + +/* Wait at most 30s between connection attempts. */ +#define MAX_WAIT 30 + +/* Open a connection on the given socket/port. + * Act as server if the port doesn't contain a hostname, + * as a client otherwise. If socket < 0 or in client mode, + * create the socket from the given port and update socket. + * Block until the connection succeeds. + * Return a file descriptor for the new connection. */ +static int +open_connection(struct port_info *info) +{ + int conn; + char *p = strchr(info->port, ':'); + if (p) { + for (int try = 1;; ) { + conn = open_client_connection(info->port); + if (conn >= 0) break; + sleep(try); + if (try < MAX_WAIT) try++; + } + info->socket = conn; + } else { + if (info->socket < 0) + info->socket = port_listen(info->port, MAX_CONNEXIONS); + conn = open_server_connection(info->socket, NULL); + } + return conn; +} + +/* Open the log connection on the given port, redirect stderr to it. */ +static void +open_log_connection(struct port_info *info) +{ + int log_conn = open_connection(info); + if (dup2(log_conn, STDERR) < 0) + die("dup2"); + if (DEBUGL(0)) + fprintf(stderr, "log connection opened\n"); +} + +/* Thread keeping the log connection open and redirecting stderr to it. + * It also echoes its input, which can be used to check if the + * program is alive. As a weak identity check, in server mode the input + * must start with "Pachi" (without the quotes). */ +static void * +log_thread(void *arg) +{ + struct port_info *info = arg; + assert(info && info->port); + for (;;) { + char buf[BSIZE]; + int size; + bool check = !strchr(info->port, ':'); + if (!check) + write(STDERR, "Pachi\n", 6); + while ((size = read(STDERR, buf, BSIZE)) > 0) { + if (check && strncasecmp(buf, "Pachi", 5)) break; + check = false; + write(STDERR, buf, size); + } + fflush(stderr); + open_log_connection(info); + } +} + +/* Open the log connection on the given port, redirect stderr to it, + * and keep reopening it if the connection is closed. */ +void +open_log_port(char *port) +{ + pthread_t thread; + static struct port_info log_info = { .socket = -1 }; + log_info.port = port; + open_log_connection(&log_info); + + /* From now on, log_info may only be modified by the single + * log_thread so static allocation is ok and there is no race. */ + pthread_create(&thread, NULL, log_thread, (void *)&log_info); +} + +/* Open the gtp connection on the given port, redirect stdin & stdout to it. */ +void +open_gtp_connection(int *socket, char *port) +{ + static struct port_info gtp_info = { .socket = -1 }; + gtp_info.port = port; + int gtp_conn = open_connection(>p_info); + for (int d = STDIN; d <= STDOUT; d++) { + if (dup2(gtp_conn, d) < 0) + die("dup2"); + } + if (DEBUGL(0)) + fprintf(stderr, "gtp connection opened\n"); +} diff --git a/jni/pachi/network.h b/jni/pachi/network.h new file mode 100644 index 0000000..efbe0e3 --- /dev/null +++ b/jni/pachi/network.h @@ -0,0 +1,15 @@ +#ifndef PACHI_NETWORK_H +#define PACHI_NETWORK_H + +#ifdef _WIN32 +#include +#else +#include +#endif + +int port_listen(char *port, int max_connections); +int open_server_connection(int socket, struct in_addr *client); +void open_log_port(char *port); +void open_gtp_connection(int *socket, char *port); + +#endif diff --git a/jni/pachi/ownermap.c b/jni/pachi/ownermap.c new file mode 100644 index 0000000..906b5ed --- /dev/null +++ b/jni/pachi/ownermap.c @@ -0,0 +1,104 @@ +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "move.h" +#include "mq.h" +#include "ownermap.h" + + +void +board_ownermap_fill(struct board_ownermap *ownermap, struct board *b) +{ + ownermap->playouts++; + foreach_point(b) { + enum stone color = board_at(b, c); + if (color == S_NONE) + color = board_get_one_point_eye(b, c); + ownermap->map[c][color]++; + } foreach_point_end; +} + +void +board_ownermap_merge(int bsize2, struct board_ownermap *dst, struct board_ownermap *src) +{ + dst->playouts += src->playouts; + for (int i = 0; i < bsize2; i++) + for (int j = 0; j < S_MAX; j++) + dst->map[i][j] += src->map[i][j]; +} + +enum point_judgement +board_ownermap_judge_point(struct board_ownermap *ownermap, coord_t c, floating_t thres) +{ + assert(ownermap->map); + int n = ownermap->map[c][S_NONE]; + int b = ownermap->map[c][S_BLACK]; + int w = ownermap->map[c][S_WHITE]; + int total = ownermap->playouts; + if (n >= total * thres) + return PJ_DAME; + else if (n + b >= total * thres) + return PJ_BLACK; + else if (n + w >= total * thres) + return PJ_WHITE; + else + return PJ_UNKNOWN; +} + +void +board_ownermap_judge_groups(struct board *b, struct board_ownermap *ownermap, struct group_judgement *judge) +{ + assert(ownermap->map); + assert(judge->gs); + memset(judge->gs, GS_NONE, board_size2(b) * sizeof(judge->gs[0])); + + foreach_point(b) { + enum stone color = board_at(b, c); + group_t g = group_at(b, c); + if (!g) continue; + + enum point_judgement pj = board_ownermap_judge_point(ownermap, c, judge->thres); + // assert(judge->gs[g] == GS_NONE || judge->gs[g] == pj); + if (pj == PJ_UNKNOWN) { + /* Fate is uncertain. */ + judge->gs[g] = GS_UNKNOWN; + + } else if (judge->gs[g] != GS_UNKNOWN) { + /* Update group state. */ + enum gj_state new; + + // Comparing enum types, casting (int) avoids compiler warnings + if ((int)pj == (int)color) { + new = GS_ALIVE; + } else if ((int)pj == (int)stone_other(color)) { + new = GS_DEAD; + } else { assert(pj == PJ_DAME); + /* Exotic! */ + new = GS_UNKNOWN; + } + + if (judge->gs[g] == GS_NONE) { + judge->gs[g] = new; + } else if (judge->gs[g] != new) { + /* Contradiction. :( */ + judge->gs[g] = GS_UNKNOWN; + } + } + } foreach_point_end; +} + +void +groups_of_status(struct board *b, struct group_judgement *judge, enum gj_state s, struct move_queue *mq) +{ + foreach_point(b) { /* foreach_group, effectively */ + group_t g = group_at(b, c); + if (!g || g != c) continue; + + assert(judge->gs[g] != GS_NONE); + if (judge->gs[g] == s) + mq_add(mq, g, 0); + } foreach_point_end; +} diff --git a/jni/pachi/ownermap.h b/jni/pachi/ownermap.h new file mode 100644 index 0000000..6838b97 --- /dev/null +++ b/jni/pachi/ownermap.h @@ -0,0 +1,50 @@ +#ifndef PACHI_OWNERMAP_H +#define PACHI_OWNERMAP_H + +/* Map of board intersection owners, and devices to derive group status + * information from the map. */ + +#include // sig_atomic_t + +struct board_ownermap { + /* Map of final owners of all intersections on the board. */ + /* This may be shared between multiple threads! */ + /* XXX: We assume sig_atomic_t is thread-atomic. This may not + * be true in pathological cases. */ + sig_atomic_t playouts; + /* At the final board position, for each coordinate increase the + * counter of appropriate color. */ + sig_atomic_t (*map)[S_MAX]; // [board_size2(b)] +}; + +void board_ownermap_fill(struct board_ownermap *ownermap, struct board *b); +void board_ownermap_merge(int bsize2, struct board_ownermap *dst, struct board_ownermap *src); + + +/* Estimate coord ownership based on ownermap stats. */ +enum point_judgement { + PJ_DAME = S_NONE, + PJ_BLACK = S_BLACK, + PJ_WHITE = S_WHITE, + PJ_UNKNOWN = 3, +}; +enum point_judgement board_ownermap_judge_point(struct board_ownermap *ownermap, coord_t c, floating_t thres); + + +/* Estimate status of stones on board based on ownermap stats. */ +struct group_judgement { + floating_t thres; + enum gj_state { + GS_NONE, + GS_DEAD, + GS_ALIVE, + GS_UNKNOWN, + } *gs; // [bsize2] +}; +void board_ownermap_judge_groups(struct board *b, struct board_ownermap *ownermap, struct group_judgement *judge); + +/* Add groups of given status to mq. */ +struct move_queue; +void groups_of_status(struct board *b, struct group_judgement *judge, enum gj_state s, struct move_queue *mq); + +#endif diff --git a/jni/pachi/pachi.c b/jni/pachi/pachi.c new file mode 100644 index 0000000..643a203 --- /dev/null +++ b/jni/pachi/pachi.c @@ -0,0 +1,232 @@ +#define DEBUG +#include +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "replay/replay.h" +#include "montecarlo/montecarlo.h" +#include "random/random.h" +#include "patternscan/patternscan.h" +#include "patternplay/patternplay.h" +#include "joseki/joseki.h" +#include "t-unit/test.h" +#include "uct/uct.h" +#include "distributed/distributed.h" +#include "gtp.h" +#include "chat.h" +#include "timeinfo.h" +#include "random.h" +#include "version.h" +#include "network.h" + +int debug_level = 3; +bool debug_boardprint = true; +long verbose_logs = 0; +int seed; + + +enum engine_id { + E_RANDOM, + E_REPLAY, + E_PATTERNSCAN, + E_PATTERNPLAY, + E_MONTECARLO, + E_UCT, + E_DISTRIBUTED, + E_JOSEKI, + E_MAX, +}; + +static struct engine *(*engine_init[E_MAX])(char *arg, struct board *b) = { + engine_random_init, + engine_replay_init, + engine_patternscan_init, + engine_patternplay_init, + engine_montecarlo_init, + engine_uct_init, + engine_distributed_init, + engine_joseki_init, +}; + +static struct engine *init_engine(enum engine_id engine, char *e_arg, struct board *b) +{ + char *arg = e_arg? strdup(e_arg) : e_arg; + assert(engine < E_MAX); + struct engine *e = engine_init[engine](arg, b); + if (arg) free(arg); + return e; +} + +static void done_engine(struct engine *e) +{ + if (e->done) e->done(e); + if (e->data) free(e->data); + free(e); +} + +static void usage(char *name) +{ + fprintf(stderr, "Pachi version %s\n", PACHI_VERSION); + fprintf(stderr, "Usage: %s [-e random|replay|montecarlo|uct|distributed]\n" + " [-d DEBUG_LEVEL] [-D] [-r RULESET] [-s RANDOM_SEED] [-t TIME_SETTINGS] [-u TEST_FILENAME]\n" + " [-g [HOST:]GTP_PORT] [-l [HOST:]LOG_PORT] [-f FBOOKFILE] [ENGINE_ARGS]\n", name); +} + +int main(int argc, char *argv[]) +{ + enum engine_id engine = E_UCT; + struct time_info ti_default = { .period = TT_NULL }; + char *testfile = NULL; + char *gtp_port = NULL; + char *log_port = NULL; + int gtp_sock = -1; + char *chatfile = NULL; + char *fbookfile = NULL; + char *ruleset = NULL; + + seed = time(NULL) ^ getpid(); + + int opt; + while ((opt = getopt(argc, argv, "c:e:d:Df:g:l:r:s:t:u:")) != -1) { + switch (opt) { + case 'c': + chatfile = strdup(optarg); + break; + case 'e': + if (!strcasecmp(optarg, "random")) { + engine = E_RANDOM; + } else if (!strcasecmp(optarg, "replay")) { + engine = E_REPLAY; + } else if (!strcasecmp(optarg, "montecarlo")) { + engine = E_MONTECARLO; + } else if (!strcasecmp(optarg, "uct")) { + engine = E_UCT; + } else if (!strcasecmp(optarg, "distributed")) { + engine = E_DISTRIBUTED; + } else if (!strcasecmp(optarg, "patternscan")) { + engine = E_PATTERNSCAN; + } else if (!strcasecmp(optarg, "patternplay")) { + engine = E_PATTERNPLAY; + } else if (!strcasecmp(optarg, "joseki")) { + engine = E_JOSEKI; + } else { + fprintf(stderr, "%s: Invalid -e argument %s\n", argv[0], optarg); + exit(1); + } + break; + case 'd': + debug_level = atoi(optarg); + break; + case 'D': + debug_boardprint = false; + break; + case 'f': + fbookfile = strdup(optarg); + break; + case 'g': + gtp_port = strdup(optarg); + break; + case 'l': + log_port = strdup(optarg); + break; + case 'r': + ruleset = strdup(optarg); + break; + case 's': + seed = atoi(optarg); + break; + case 't': + /* Time settings to follow; if specified, + * GTP time information is ignored. Useful + * e.g. when you want to force your bot to + * play weaker while giving the opponent + * reasonable time to play, or force play + * by number of simulations in timed games. */ + /* Please see timeinfo.h:time_parse() + * description for syntax details. */ + if (!time_parse(&ti_default, optarg)) { + fprintf(stderr, "%s: Invalid -t argument %s\n", argv[0], optarg); + exit(1); + } + ti_default.ignore_gtp = true; + assert(ti_default.period != TT_NULL); + break; + case 'u': + testfile = strdup(optarg); + break; + default: /* '?' */ + usage(argv[0]); + exit(1); + } + } + + if (log_port) + open_log_port(log_port); + + fast_srandom(seed); + if (DEBUGL(0)) + fprintf(stderr, "Random seed: %d\n", seed); + + struct board *b = board_init(fbookfile); + if (ruleset) { + if (!board_set_rules(b, ruleset)) { + fprintf(stderr, "Unknown ruleset: %s\n", ruleset); + exit(1); + } + } + + struct time_info ti[S_MAX]; + ti[S_BLACK] = ti_default; + ti[S_WHITE] = ti_default; + + chat_init(chatfile); + + char *e_arg = NULL; + if (optind < argc) + e_arg = argv[optind]; + struct engine *e = init_engine(engine, e_arg, b); + + if (testfile) { + unittest(testfile); + return 0; + } + + if (gtp_port) { + open_gtp_connection(>p_sock, gtp_port); + } + + for (;;) { + char buf[4096]; + while (fgets(buf, 4096, stdin)) { + if (DEBUGL(1)) + fprintf(stderr, "IN: %s", buf); + + enum parse_code c = gtp_parse(b, e, ti, buf); + if (c == P_ENGINE_RESET) { + ti[S_BLACK] = ti_default; + ti[S_WHITE] = ti_default; + if (!e->keep_on_clear) { + b->es = NULL; + done_engine(e); + e = init_engine(engine, e_arg, b); + } + } else if (c == P_UNKNOWN_COMMAND && gtp_port) { + /* The gtp command is a weak identity check, + * close the connection with a wrong peer. */ + break; + } + } + if (!gtp_port) break; + open_gtp_connection(>p_sock, gtp_port); + } + done_engine(e); + chat_done(); + return 0; +} diff --git a/jni/pachi/pattern.c b/jni/pachi/pattern.c new file mode 100644 index 0000000..e4f9cd7 --- /dev/null +++ b/jni/pachi/pattern.c @@ -0,0 +1,584 @@ +#define DEBUG +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "pattern.h" +#include "patternsp.h" +#include "patternprob.h" +#include "tactics/ladder.h" +#include "tactics/selfatari.h" +#include "tactics/util.h" +#ifdef ANDROID +#include "android/util.h" +#endif + +#define CAPTURE_COUNTSTONES_MAX ((1 << CAPTURE_COUNTSTONES_PAYLOAD_SIZE) - 1) + + +struct pattern_config DEFAULT_PATTERN_CONFIG = { + .bdist_max = 4, + + .spat_min = 3, .spat_max = MAX_PATTERN_DIST, + .spat_largest = true, +}; + +#define PF_MATCH 15 + +pattern_spec PATTERN_SPEC_MATCH_DEFAULT = { + [FEAT_CAPTURE] = ~0, + [FEAT_AESCAPE] = ~0, + [FEAT_SELFATARI] = ~0, + [FEAT_ATARI] = ~0, + [FEAT_BORDER] = ~0, + [FEAT_CONTIGUITY] = 0, + [FEAT_SPATIAL] = ~0, +}; + +static const struct feature_info { + char *name; + int payloads; +} features[FEAT_MAX] = { + [FEAT_CAPTURE] = { .name = "capture", .payloads = 64 }, + [FEAT_AESCAPE] = { .name = "atariescape", .payloads = 16 }, + [FEAT_SELFATARI] = { .name = "selfatari", .payloads = 4 }, + [FEAT_ATARI] = { .name = "atari", .payloads = 4 }, + [FEAT_BORDER] = { .name = "border", .payloads = -1 }, + [FEAT_CONTIGUITY] = { .name = "cont", .payloads = 2 }, + [FEAT_SPATIAL] = { .name = "s", .payloads = -1 }, +}; + +char * +feature2str(char *str, struct feature *f) +{ + return str + sprintf(str + strlen(str), "%s:%d", features[f->id].name, f->payload); +} + +char * +str2feature(char *str, struct feature *f) +{ + while (isspace(*str)) str++; + + int unsigned flen = strcspn(str, ":"); + for (unsigned int i = 0; i < sizeof(features)/sizeof(features[0]); i++) + if (strlen(features[i].name) == flen && !strncmp(features[i].name, str, flen)) { + f->id = i; + goto found; + } + fprintf(stderr, "invalid featurespec: %s[%d]\n", str, flen); + exit(EXIT_FAILURE); + +found: + str += flen + 1; + f->payload = strtoull(str, &str, 10); + return str; +} + +char * +feature_name(enum feature_id f) +{ + return features[f].name; +} + +int +feature_payloads(struct pattern_setup *pat, enum feature_id f) +{ + switch (f) { + int payloads; + case FEAT_CAPTURE: + payloads = features[f].payloads; + if (pat->ps[FEAT_CAPTURE] & (1<pc.spat_dict->nspatials; + case FEAT_BORDER: + assert(features[f].payloads < 0); + return pat->pc.bdist_max + 1; + default: + assert(features[f].payloads > 0); + return features[f].payloads; + } +} + + +void +patterns_init(struct pattern_setup *pat, char *arg, bool will_append, bool load_prob) +{ + char *pdict_file = NULL; + + memset(pat, 0, sizeof(*pat)); + + pat->pc = DEFAULT_PATTERN_CONFIG; + pat->pc.spat_dict = spatial_dict_init(will_append, !load_prob); + + memcpy(&pat->ps, PATTERN_SPEC_MATCH_DEFAULT, sizeof(pattern_spec)); + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + /* See pattern.h:pattern_config for description and + * pattern.c:DEFAULT_PATTERN_CONFIG for default values + * of the following options. */ + if (!strcasecmp(optname, "bdist_max") && optval) { + pat->pc.bdist_max = atoi(optval); + } else if (!strcasecmp(optname, "spat_min") && optval) { + pat->pc.spat_min = atoi(optval); + } else if (!strcasecmp(optname, "spat_max") && optval) { + pat->pc.spat_max = atoi(optval); + } else if (!strcasecmp(optname, "spat_largest")) { + pat->pc.spat_largest = !optval || atoi(optval); + + } else if (!strcasecmp(optname, "pdict_file") && optval) { + pdict_file = optval; + + } else { + fprintf(stderr, "patterns: Invalid argument %s or missing value\n", optname); + exit(EXIT_FAILURE); + } + } + } + + if (load_prob && pat->pc.spat_dict) { + pat->pd = pattern_pdict_init(pdict_file, &pat->pc); + } +} + + +/* pattern_spec helpers */ +#define PS_ANY(F) (ps[FEAT_ ## F] & (1 << PF_MATCH)) +#define PS_PF(F, P) (ps[FEAT_ ## F] & (1 << PF_ ## F ## _ ## P)) + +static struct feature * +pattern_match_capture(struct pattern_config *pc, pattern_spec ps, + struct pattern *p, struct feature *f, + struct board *b, struct move *m) +{ + f->id = FEAT_CAPTURE; f->payload = 0; +#ifdef BOARD_TRAITS + if (!trait_at(b, m->coord, m->color).cap) + return f; + /* Capturable! */ + if ((ps[FEAT_CAPTURE] & ~(1<payload |= (trait_at(b, m->coord, m->color).cap1 == trait_at(b, m->coord, m->color).cap) << PF_CAPTURE_1STONE; + if (PS_PF(CAPTURE, TRAPPED)) + f->payload |= (!trait_at(b, m->coord, stone_other(m->color)).safe) << PF_CAPTURE_TRAPPED; + if (PS_PF(CAPTURE, CONNECTION)) + f->payload |= (trait_at(b, m->coord, m->color).cap < neighbor_count_at(b, m->coord, stone_other(m->color))) << PF_CAPTURE_CONNECTION; + (f++, p->n++); + return f; + } + /* We need to know details, so we still have to go through + * the neighbors. */ +#endif + + /* We look at neighboring groups we could capture, and also if the + * opponent could save them. */ + /* This is very similar in spirit to board_safe_to_play(), and almost + * a color inverse of pattern_match_aescape(). */ + + /* Whether an escape move would be safe for the opponent. */ + int captures = 0; + coord_t onelib = -1; + int extra_libs = 0, connectable_groups = 0; + bool onestone = false, multistone = false; + int captured_stones = 0; + + foreach_neighbor(b, m->coord, { + if (board_at(b, c) != stone_other(m->color)) { + if (board_at(b, c) == S_NONE) + extra_libs++; // free point + else if (board_at(b, c) == m->color && board_group_info(b, group_at(b, c)).libs == 1) + extra_libs += 2; // capturable enemy group + continue; + } + + group_t g = group_at(b, c); assert(g); + if (board_group_info(b, g).libs > 1) { + connectable_groups++; + if (board_group_info(b, g).libs > 2) { + extra_libs += 2; // connected out + } else { + /* This is a bit tricky; we connect our 2-lib + * group to another 2-lib group, which counts + * as one liberty, but only if the other lib + * is not shared too. */ + if (onelib == -1) { + onelib = board_group_other_lib(b, g, c); + extra_libs++; + } else { + if (c == onelib) + extra_libs--; // take that back + else + extra_libs++; + } + } + continue; + } + + /* Capture! */ + captures++; + + if (PS_PF(CAPTURE, LADDER)) + f->payload |= is_ladder(b, m->coord, g, true) << PF_CAPTURE_LADDER; + /* TODO: is_ladder() is too conservative in some + * very obvious situations, look at complete.gtp. */ + + if (PS_PF(CAPTURE, ATARIDEF)) + foreach_in_group(b, g) { + foreach_neighbor(b, c, { + assert(board_at(b, c) != S_NONE || c == m->coord); + if (board_at(b, c) != m->color) + continue; + group_t g = group_at(b, c); + if (!g || board_group_info(b, g).libs != 1) + continue; + /* A neighboring group of ours is in atari. */ + f->payload |= 1 << PF_CAPTURE_ATARIDEF; + }); + } foreach_in_group_end; + + if (PS_PF(CAPTURE, KO) + && group_is_onestone(b, g) + && neighbor_count_at(b, m->coord, stone_other(m->color)) + + neighbor_count_at(b, m->coord, S_OFFBOARD) == 4) + f->payload |= 1 << PF_CAPTURE_KO; + + if (PS_PF(CAPTURE, COUNTSTONES) + && captured_stones < CAPTURE_COUNTSTONES_MAX) + captured_stones += group_stone_count(b, g, CAPTURE_COUNTSTONES_MAX - captured_stones); + + if (group_is_onestone(b, g)) + onestone = true; + else + multistone = true; + }); + + if (captures > 0) { + if (PS_PF(CAPTURE, 1STONE)) + f->payload |= (onestone && !multistone) << PF_CAPTURE_1STONE; + if (PS_PF(CAPTURE, TRAPPED)) + f->payload |= (extra_libs < 2) << PF_CAPTURE_TRAPPED; + if (PS_PF(CAPTURE, CONNECTION)) + f->payload |= (connectable_groups > 0) << PF_CAPTURE_CONNECTION; + if (PS_PF(CAPTURE, COUNTSTONES)) + f->payload |= captured_stones << PF_CAPTURE_COUNTSTONES; + (f++, p->n++); + } + return f; +} + +static struct feature * +pattern_match_aescape(struct pattern_config *pc, pattern_spec ps, + struct pattern *p, struct feature *f, + struct board *b, struct move *m) +{ + f->id = FEAT_AESCAPE; f->payload = 0; +#ifdef BOARD_TRAITS + if (!trait_at(b, m->coord, stone_other(m->color)).cap) + return f; + /* Opponent can capture something! */ + if ((ps[FEAT_AESCAPE] & ~(1<payload |= (trait_at(b, m->coord, stone_other(m->color)).cap1 == trait_at(b, m->coord, stone_other(m->color)).cap) << PF_AESCAPE_1STONE; + if (PS_PF(AESCAPE, TRAPPED)) + f->payload |= (!trait_at(b, m->coord, m->color).safe) << PF_AESCAPE_TRAPPED; + if (PS_PF(AESCAPE, CONNECTION)) + f->payload |= (trait_at(b, m->coord, stone_other(m->color)).cap < neighbor_count_at(b, m->coord, m->color)) << PF_AESCAPE_CONNECTION; + (f++, p->n++); + return f; + } + /* We need to know details, so we still have to go through + * the neighbors. */ +#endif + + /* Find if a neighboring group of ours is in atari, AND that we provide + * a liberty to connect out. XXX: No connect-and-die check. */ + /* This is very similar in spirit to board_safe_to_play(). */ + group_t in_atari = -1; + coord_t onelib = -1; + int extra_libs = 0, connectable_groups = 0; + bool onestone = false, multistone = false; + + foreach_neighbor(b, m->coord, { + if (board_at(b, c) != m->color) { + if (board_at(b, c) == S_NONE) + extra_libs++; // free point + else if (board_at(b, c) == stone_other(m->color) && board_group_info(b, group_at(b, c)).libs == 1) { + extra_libs += 2; // capturable enemy group + /* XXX: We just consider this move safe + * unconditionally. */ + } + continue; + } + group_t g = group_at(b, c); assert(g); + if (board_group_info(b, g).libs > 1) { + connectable_groups++; + if (board_group_info(b, g).libs > 2) { + extra_libs += 2; // connected out + } else { + /* This is a bit tricky; we connect our 2-lib + * group to another 2-lib group, which counts + * as one liberty, but only if the other lib + * is not shared too. */ + if (onelib == -1) { + onelib = board_group_other_lib(b, g, c); + extra_libs++; + } else { + if (c == onelib) + extra_libs--; // take that back + else + extra_libs++; + } + } + continue; + } + + /* In atari! */ + in_atari = g; + + if (PS_PF(AESCAPE, LADDER)) + f->payload |= is_ladder(b, m->coord, g, true) << PF_AESCAPE_LADDER; + /* TODO: is_ladder() is too conservative in some + * very obvious situations, look at complete.gtp. */ + + if (group_is_onestone(b, g)) + onestone = true; + else + multistone = true; + }); + + if (in_atari >= 0) { + if (PS_PF(AESCAPE, 1STONE)) + f->payload |= (onestone && !multistone) << PF_AESCAPE_1STONE; + if (PS_PF(AESCAPE, TRAPPED)) + f->payload |= (extra_libs < 2) << PF_AESCAPE_TRAPPED; + if (PS_PF(AESCAPE, CONNECTION)) + f->payload |= (connectable_groups > 0) << PF_AESCAPE_CONNECTION; + (f++, p->n++); + } + return f; +} + +static struct feature * +pattern_match_atari(struct pattern_config *pc, pattern_spec ps, + struct pattern *p, struct feature *f, + struct board *b, struct move *m) +{ + foreach_neighbor(b, m->coord, { + if (board_at(b, c) != stone_other(m->color)) + continue; + group_t g = group_at(b, c); + if (!g || board_group_info(b, g).libs != 2) + continue; + + /* Can atari! */ + f->id = FEAT_ATARI; f->payload = 0; + + if (PS_PF(ATARI, LADDER)) { + /* Opponent will escape by the other lib. */ + coord_t lib = board_group_other_lib(b, g, m->coord); + /* TODO: is_ladder() is too conservative in some + * very obvious situations, look at complete.gtp. */ + f->payload |= wouldbe_ladder(b, g, lib, m->coord, stone_other(m->color)) << PF_ATARI_LADDER; + } + + if (PS_PF(ATARI, KO) && !is_pass(b->ko.coord)) + f->payload |= 1 << PF_ATARI_KO; + + (f++, p->n++); + }); + return f; +} + +#ifndef BOARD_SPATHASH +#undef BOARD_SPATHASH_MAXD +#define BOARD_SPATHASH_MAXD 1 +#endif + +/* Match spatial features that are too distant to be pre-matched + * incrementally. */ +struct feature * +pattern_match_spatial_outer(struct pattern_config *pc, pattern_spec ps, + struct pattern *p, struct feature *f, + struct board *b, struct move *m, hash_t h) +{ + /* We record all spatial patterns black-to-play; simply + * reverse all colors if we are white-to-play. */ + static enum stone bt_black[4] = { S_NONE, S_BLACK, S_WHITE, S_OFFBOARD }; + static enum stone bt_white[4] = { S_NONE, S_WHITE, S_BLACK, S_OFFBOARD }; + enum stone (*bt)[4] = m->color == S_WHITE ? &bt_white : &bt_black; + + for (unsigned int d = BOARD_SPATHASH_MAXD + 1; d <= pc->spat_max; d++) { + /* Recompute missing outer circles: + * Go through all points in given distance. */ + for (unsigned int j = ptind[d]; j < ptind[d + 1]; j++) { + ptcoords_at(x, y, m->coord, b, j); + h ^= pthashes[0][j][(*bt)[board_atxy(b, x, y)]]; + } + if (d < pc->spat_min) + continue; + /* Record spatial feature, one per distance. */ + unsigned int sid = spatial_dict_get(pc->spat_dict, d, h & spatial_hash_mask); + if (sid > 0) { + f->id = FEAT_SPATIAL; + f->payload = sid; + if (!pc->spat_largest) + (f++, p->n++); + } /* else not found, ignore */ + } + return f; +} + +struct feature * +pattern_match_spatial(struct pattern_config *pc, pattern_spec ps, + struct pattern *p, struct feature *f, + struct board *b, struct move *m) +{ + /* XXX: This is partially duplicated from spatial_from_board(), but + * we build a hash instead of spatial record. */ + + assert(pc->spat_min > 0); + f->id = -1; + + hash_t h = pthashes[0][0][S_NONE]; +#ifdef BOARD_SPATHASH + bool w_to_play = m->color == S_WHITE; + for (int d = 2; d <= BOARD_SPATHASH_MAXD; d++) { + /* Reuse all incrementally matched data. */ + h ^= b->spathash[m->coord][d - 1][w_to_play]; + if (d < pc->spat_min) + continue; + /* Record spatial feature, one per distance. */ + unsigned int sid = spatial_dict_get(pc->spat_dict, d, h & spatial_hash_mask); + if (sid > 0) { + f->id = FEAT_SPATIAL; + f->payload = sid; + if (!pc->spat_largest) + (f++, p->n++); + } /* else not found, ignore */ + } +#else + assert(BOARD_SPATHASH_MAXD < 2); +#endif + if (unlikely(pc->spat_max > BOARD_SPATHASH_MAXD)) + f = pattern_match_spatial_outer(pc, ps, p, f, b, m, h); + if (pc->spat_largest && f->id == FEAT_SPATIAL) + (f++, p->n++); + return f; +} + + +void +pattern_match(struct pattern_config *pc, pattern_spec ps, + struct pattern *p, struct board *b, struct move *m) +{ + p->n = 0; + struct feature *f = &p->f[0]; + + /* TODO: We should match pretty much all of these features + * incrementally. */ + + if (PS_ANY(CAPTURE)) { + f = pattern_match_capture(pc, ps, p, f, b, m); + } + + if (PS_ANY(AESCAPE)) { + f = pattern_match_aescape(pc, ps, p, f, b, m); + } + + if (PS_ANY(SELFATARI)) { + bool simple = false; + if (PS_PF(SELFATARI, STUPID)) { +#ifdef BOARD_TRAITS + if (!b->precise_selfatari) + simple = !trait_at(b, m->coord, m->color).safe; + else +#endif + simple = !board_safe_to_play(b, m->coord, m->color); + } + bool thorough = false; + if (PS_PF(SELFATARI, SMART)) { +#ifdef BOARD_TRAITS + if (b->precise_selfatari) + thorough = !trait_at(b, m->coord, m->color).safe; + else +#endif + thorough = is_bad_selfatari(b, m->color, m->coord); + } + if (simple || thorough) { + f->id = FEAT_SELFATARI; + f->payload = simple << PF_SELFATARI_STUPID; + f->payload |= thorough << PF_SELFATARI_SMART; + (f++, p->n++); + } + } + + if (PS_ANY(ATARI)) { + f = pattern_match_atari(pc, ps, p, f, b, m); + } + + if (PS_ANY(BORDER)) { + unsigned int bdist = coord_edge_distance(m->coord, b); + if (bdist <= pc->bdist_max) { + f->id = FEAT_BORDER; + f->payload = bdist; + (f++, p->n++); + } + } + + if (PS_ANY(CONTIGUITY) && !is_pass(b->last_move.coord) + && coord_is_8adjecent(m->coord, b->last_move.coord, b)) { + f->id = FEAT_CONTIGUITY; + f->payload = 1; + (f++, p->n++); + } + + if (PS_ANY(SPATIAL) && pc->spat_max > 0 && pc->spat_dict) { + f = pattern_match_spatial(pc, ps, p, f, b, m); + } +} + +char * +pattern2str(char *str, struct pattern *p) +{ + str = stpcpy(str, "("); + for (int i = 0; i < p->n; i++) { + if (i > 0) str = stpcpy(str, " "); + str = feature2str(str, &p->f[i]); + } + str = stpcpy(str, ")"); + return str; +} + +char * +str2pattern(char *str, struct pattern *p) +{ + p->n = 0; + while (isspace(*str)) str++; + if (*str++ != '(') { + fprintf(stderr, "invalid patternspec: %s\n", str); + exit(EXIT_FAILURE); + } + + while (*str != ')') { + str = str2feature(str, &p->f[p->n++]); + } + + str++; + return str; +} diff --git a/jni/pachi/pattern.h b/jni/pachi/pattern.h new file mode 100644 index 0000000..94c3310 --- /dev/null +++ b/jni/pachi/pattern.h @@ -0,0 +1,179 @@ +#ifndef PACHI_PATTERN_H +#define PACHI_PATTERN_H + +/* Matching of multi-featured patterns. */ + +#include "board.h" +#include "move.h" + +/* When someone says "pattern", you imagine a configuration of stones in given + * area (e.g. as matched very efficiently by pattern3 in case of 3x3 area). + * However, we use a richer definition of pattern, where this is merely one + * pattern _feature_. Another features may be is-a-selfatari, is-a-capture, + * number of liberties, distance from last move, etc. */ + +/* Each feature is represented by its id and an optional 32-bit payload; + * when matching, discrete (id,payload) pairs are considered. */ + +/* This is heavily influenced by (Coulom, 2007), of course. In addition, + * the work of van der Werf, de Groot, Stern et al. and possibly others + * inspired this pattern matcher. */ +/* TODO: Try completely separate ko / no-ko features. And many other + * features described in the literature. */ + +/* See the HACKING file for another description of the pattern matcher and + * instructions on how to harvest and inspect patterns. */ + +/* If you add a payload bit for a feature, don't forget to update the value + * in feature_info. */ +enum feature_id { + /* Implemented: */ + + /* Simple capture move. */ + /* Payload: [bit0] Capturing laddered group? */ +#define PF_CAPTURE_LADDER 0 + /* [bit1] Enables our atari group get more libs? */ +#define PF_CAPTURE_ATARIDEF 1 + /* [bit2] Capturing ko? */ +#define PF_CAPTURE_KO 2 + /* [bit3] Single-stone group? */ +#define PF_CAPTURE_1STONE 3 + /* [bit4] Unsafe move for opponent? */ +#define PF_CAPTURE_TRAPPED 4 + /* [bit5] Preventing connection to an outside group. */ +#define PF_CAPTURE_CONNECTION 5 + /* [bit6] Are we counting captured stones? */ +#define PF_CAPTURE_COUNTSTONES 6 + /* How many bits of payload are used for counting captured stones. */ +#define CAPTURE_COUNTSTONES_PAYLOAD_SIZE 4 /* that is, payload bits 6,7,8,9 */ + FEAT_CAPTURE, + + /* Atari escape (extension). */ + /* Payload: [bit0] Escaping with laddered group? */ +#define PF_AESCAPE_LADDER 0 + /* [bit1] Single-stone group? */ +#define PF_AESCAPE_1STONE 1 + /* [bit2] Unsafe move for us? */ +#define PF_AESCAPE_TRAPPED 2 + /* [bit3] Connecting out to an outside group. */ +#define PF_AESCAPE_CONNECTION 3 + FEAT_AESCAPE, + + /* Self-atari move. */ + /* Payload: [bit0] Matched by trivial definition? */ + /* [bit1] Matched by complex definition? (tries to be aware of nakade, throwins, ...) */ +#define PF_SELFATARI_STUPID 0 +#define PF_SELFATARI_SMART 1 + FEAT_SELFATARI, + + /* Atari move. */ + /* Payload: [bit0] The atari'd group gets laddered? */ +#define PF_ATARI_LADDER 0 + /* [bit1] Playing ko? */ +#define PF_ATARI_KO 1 + FEAT_ATARI, + + /* Border distance. */ + /* Payload: The distance - "line number". Only up to 4. */ + FEAT_BORDER, + + /* Continuity. */ + /* Payload: [bit0] The move is in 8-neighborhood of last move. */ + FEAT_CONTIGUITY, + + /* Spatial configuration of stones in certain board area, + * with black to play. */ + /* Payload: Index in the spatial_dict. */ + FEAT_SPATIAL, + + /* TODO: MC owner, playing ko, #liberties, #libs of opponent, ... */ + + FEAT_MAX +}; + +struct feature { + enum feature_id id:8; + unsigned int payload:24; +}; + +struct pattern { + /* Pattern (matched) is set of features. */ + int n; + /* XXX: Should be at least 6 + spat_max-spat_min if spat_largest + * is false! However, this has large effect on consumed memory. */ +#define FEATURES 18 // XXX: can be just 8 if spat_largest is true + struct feature f[FEATURES]; +}; + +struct spatial_dict; +struct pattern_config { + /* FEAT_BORDER: Generate features only up to this board distance. */ + unsigned int bdist_max; + + /* FEAT_SPATIAL: Generate patterns only for these sizes (gridcular). + * TODO: Special-case high values to match larger areas or the + * whole board. */ + unsigned int spat_min, spat_max; + /* Produce only a single spatial feature per pattern, corresponding + * to the largest matched spatial pattern. */ + bool spat_largest; + /* The spatial patterns dictionary used by FEAT_SPATIAL. */ + struct spatial_dict *spat_dict; +}; +extern struct pattern_config DEFAULT_PATTERN_CONFIG; + +/* The pattern_spec[] specifies which features to tests for; + * highest bit controls whether to test for the feature at all, + * then for bitmap features (except FEAT_SPATIAL) the rest + * of the bits controls various PF tests; for non-bitmap + * features, you will need to tweak the patternconfig to + * fine-tune them. */ +typedef uint16_t pattern_spec[FEAT_MAX]; +/* Match (almost?) all supported features. */ +extern pattern_spec PATTERN_SPEC_MATCH_DEFAULT; + + +/* General structure describing a loaded pattern configuration + * with all its attributes. */ +struct pattern_pdict; +struct pattern_setup { + struct pattern_config pc; + pattern_spec ps; + struct pattern_pdict *pd; +}; + +void patterns_init(struct pattern_setup *pat, char *arg, bool will_append, bool load_prob); + + +/* Append feature to string. */ +char *feature2str(char *str, struct feature *f); +/* Convert string to feature, return pointer after the featurespec. */ +char *str2feature(char *str, struct feature *f); +/* Get name of given feature. */ +char *feature_name(enum feature_id f); +/* Get number of possible payload values associated with the feature. */ +int feature_payloads(struct pattern_setup *pat, enum feature_id f); + +/* Append pattern as feature spec string. */ +char *pattern2str(char *str, struct pattern *p); +/* Convert string to pattern, return pointer after the patternspec. */ +char *str2pattern(char *str, struct pattern *p); +/* Compare two patterns for equality. Assumes fixed feature order. */ +static bool pattern_eq(struct pattern *p1, struct pattern *p2); + +/* Initialize p and fill it with features matched by the + * given board move. */ +void pattern_match(struct pattern_config *pc, pattern_spec ps, struct pattern *p, struct board *b, struct move *m); + + +static inline bool +pattern_eq(struct pattern *p1, struct pattern *p2) +{ + if (p1->n != p2->n) return false; + for (int i = 0; i < p1->n; i++) + if (p1->f[i].id != p2->f[i].id || p1->f[i].payload != p2->f[i].payload) + return false; + return true; +} + +#endif diff --git a/jni/pachi/pattern3.c b/jni/pachi/pattern3.c new file mode 100644 index 0000000..3915f00 --- /dev/null +++ b/jni/pachi/pattern3.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "pattern3.h" + + +static void +pattern_record(struct pattern3s *p, char *str, hash3_t pat, int fixed_color) +{ + hash_t h = hash3_to_hash(pat); + while (p->hash[h & pattern3_hash_mask].pattern != pat + && p->hash[h & pattern3_hash_mask].value != 0) + h++; +#if 0 + if (h != hash3_to_hash(pat) && p->hash[h & pattern3_hash_mask].pattern != pat) + fprintf(stderr, "collision of %06x: %llx(%x)\n", pat, hash3_to_hash(pat)&pattern3_hash_mask, p->hash[hash3_to_hash(pat)&pattern3_hash_mask].pattern); +#endif + p->hash[h & pattern3_hash_mask].pattern = pat; + p->hash[h & pattern3_hash_mask].value = fixed_color ? fixed_color : 3; + //fprintf(stderr, "[%s] %04x %d\n", str, pat, fixed_color); +} + +static int +pat_vmirror(hash3_t pat) +{ + /* V mirror pattern; reverse order of 3-2-3 color chunks and + * 1-2-1 atari chunks */ + return ((pat & 0xfc00) >> 10) | (pat & 0x03c0) | ((pat & 0x003f) << 10) + | ((pat & 0x80000) >> 3) | (pat & 0x60000) | ((pat & 0x10000) << 3); +} + +static int +pat_hmirror(hash3_t pat) +{ + /* H mirror pattern; reverse order of 2-bit values within the chunks, + * and the 2-bit middle atari chunk. */ +#define rev3(p) ((p >> 4) | (p & 0xc) | ((p & 0x3) << 4)) +#define rev2(p) ((p >> 2) | ((p & 0x3) << 2)) + return (rev3((pat & 0xfc00) >> 10) << 10) + | (rev2((pat & 0x03c0) >> 6) << 6) + | rev3((pat & 0x003f)) + | ((pat & 0x20000) << 1) + | ((pat & 0x40000) >> 1) + | (pat & 0x90000); +#undef rev3 +#undef rev2 +} + +static int +pat_90rot(hash3_t pat) +{ + /* Rotate by 90 degrees: + * 5 6 7 3 7 4 2 2 + * 3 4 1 2 -> 6 1 -> 3 0 + * 0 1 2 0 5 3 0 1 */ + /* I'm too lazy to optimize this :) */ + + int p2 = 0; + + /* Stone info */ + int vals[8]; + for (int i = 0; i < 8; i++) + vals[i] = (pat >> (i * 2)) & 0x3; + int vals2[8]; + vals2[0] = vals[5]; vals2[1] = vals[3]; vals2[2] = vals[0]; + vals2[3] = vals[6]; vals2[4] = vals[1]; + vals2[5] = vals[7]; vals2[6] = vals[4]; vals2[7] = vals[2]; + for (int i = 0; i < 8; i++) + p2 |= vals2[i] << (i * 2); + + /* Atari info */ + int avals[4]; + for (int i = 0; i < 4; i++) + avals[i] = (pat >> (16 + i)) & 0x1; + int avals2[4]; + avals2[3] = avals[2]; + avals2[1] = avals[3]; avals2[2] = avals[0]; + avals2[0] = avals[1]; + for (int i = 0; i < 4; i++) + p2 |= avals2[i] << (16 + i); + + return p2; +} + +void +pattern3_transpose(hash3_t pat, hash3_t (*transp)[8]) +{ + int i = 0; + (*transp)[i++] = pat; + (*transp)[i++] = pat_vmirror(pat); + (*transp)[i++] = pat_hmirror(pat); + (*transp)[i++] = pat_vmirror(pat_hmirror(pat)); + (*transp)[i++] = pat_90rot(pat); + (*transp)[i++] = pat_90rot(pat_vmirror(pat)); + (*transp)[i++] = pat_90rot(pat_hmirror(pat)); + (*transp)[i++] = pat_90rot(pat_vmirror(pat_hmirror(pat))); +} + +static void +pattern_gen(struct pattern3s *p, hash3_t pat, char *src, int srclen, int fixed_color) +{ + for (; srclen > 0; src++, srclen--) { + if (srclen == 5) + continue; + static const int ataribits[] = { -1, 0, -1, 1, 2, -1, 3, -1 }; + int patofs = (srclen > 5 ? srclen - 1 : srclen) - 1; + switch (*src) { + /* Wildcards. */ + case '?': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'X'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'O'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '?'; // for future recursions + return; + case 'x': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'O'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'x'; // for future recursions + return; + case 'o': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'X'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'o'; // for future recursions + return; + + case 'X': + *src = 'Y'; pattern_gen(p, pat, src, srclen, fixed_color); + if (ataribits[patofs] >= 0) + *src = '|'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'X'; // for future recursions + return; + case 'O': + *src = 'Q'; pattern_gen(p, pat, src, srclen, fixed_color); + if (ataribits[patofs] >= 0) + *src = '@'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'O'; // for future recursions + return; + + case 'y': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '|'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'O'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'y'; // for future recursions + return; + case 'q': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '@'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'X'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'q'; // for future recursions + return; + + case '=': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'Y'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'O'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '='; // for future recursions + return; + case '0': + *src = '.'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'Q'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = 'X'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '#'; pattern_gen(p, pat, src, srclen, fixed_color); + *src = '0'; // for future recursions + return; + + /* Atoms. */ + case '.': /* 0 */ break; + case 'Y': pat |= S_BLACK << (patofs * 2); break; + case 'Q': pat |= S_WHITE << (patofs * 2); break; + case '|': assert(ataribits[patofs] >= 0); + pat |= (S_BLACK << (patofs * 2)) | (1 << (16 + ataribits[patofs])); + break; + case '@': assert(ataribits[patofs] >= 0); + pat |= (S_WHITE << (patofs * 2)) | (1 << (16 + ataribits[patofs])); + break; + case '#': pat |= S_OFFBOARD << (patofs * 2); break; + } + } + + /* Original pattern, all transpositions and rotations */ + hash3_t transp[8]; + pattern3_transpose(pat, &transp); + for (int i = 0; i < 8; i++) { + /* Original color assignment */ + pattern_record(p, src - 9, transp[i], fixed_color); + /* Reverse color assignment */ + if (fixed_color) + fixed_color = 2 - (fixed_color == 2); + pattern_record(p, src - 9, pattern3_reverse(transp[i]), fixed_color); + } +} + +static void +patterns_gen(struct pattern3s *p, char src[][11], int src_n) +{ + for (int i = 0; i < src_n; i++) { + //printf("<%s>\n", src[i]); + int fixed_color = 0; + switch (src[i][9]) { + case 'X': fixed_color = S_BLACK; break; + case 'O': fixed_color = S_WHITE; break; + } + //fprintf(stderr, "** %s **\n", src[i]); + pattern_gen(p, 0, src[i], 9, fixed_color); + } +} + +static bool +patterns_load(char src[][11], int src_n, char *filename) +{ + FILE *f = fopen("moggy.patterns", "r"); + if (!f) return false; + + int i; + for (i = 0; i < src_n; i++) { + char line[32]; + if (!fgets(line, sizeof(line), f)) + goto error; + int l = strlen(line); + if (l != 10 + (line[l - 1] == '\n')) + goto error; + memcpy(src[i], line, 10); + } + fprintf(stderr, "moggy.patterns: %d patterns loaded\n", i); + fclose(f); + return true; +error: + fprintf(stderr, "Error loading moggy.patterns.\n"); + fclose(f); + return false; +} + +void +pattern3s_init(struct pattern3s *p, char src[][11], int src_n) +{ + char nsrc[src_n][11]; + + if (!patterns_load(nsrc, src_n, "moggy.patterns")) { + /* Use default pattern set. */ + for (int i = 0; i < src_n; i++) + strcpy(nsrc[i], src[i]); + } + + patterns_gen(p, nsrc, src_n); +} + + +static __attribute__((constructor)) void +p3hashes_init(void) +{ + /* tuned for 11482 collisions */ + /* XXX: tune better */ + hash_t h = 0x35373c; + for (int i = 0; i < 8; i++) { + for (int a = 0; a < 2; a++) { + p3hashes[i][a][S_NONE] = (h = h * 16803-7); + p3hashes[i][a][S_BLACK] = (h = h * 16805-2); + p3hashes[i][a][S_WHITE] = (h = h * 16807-11); + p3hashes[i][a][S_OFFBOARD] = (h = h * 16809+7); + } + } +} diff --git a/jni/pachi/pattern3.h b/jni/pachi/pattern3.h new file mode 100644 index 0000000..f456d54 --- /dev/null +++ b/jni/pachi/pattern3.h @@ -0,0 +1,126 @@ +#ifndef PACHI_PATTERN3_H +#define PACHI_PATTERN3_H + +/* Fast matching of simple 3x3 patterns. */ + +#include "board.h" + +/* (Note that this is completely independent from the general pattern + * matching infrastructure in pattern.[ch]. This is fast and simple.) */ + +struct board; +struct move; + +/* hash3_t pattern: ignore middle point, 2 bits per intersection (color) + * plus 1 bit per each direct neighbor => 8*2 + 4 bits. Bitmap point order: + * 7 6 5 b + * 4 3 a 9 + * 2 1 0 8 */ +/* Value bit 0: black pattern; bit 1: white pattern */ + +/* XXX: See for hash3_t typedef. */ + +struct pattern2p { + hash3_t pattern; + char value; +}; + +struct pattern3s { + /* In case of a collision, following hash entries are + * used. value==0 indicates an unoccupied hash entry. */ + /* The hash indices are zobrist hashes based on p3hashes. */ +#define pattern3_hash_bits 19 +#define pattern3_hash_size (1 << pattern3_hash_bits) +#define pattern3_hash_mask (pattern3_hash_size - 1) + struct pattern2p hash[pattern3_hash_size]; +}; + +/* Zobrist hashes for the various 3x3 points. */ +/* [point][is_atari][color] */ +hash_t p3hashes[8][2][S_MAX]; + +/* Source pattern encoding: + * X: black; O: white; .: empty; #: edge + * x: !black; o: !white; ?: any + * + * |/=: black in atari/anything but black in atari + * @/0: white in atari + * Y/y: black notin atari; Q/q: white notin atari + * + * extra X: pattern valid only for one side; + * middle point ignored. */ + +void pattern3s_init(struct pattern3s *p, char src[][11], int src_n); + +/* Compute pattern3 hash at local position. */ +static hash3_t pattern3_hash(struct board *b, coord_t c); + +/* Check if we match any 3x3 pattern centered on given move. */ +static bool pattern3_move_here(struct pattern3s *p, struct board *b, struct move *m); + +/* Generate all transpositions of given pattern, stored in an + * hash3_t[8] array. */ +void pattern3_transpose(hash3_t pat, hash3_t (*transp)[8]); + +/* Reverse pattern to opposite color assignment. */ +static hash3_t pattern3_reverse(hash3_t pat); + + +static inline hash3_t +pattern3_hash(struct board *b, coord_t c) +{ + hash3_t pat = 0; + int x = coord_x(c, b), y = coord_y(c, b); + /* Stone info. */ + pat |= (board_atxy(b, x - 1, y - 1) << 14) + | (board_atxy(b, x, y - 1) << 12) + | (board_atxy(b, x + 1, y - 1) << 10); + pat |= (board_atxy(b, x - 1, y) << 8) + | (board_atxy(b, x + 1, y) << 6); + pat |= (board_atxy(b, x - 1, y + 1) << 4) + | (board_atxy(b, x, y + 1) << 2) + | (board_atxy(b, x + 1, y + 1)); + /* Atari info. */ +#define atari_atxy(b, x, y) (group_atxy(b, x, y) && board_group_info(b, group_atxy(b, x, y)).libs == 1) + pat |= (atari_atxy(b, x, y - 1) << 19); + pat |= (atari_atxy(b, x - 1, y) << 18) + | (atari_atxy(b, x + 1, y) << 17); + pat |= (atari_atxy(b, x, y + 1) << 16); +#undef atari_atxy + return pat; +} + +static inline __attribute__((const)) hash_t +hash3_to_hash(hash3_t pat) +{ + hash_t h = 0; + static const int ataribits[8] = { -1, 0, -1, 1, 2, -1, 3, -1 }; + for (int i = 0; i < 8; i++) { + h ^= p3hashes[i][ataribits[i] >= 0 ? (pat >> (16 + ataribits[i])) & 1 : 0][(pat >> (i*2)) & 3]; + } + return h; +} + +static inline bool +pattern3_move_here(struct pattern3s *p, struct board *b, struct move *m) +{ +#ifdef BOARD_PAT3 + hash3_t pat = b->pat3[m->coord]; +#else + hash3_t pat = pattern3_hash(b, m->coord); +#endif + hash_t h = hash3_to_hash(pat); + while (p->hash[h & pattern3_hash_mask].pattern != pat + && p->hash[h & pattern3_hash_mask].value != 0) + h++; + return (p->hash[h & pattern3_hash_mask].value & m->color); +} + +static inline hash3_t +pattern3_reverse(hash3_t pat) +{ + /* Reverse color assignment - achieved by swapping odd and even bits */ + return ((pat >> 1) & 0x5555) | ((pat & 0x5555) << 1) | (pat & 0xf0000); +} + +#endif diff --git a/jni/pachi/patternplay/Makefile b/jni/pachi/patternplay/Makefile new file mode 100644 index 0000000..820884a --- /dev/null +++ b/jni/pachi/patternplay/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=patternplay.o + +all: patternplay.a +patternplay.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/patternplay/patternplay.c b/jni/pachi/patternplay/patternplay.c new file mode 100644 index 0000000..d4a857a --- /dev/null +++ b/jni/pachi/patternplay/patternplay.c @@ -0,0 +1,128 @@ +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "move.h" +#include "patternplay/patternplay.h" +#include "pattern.h" +#include "patternsp.h" +#include "patternprob.h" +#include "random.h" + + +/* Internal engine state. */ +struct patternplay { + int debug_level; + + struct pattern_setup pat; +}; + + +static coord_t * +patternplay_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + struct patternplay *pp = e->data; + + struct pattern pats[b->flen]; + floating_t probs[b->flen]; + pattern_rate_moves(&pp->pat, b, color, pats, probs); + + int best = 0; + for (int f = 0; f < b->flen; f++) { + if (pp->debug_level >= 5 && probs[f] >= 0.001) { + char s[256]; pattern2str(s, &pats[f]); + fprintf(stderr, "\t%s: %.3f %s\n", coord2sstr(b->f[f], b), probs[f], s); + } + if (probs[f] > probs[best]) + best = f; + } + + return coord_copy(b->f[best]); +} + +void +patternplay_evaluate(struct engine *e, struct board *b, struct time_info *ti, floating_t *vals, enum stone color) +{ + struct patternplay *pp = e->data; + + struct pattern pats[b->flen]; + pattern_rate_moves(&pp->pat, b, color, pats, vals); + +#if 0 + // unused variable 'total' in above call to pattern_rate_moves() + floating_t total = pattern_rate_moves(&pp->pat, b, color, pats, vals); + /* Rescale properly. */ + for (int f = 0; f < b->flen; f++) { + probs[f] /= total; + } +#endif + + if (pp->debug_level >= 4) { + for (int f = 0; f < b->flen; f++) { + if (vals[f] >= 0.001) { + char s[256]; pattern2str(s, &pats[f]); + fprintf(stderr, "\t%s: %.3f %s\n", coord2sstr(b->f[f], b), vals[f], s); + } + } + } +} + + +struct patternplay * +patternplay_state_init(char *arg) +{ + struct patternplay *pp = calloc2(1, sizeof(struct patternplay)); + bool pat_setup = false; + + pp->debug_level = debug_level; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "debug")) { + if (optval) + pp->debug_level = atoi(optval); + else + pp->debug_level++; + + } else if (!strcasecmp(optname, "patterns") && optval) { + patterns_init(&pp->pat, optval, false, true); + pat_setup = true; + + } else { + fprintf(stderr, "patternplay: Invalid engine argument %s or missing value\n", optname); + exit(EXIT_FAILURE); + } + } + } + + if (!pat_setup) + patterns_init(&pp->pat, NULL, false, true); + + return pp; +} + +struct engine * +engine_patternplay_init(char *arg, struct board *b) +{ + struct patternplay *pp = patternplay_state_init(arg); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "PatternPlay Engine"; + e->comment = "I select moves blindly according to learned patterns. I won't pass as long as there is a place on the board where I can play. When we both pass, I will consider all the stones on the board alive."; + e->genmove = patternplay_genmove; + e->evaluate = patternplay_evaluate; + e->data = pp; + + return e; +} diff --git a/jni/pachi/patternplay/patternplay.h b/jni/pachi/patternplay/patternplay.h new file mode 100644 index 0000000..0fb9b50 --- /dev/null +++ b/jni/pachi/patternplay/patternplay.h @@ -0,0 +1,8 @@ +#ifndef PACHI_PATTERNPLAY_PATTERNPLAY_H +#define PACHI_PATTERNPLAY_PATTERNPLAY_H + +#include "engine.h" + +struct engine *engine_patternplay_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/patternprob.c b/jni/pachi/patternprob.c new file mode 100644 index 0000000..8496e54 --- /dev/null +++ b/jni/pachi/patternprob.c @@ -0,0 +1,118 @@ +#define DEBUG +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "pattern.h" +#include "patternsp.h" +#include "patternprob.h" + + +/* We try to avoid needlessly reloading probability dictionary + * since it may take rather long time. */ +static struct pattern_pdict *cached_dict; + +struct pattern_pdict * +pattern_pdict_init(char *filename, struct pattern_config *pc) +{ + if (cached_dict) { + cached_dict->pc = pc; + return cached_dict; + } + + if (!filename) + filename = "patterns.prob"; + FILE *f = fopen(filename, "r"); + if (!f) { + if (DEBUGL(1)) + fprintf(stderr, "No pattern probtable, will not use learned patterns.\n"); + return NULL; + } + + struct pattern_pdict *dict = calloc2(1, sizeof(*dict)); + dict->pc = pc; + dict->table = calloc2(pc->spat_dict->nspatials + 1, sizeof(*dict->table)); + + char *sphcachehit = calloc2(pc->spat_dict->nspatials, 1); + hash_t (*sphcache)[PTH__ROTATIONS] = malloc(pc->spat_dict->nspatials * sizeof(sphcache[0])); + + int i = 0; + char sbuf[1024]; + while (fgets(sbuf, sizeof(sbuf), f)) { + struct pattern_prob *pb = calloc2(1, sizeof(*pb)); + int c, o; + + char *buf = sbuf; + if (buf[0] == '#') continue; + while (isspace(*buf)) buf++; + while (!isspace(*buf)) buf++; // we recompute the probability + while (isspace(*buf)) buf++; + c = strtol(buf, &buf, 10); + while (isspace(*buf)) buf++; + o = strtol(buf, &buf, 10); + pb->prob = (floating_t) c / o; + while (isspace(*buf)) buf++; + str2pattern(buf, &pb->p); + + uint32_t spi = pattern2spatial(dict, &pb->p); + pb->next = dict->table[spi]; + dict->table[spi] = pb; + + /* Some spatials may not have been loaded if they correspond + * to a radius larger than supported. */ + if (pc->spat_dict->spatials[spi].dist > 0) { + /* We rehash spatials in the order of loaded patterns. This way + * we make sure that the most popular patterns will be hashed + * last and therefore take priority. */ + if (!sphcachehit[spi]) { + sphcachehit[spi] = 1; + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) + sphcache[spi][r] = spatial_hash(r, &pc->spat_dict->spatials[spi]); + } + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) + spatial_dict_addh(pc->spat_dict, sphcache[spi][r], spi); + } + + i++; + } + + free(sphcache); + free(sphcachehit); + if (DEBUGL(3)) + spatial_dict_hashstats(pc->spat_dict); + + fclose(f); + if (DEBUGL(1)) + fprintf(stderr, "Loaded %d pattern-probability pairs.\n", i); + cached_dict = dict; + return dict; +} + +floating_t +pattern_rate_moves(struct pattern_setup *pat, + struct board *b, enum stone color, + struct pattern *pats, floating_t *probs) +{ + floating_t total = 0; + for (int f = 0; f < b->flen; f++) { + probs[f] = NAN; + + struct move mo = { .coord = b->f[f], .color = color }; + if (is_pass(mo.coord)) + continue; + if (!board_is_valid_move(b, &mo)) + continue; + + pattern_match(&pat->pc, pat->ps, &pats[f], b, &mo); + floating_t prob = pattern_prob(pat->pd, &pats[f]); + if (!isnan(prob)) { + probs[f] = prob; + total += prob; + } + } + return total; +} + diff --git a/jni/pachi/patternprob.h b/jni/pachi/patternprob.h new file mode 100644 index 0000000..7ca52b2 --- /dev/null +++ b/jni/pachi/patternprob.h @@ -0,0 +1,75 @@ +#ifndef PACHI_PATTERNPROB_H +#define PACHI_PATTERNPROB_H + +/* Pattern probability table. */ + +#include + +#include "board.h" +#include "move.h" +#include "pattern.h" + + +/* The pattern probability table considers each pattern as a whole + * (not dividing it to individual features) and stores probability + * of the pattern being played. */ + +/* The table primary key is the pattern spatial (most distinctive + * feature); within a single primary key chain, the entries are + * unsorted (for now). */ + +struct pattern_prob { + struct pattern p; + floating_t prob; + struct pattern_prob *next; +}; + +struct pattern_pdict { + struct pattern_config *pc; + + struct pattern_prob **table; /* [pc->spat_dict->nspatials + 1] */ +}; + +/* Initialize the pdict data structure from a given file (pass NULL + * to use default filename). Returns NULL if the file with patterns + * has been found. */ +struct pattern_pdict *pattern_pdict_init(char *filename, struct pattern_config *pc); + +/* Return probability associated with given pattern. Returns NaN if + * the pattern cannot be found. */ +static floating_t pattern_prob(struct pattern_pdict *dict, struct pattern *p); + +/* Evaluate patterns for all available moves. Stores found patterns + * to pats[b->flen] and NON-normalized probability of each pattern + * (or NaN in case of no match) to probs[b->flen]. Returns the sum + * of all probabilities that can be used for normalization. */ +floating_t pattern_rate_moves(struct pattern_setup *pat, + struct board *b, enum stone color, + struct pattern *pats, floating_t *probs); + +/* Utility function - extract spatial id from a pattern. If the pattern + * has no spatial feature, it is represented by the highest spatial id + * plus one. */ +static uint32_t pattern2spatial(struct pattern_pdict *dict, struct pattern *p); + + +static inline floating_t +pattern_prob(struct pattern_pdict *dict, struct pattern *p) +{ + uint32_t spi = pattern2spatial(dict, p); + for (struct pattern_prob *pb = dict->table[spi]; pb; pb = pb->next) + if (pattern_eq(p, &pb->p)) + return pb->prob; + return NAN; // XXX: We assume quiet NAN existence +} + +static inline uint32_t +pattern2spatial(struct pattern_pdict *dict, struct pattern *p) +{ + for (int i = 0; i < p->n; i++) + if (p->f[i].id == FEAT_SPATIAL) + return p->f[i].payload; + return dict->pc->spat_dict->nspatials; +} + +#endif diff --git a/jni/pachi/patternscan/Makefile b/jni/pachi/patternscan/Makefile new file mode 100644 index 0000000..e34f449 --- /dev/null +++ b/jni/pachi/patternscan/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=patternscan.o + +all: patternscan.a +patternscan.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/patternscan/patternscan.c b/jni/pachi/patternscan/patternscan.c new file mode 100644 index 0000000..25b78d4 --- /dev/null +++ b/jni/pachi/patternscan/patternscan.c @@ -0,0 +1,323 @@ +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "move.h" +#include "patternscan/patternscan.h" +#include "pattern.h" +#include "patternsp.h" +#include "random.h" + + +/* The engine has two modes: + * + * * gen_spat_dict=1: patterns.spat file is generated with a list of all + * encountered spatials + * + * * gen_spat_dict=0,no_pattern_match=1: all encountered patterns are + * listed on output on each move; the general format is + * [(winpattern)] + * but with competition=1 it is + * [(winpattern)] [(witnesspattern0) (witnesspattern1) ...] + * and with spat_split_sizes=1 even + * [(winpattern0) (winpattern1) ...] [(witpattern0) (witpattern1) ...] + */ + + +/* Internal engine state. */ +struct patternscan { + int debug_level; + + struct pattern_setup pat; + bool competition; + bool spat_split_sizes; + int color_mask; + + bool no_pattern_match; + bool gen_spat_dict; + /* Minimal number of occurences for spatial to be saved. */ + int spat_threshold; + /* Number of loaded spatials; checkpoint for saving new sids + * in case gen_spat_dict is enabled. */ + int loaded_spatials; + + /* Book-keeping of spatial occurence count. */ + int gameno; + unsigned int nscounts; + int *scounts; + int *sgameno; +}; + + +static void +process_pattern(struct patternscan *ps, struct board *b, struct move *m, char **str) +{ + /* First, store the spatial configuration in dictionary + * if applicable. */ + if (ps->gen_spat_dict && !is_pass(m->coord)) { + struct spatial s; + spatial_from_board(&ps->pat.pc, &s, b, m); + int dmax = s.dist; + for (int d = ps->pat.pc.spat_min; d <= dmax; d++) { + s.dist = d; + unsigned int sid = spatial_dict_put(ps->pat.pc.spat_dict, &s, spatial_hash(0, &s)); + assert(sid > 0); + #define SCOUNTS_ALLOC 1048576 // Allocate space in 1M*4 blocks. + if (sid >= ps->nscounts) { + int newnsc = (sid / SCOUNTS_ALLOC + 1) * SCOUNTS_ALLOC; + ps->scounts = realloc(ps->scounts, newnsc * sizeof(*ps->scounts)); + memset(&ps->scounts[ps->nscounts], 0, (newnsc - ps->nscounts) * sizeof(*ps->scounts)); + ps->sgameno = realloc(ps->sgameno, newnsc * sizeof(*ps->sgameno)); + memset(&ps->sgameno[ps->nscounts], 0, (newnsc - ps->nscounts) * sizeof(*ps->sgameno)); + ps->nscounts = newnsc; + } + if (ps->debug_level > 1 && !fast_random(65536) && !fast_random(32)) { + fprintf(stderr, "%d spatials, %d collisions\n", ps->pat.pc.spat_dict->nspatials, ps->pat.pc.spat_dict->collisions); + } + if (ps->sgameno[sid] != ps->gameno) { + ps->scounts[sid]++; + ps->sgameno[sid] = ps->gameno; + } + } + } + + /* Now, match the pattern. */ + if (!ps->no_pattern_match) { + struct pattern p; + pattern_match(&ps->pat.pc, ps->pat.ps, &p, b, m); + + if (!ps->spat_split_sizes) { + *str = pattern2str(*str, &p); + } else { + /* XXX: We assume that FEAT_SPATIAL items + * are at the end. */ + struct pattern p2; + int i = 0; + while (i < p.n && p.f[i].id != FEAT_SPATIAL) { + p2.f[i] = p.f[i]; + i++; + } + if (i == p.n) { + p2.n = i; + *str = pattern2str(*str, &p2); + } else { + p2.n = i + 1; + for (int j = i; j < p.n; j++) { + assert(p.f[j].id == FEAT_SPATIAL); + p2.f[i] = p.f[j]; + if ((*str)[-1] == ')') + *(*str)++ = ' '; + *str = pattern2str(*str, &p2); + } + } + } + } +} + +static char * +patternscan_play(struct engine *e, struct board *b, struct move *m, char *enginearg) +{ + struct patternscan *ps = e->data; + + if (is_resign(m->coord)) + return NULL; + /* Deal with broken game records that sometimes get fed in. */ + if (board_at(b, m->coord) != S_NONE) + return NULL; + + if (b->moves == (b->handicap ? b->handicap * 2 : 1)) + ps->gameno++; + + if (!(m->color & ps->color_mask)) + return NULL; + /* The user can request this play to be "silent", to get patterns + * only for a single specific situation. */ + if (enginearg && *enginearg == '0') + return NULL; + + static char str[1048576]; // XXX + char *strp = str; + *str = 0; + + /* Scan for supported features. */ + /* For specifiation of features and their payloads, + * please refer to pattern.h. */ + *strp++ = '['; + process_pattern(ps, b, m, &strp); + *strp++ = ']'; + + if (ps->competition) { + /* Look at other possible moves as well. */ + *strp++ = ' '; + *strp++ = '['; + for (int f = 0; f < b->flen; f++) { + struct move mo = { .coord = b->f[f], .color = m->color }; + if (is_pass(mo.coord)) + continue; + if (!board_is_valid_move(b, &mo)) + continue; + if (strp[-1] != '[') + *strp++ = ' '; + process_pattern(ps, b, &mo, &strp); + } + *strp++ = ']'; + } + *strp++ = 0; + + return ps->no_pattern_match ? NULL : str; +} + +static coord_t * +patternscan_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + fprintf(stderr, "genmove command not available during patternscan!\n"); + exit(EXIT_FAILURE); +} + +void +patternscan_done(struct engine *e) +{ + struct patternscan *ps = e->data; + if (!ps->gen_spat_dict) + return; + + /* Save newly found patterns. */ + + bool newfile = true; + FILE *f = fopen(spatial_dict_filename, "r"); + if (f) { fclose(f); newfile = false; } + f = fopen(spatial_dict_filename, "a"); + if (newfile) + spatial_dict_writeinfo(ps->pat.pc.spat_dict, f); + + for (unsigned int i = ps->loaded_spatials; i < ps->pat.pc.spat_dict->nspatials; i++) { + /* By default, threshold is 0 and condition is always true. */ + assert(i < ps->nscounts && ps->scounts[i] > 0); + if (ps->scounts[i] >= ps->spat_threshold) + spatial_write(ps->pat.pc.spat_dict, &ps->pat.pc.spat_dict->spatials[i], i, f); + } + fclose(f); +} + + +struct patternscan * +patternscan_state_init(char *arg) +{ + struct patternscan *ps = calloc2(1, sizeof(struct patternscan)); + bool pat_setup = false; + int xspat = -1; + + ps->debug_level = 1; + ps->color_mask = S_BLACK | S_WHITE; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "debug")) { + if (optval) + ps->debug_level = atoi(optval); + else + ps->debug_level++; + + } else if (!strcasecmp(optname, "gen_spat_dict")) { + /* If set, re-generate the spatial patterns + * dictionary; you need to have a dictionary + * of spatial stone configurations in order + * to match any spatial features. */ + /* XXX: If you specify the 'patterns' option, + * this must come first! */ + ps->gen_spat_dict = !optval || atoi(optval); + + } else if (!strcasecmp(optname, "no_pattern_match")) { + /* If set, do not actually match patterns. + * Useful only together with gen_spat_dict + * when just building spatial dictionary. */ + ps->no_pattern_match = !optval || atoi(optval); + + } else if (!strcasecmp(optname, "spat_threshold") && optval) { + /* Minimal number of times new spatial + * feature must occur in this run (!) to + * be included in the dictionary. Note that + * this will produce discontinuous dictionary + * that you should renumber. Also note that + * 3x3 patterns are always saved. */ + ps->spat_threshold = atoi(optval); + + } else if (!strcasecmp(optname, "competition")) { + /* In competition mode, first the played + * pattern is printed, then all patterns + * that could be played (including the played + * one). */ + ps->competition = !optval || atoi(optval); + + } else if (!strcasecmp(optname, "spat_split_sizes")) { + /* Generate a separate pattern for each + * spatial size. This is important to + * preserve good generalization in unknown + * situations where the largest pattern + * might not match. */ + ps->spat_split_sizes = 1; + + } else if (!strcasecmp(optname, "color_mask") && optval) { + /* Bitmask of move colors to match. Set this + * to 2 if you want to match only white moves, + * for example. (Useful for processing + * handicap games.) */ + ps->color_mask = atoi(optval); + + } else if (!strcasecmp(optname, "xspat") && optval) { + /* xspat==0: don't match spatial features + * xspat==1: match *only* spatial features */ + xspat = atoi(optval); + + } else if (!strcasecmp(optname, "patterns") && optval) { + patterns_init(&ps->pat, optval, ps->gen_spat_dict, false); + pat_setup = true; + + } else { + fprintf(stderr, "patternscan: Invalid engine argument %s or missing value\n", optname); + exit(EXIT_FAILURE); + } + } + } + + if (!pat_setup) + patterns_init(&ps->pat, NULL, ps->gen_spat_dict, false); + if (ps->spat_split_sizes) + ps->pat.pc.spat_largest = 0; + + for (int i = 0; i < FEAT_MAX; i++) if ((xspat == 0 && i == FEAT_SPATIAL) || (xspat == 1 && i != FEAT_SPATIAL)) ps->pat.ps[i] = 0; + ps->loaded_spatials = ps->pat.pc.spat_dict->nspatials; + + ps->gameno = 1; + + return ps; +} + +struct engine * +engine_patternscan_init(char *arg, struct board *b) +{ + struct patternscan *ps = patternscan_state_init(arg); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "PatternScan Engine"; + e->comment = "You cannot play Pachi with this engine, it is intended for special development use - scanning of games fed to it as GTP streams for various pattern features."; + e->genmove = patternscan_genmove; + e->notify_play = patternscan_play; + e->done = patternscan_done; + e->data = ps; + // clear_board does not concern us, we like to work over many games + e->keep_on_clear = true; + + return e; +} diff --git a/jni/pachi/patternscan/patternscan.h b/jni/pachi/patternscan/patternscan.h new file mode 100644 index 0000000..8bf50d7 --- /dev/null +++ b/jni/pachi/patternscan/patternscan.h @@ -0,0 +1,8 @@ +#ifndef PACHI_PATTERNSCAN_PATTERNSCAN_H +#define PACHI_PATTERNSCAN_PATTERNSCAN_H + +#include "engine.h" + +struct engine *engine_patternscan_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/patternsp.c b/jni/pachi/patternsp.c new file mode 100644 index 0000000..16a50a0 --- /dev/null +++ b/jni/pachi/patternsp.c @@ -0,0 +1,472 @@ +#define DEBUG +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "pattern.h" +#include "patternsp.h" + +/* Mapping from point sequence to coordinate offsets (to determine + * coordinates relative to pattern center). The array is ordered + * in the gridcular metric order so that we can go through it + * and incrementally match spatial features in nested circles. + * Within one circle, coordinates are ordered by rows to keep + * good cache behavior. */ +struct ptcoord ptcoords[MAX_PATTERN_AREA]; + +/* For each radius, starting index in ptcoords[]. */ +unsigned int ptind[MAX_PATTERN_DIST + 2]; + +/* ptcoords[], ptind[] setup */ +static void +ptcoords_init(void) +{ + int i = 0; /* Indexing ptcoords[] */ + + /* First, center point. */ + ptind[0] = ptind[1] = 0; + ptcoords[i].x = ptcoords[i].y = 0; i++; + + for (int d = 2; d <= MAX_PATTERN_DIST; d++) { + ptind[d] = i; + /* For each y, examine all integer solutions + * of d = |x| + |y| + max(|x|, |y|). */ + /* TODO: (Stern, 2006) uses a hand-modified + * circles that are finer for small d and more + * coarse for large d. */ + for (short y = d / 2; y >= 0; y--) { + short x; + if (y > d / 3) { + /* max(|x|, |y|) = |y|, non-zero x */ + x = d - y * 2; + if (x + y * 2 != d) continue; + } else { + /* max(|x|, |y|) = |x| */ + /* Or, max(|x|, |y|) = |y| and x is zero */ + x = (d - y) / 2; + if (x * 2 + y != d) continue; + } + + assert((x > y ? x : y) + x + y == d); + + ptcoords[i].x = x; ptcoords[i].y = y; i++; + if (x != 0) { ptcoords[i].x = -x; ptcoords[i].y = y; i++; } + if (y != 0) { ptcoords[i].x = x; ptcoords[i].y = -y; i++; } + if (x != 0 && y != 0) { ptcoords[i].x = -x; ptcoords[i].y = -y; i++; } + } + } + ptind[MAX_PATTERN_DIST + 1] = i; + +#if 0 + for (int d = 0; d <= MAX_PATTERN_DIST; d++) { + fprintf(stderr, "d=%d (%d) ", d, ptind[d]); + for (int j = ptind[d]; j < ptind[d + 1]; j++) { + fprintf(stderr, "%d,%d ", ptcoords[j].x, ptcoords[j].y); + } + fprintf(stderr, "\n"); + } +#endif +} + + +/* Zobrist hashes used for points in patterns. */ +hash_t pthashes[PTH__ROTATIONS][MAX_PATTERN_AREA][S_MAX]; + +static void +pthashes_init(void) +{ + /* We need fixed hashes for all pattern-relative in + * all pattern users! This is a simple way to generate + * hopefully good ones. Park-Miller powa. :) */ + + /* We create a virtual board (centered at the sequence start), + * plant the hashes there, then pick them up into the sequence + * with correct coordinates. It would be possible to generate + * the sequence point hashes directly, but the rotations would + * make for enormous headaches. */ +#define PATTERN_BOARD_SIZE ((MAX_PATTERN_DIST + 1) * (MAX_PATTERN_DIST + 1)) + hash_t pthboard[PATTERN_BOARD_SIZE][4]; + int pthbc = PATTERN_BOARD_SIZE / 2; // tengen coord + + /* The magic numbers are tuned for minimal collisions. */ + hash_t h1 = 0xd6d6d6d1; + hash_t h2 = 0xd6d6d6d2; + hash_t h3 = 0xd6d6d6d3; + hash_t h4 = 0xd6d6d6d4; + for (int i = 0; i < PATTERN_BOARD_SIZE; i++) { + pthboard[i][S_NONE] = (h1 = h1 * 16787); + pthboard[i][S_BLACK] = (h2 = h2 * 16823); + pthboard[i][S_WHITE] = (h3 = h3 * 16811 - 13); + pthboard[i][S_OFFBOARD] = (h4 = h4 * 16811); + } + + /* Virtual board with hashes created, now fill + * pthashes[] with hashes for points in actual + * sequences, also considering various rotations. */ +#define PTH_VMIRROR 1 +#define PTH_HMIRROR 2 +#define PTH_90ROT 4 + for (int r = 0; r < PTH__ROTATIONS; r++) { + for (int i = 0; i < MAX_PATTERN_AREA; i++) { + /* Rotate appropriately. */ + int rx = ptcoords[i].x; + int ry = ptcoords[i].y; + if (r & PTH_VMIRROR) ry = -ry; + if (r & PTH_HMIRROR) rx = -rx; + if (r & PTH_90ROT) { + int rs = rx; rx = -ry; ry = rs; + } + int bi = pthbc + ry * (MAX_PATTERN_DIST + 1) + rx; + + /* Copy info. */ + pthashes[r][i][S_NONE] = pthboard[bi][S_NONE]; + pthashes[r][i][S_BLACK] = pthboard[bi][S_BLACK]; + pthashes[r][i][S_WHITE] = pthboard[bi][S_WHITE]; + pthashes[r][i][S_OFFBOARD] = pthboard[bi][S_OFFBOARD]; + } + } +} + +static void __attribute__((constructor)) +spatial_init(void) +{ + /* Initialization of various static data structures for + * fast pattern processing. */ + ptcoords_init(); + pthashes_init(); +} + +inline hash_t +spatial_hash(unsigned int rotation, struct spatial *s) +{ + hash_t h = 0; + for (unsigned int i = 0; i < ptind[s->dist + 1]; i++) { + h ^= pthashes[rotation][i][spatial_point_at(*s, i)]; + } + return h & spatial_hash_mask; +} + +char * +spatial2str(struct spatial *s) +{ + static char buf[1024]; + for (unsigned int i = 0; i < ptind[s->dist + 1]; i++) { + buf[i] = stone2char(spatial_point_at(*s, i)); + } + buf[ptind[s->dist + 1]] = 0; + return buf; +} + +void +spatial_from_board(struct pattern_config *pc, struct spatial *s, + struct board *b, struct move *m) +{ + assert(pc->spat_min > 0); + + /* We record all spatial patterns black-to-play; simply + * reverse all colors if we are white-to-play. */ + static enum stone bt_black[4] = { S_NONE, S_BLACK, S_WHITE, S_OFFBOARD }; + static enum stone bt_white[4] = { S_NONE, S_WHITE, S_BLACK, S_OFFBOARD }; + enum stone (*bt)[4] = m->color == S_WHITE ? &bt_white : &bt_black; + + memset(s, 0, sizeof(*s)); + for (unsigned int j = 0; j < ptind[pc->spat_max + 1]; j++) { + ptcoords_at(x, y, m->coord, b, j); + s->points[j / 4] |= (*bt)[board_atxy(b, x, y)] << ((j % 4) * 2); + } + s->dist = pc->spat_max; +} + +/* Compare two spatials, allowing for differences up to isomorphism. + * True means the spatials are equivalent. */ +static bool +spatial_cmp(struct spatial *s1, struct spatial *s2) +{ + /* Quick preliminary check. */ + if (s1->dist != s2->dist) + return false; + + /* We could create complex transposition tables, but it seems most + * foolproof to just check if the sets of rotation hashes are the + * same for both. */ + hash_t s1r[PTH__ROTATIONS]; + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) + s1r[r] = spatial_hash(r, s1); + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) { + hash_t s2r = spatial_hash(r, s2); + for (unsigned int p = 0; p < PTH__ROTATIONS; p++) + if (s2r == s1r[p]) + goto found_rot; + /* Rotation hash s2r does not correspond to s1r. */ + return false; +found_rot:; + } + + /* All rotation hashes of s2 occur in s1. Hopefully that + * indicates something. */ + return true; +} + + +/* Spatial dict manipulation. */ + +static unsigned int +spatial_dict_addc(struct spatial_dict *dict, struct spatial *s) +{ + /* Allocate space in 1024 blocks. */ +#define SPATIALS_ALLOC 1024 + if (!(dict->nspatials % SPATIALS_ALLOC)) { + dict->spatials = realloc(dict->spatials, + (dict->nspatials + SPATIALS_ALLOC) + * sizeof(*dict->spatials)); + } + dict->spatials[dict->nspatials] = *s; + return dict->nspatials++; +} + +bool +spatial_dict_addh(struct spatial_dict *dict, hash_t hash, unsigned int id) +{ + if (dict->hash[hash]) { + if (dict->hash[hash] != id) + dict->collisions++; + } else { + dict->fills++; + } + dict->hash[hash] = id; + return true; +} + +/* Spatial dictionary file format: + * /^#/ - comments + * INDEX RADIUS STONES HASH... + * INDEX: index in the spatial table + * RADIUS: @d of the pattern + * STONES: string of ".XO#" chars + * HASH...: space-separated 18bit hash-table indices for the pattern */ + +static void +spatial_dict_read(struct spatial_dict *dict, char *buf, bool hash) +{ + /* XXX: We trust the data. Bad data will crash us. */ + char *bufp = buf; + + unsigned int index, radius; + index = strtoul(bufp, &bufp, 10); + radius = strtoul(bufp, &bufp, 10); + while (isspace(*bufp)) bufp++; + + if (radius > MAX_PATTERN_DIST) { + /* Too large spatial, skip. */ + struct spatial s = { .dist = 0 }; + unsigned int id = spatial_dict_addc(dict, &s); + assert(id == index); + return; + } + + /* Load the stone configuration. */ + struct spatial s = { .dist = radius }; + unsigned int sl = 0; + while (!isspace(*bufp)) { + s.points[sl / 4] |= char2stone(*bufp++) << ((sl % 4)*2); + sl++; + } + while (isspace(*bufp)) bufp++; + + /* Sanity check. */ + if (sl != ptind[s.dist + 1]) { + fprintf(stderr, "Spatial dictionary: Invalid number of stones (%d != %d) on this line: %s\n", + sl, ptind[radius + 1] - 1, buf); + exit(EXIT_FAILURE); + } + + /* Add to collection. */ + unsigned int id = spatial_dict_addc(dict, &s); + assert(id == index); + + /* Add to specified hash places. */ + if (hash) + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) + spatial_dict_addh(dict, spatial_hash(r, &s), id); +} + +void +spatial_write(struct spatial_dict *dict, struct spatial *s, unsigned int id, FILE *f) +{ + fprintf(f, "%d %d ", id, s->dist); + fputs(spatial2str(s), f); + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) { + hash_t rhash = spatial_hash(r, s); + unsigned int id2 = dict->hash[rhash]; + if (id2 != id) { + /* This hash does not belong to us. Decide whether + * we or the current owner is better owner. */ + /* TODO: Compare also # of patternscan encounters? */ + struct spatial *s2 = &dict->spatials[id2]; + if (s2->dist < s->dist) + continue; + if (s2->dist == s->dist && id2 < id) + continue; + } + fprintf(f, " %"PRIhash"", spatial_hash(r, s)); + } + fputc('\n', f); +} + +static void +spatial_dict_load(struct spatial_dict *dict, FILE *f, bool hash) +{ + char buf[1024]; + while (fgets(buf, sizeof(buf), f)) { + if (buf[0] == '#') continue; + spatial_dict_read(dict, buf, hash); + } + if (DEBUGL(1)) { + fprintf(stderr, "Loaded spatial dictionary of %d patterns.\n", dict->nspatials); + if (hash) + spatial_dict_hashstats(dict); + } +} + +void +spatial_dict_hashstats(struct spatial_dict *dict) +{ + /* m hash size, n number of patterns; is zobrist universal hash? + * + * Not so rigorous analysis, but it should give a good approximation: + * Probability of empty bucket is (1-1/m)^n ~ e^(-n/m) + * Probability of non-empty bucket is 1-e^(-n/m) + * Expected number of non-empty buckets is m*(1-e^(-n/m)) + * Number of collisions is n-m*(1-e^(-n/m)). */ + + /* The result: Reality matches these expectations pretty well! + * + * Actual: + * Loaded spatial dictionary of 1064482 patterns. + * (Spatial dictionary hash: 513997 collisions (incl. repetitions), 11.88% (7970033/67108864) fill rate). + * + * Theoretical: + * m = 2^26 + * n <= 8*1064482 (some patterns may have some identical rotations) + * n = 513997+7970033 = 8484030 should be the correct number + * n-m*(1-e^(-n/m)) = 514381 + * + * To verify, make sure to turn patternprob off (e.g. use + * -e patternscan), since it will insert a pattern multiple times, + * multiplying the reported number of collisions. */ + + unsigned long buckets = (sizeof(dict->hash) / sizeof(dict->hash[0])); + fprintf(stderr, "\t(Spatial dictionary hash: %d collisions (incl. repetitions), %.2f%% (%d/%lu) fill rate).\n", + dict->collisions, + (double) dict->fills * 100 / buckets, + dict->fills, buckets); +} + +void +spatial_dict_writeinfo(struct spatial_dict *dict, FILE *f) +{ + /* New file. First, create a comment describing order + * of points in the array. This is just for purposes + * of external tools, Pachi never interprets it itself. */ + fprintf(f, "# Pachi spatial patterns dictionary v1.0 maxdist %d\n", + MAX_PATTERN_DIST); + for (unsigned int d = 0; d <= MAX_PATTERN_DIST; d++) { + fprintf(f, "# Point order: d=%d ", d); + for (unsigned int j = ptind[d]; j < ptind[d + 1]; j++) { + fprintf(f, "%d,%d ", ptcoords[j].x, ptcoords[j].y); + } + fprintf(f, "\n"); + } +} + +/* We try to avoid needlessly reloading spatial dictionary + * since it may take rather long time. */ +static struct spatial_dict *cached_dict; + +const char *spatial_dict_filename = "patterns.spat"; +struct spatial_dict * +spatial_dict_init(bool will_append, bool hash) +{ + if (cached_dict && !will_append) + return cached_dict; + + FILE *f = fopen(spatial_dict_filename, "r"); + if (!f && !will_append) { + if (DEBUGL(1)) + fprintf(stderr, "No spatial dictionary, will not match spatial pattern features.\n"); + return NULL; + } + + struct spatial_dict *dict = calloc2(1, sizeof(*dict)); + /* We create a dummy record for index 0 that we will + * never reference. This is so that hash value 0 can + * represent "no value". */ + struct spatial dummy = { .dist = 0 }; + spatial_dict_addc(dict, &dummy); + + if (f) { + spatial_dict_load(dict, f, hash); + fclose(f); f = NULL; + } else { + assert(will_append); + } + + cached_dict = dict; + return dict; +} + +unsigned int +spatial_dict_put(struct spatial_dict *dict, struct spatial *s, hash_t h) +{ + /* We avoid spatial_dict_get() here, since we want to ignore radius + * differences - we have custom collision detection. */ + unsigned int id = dict->hash[h]; + if (id > 0) { + /* Is this the same or isomorphous spatial? */ + if (spatial_cmp(s, &dict->spatials[id])) + return id; + + /* Look a bit harder - perhaps one of our rotations still + * points at the correct spatial. */ + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) { + hash_t rhash = spatial_hash(r, s); + unsigned int rid = dict->hash[rhash]; + /* No match means we definitely aren't stored yet. */ + if (!rid) + break; + if (id != rid && spatial_cmp(s, &dict->spatials[rid])) { + /* Yay, this is us! */ + if (DEBUGL(3)) + fprintf(stderr, "Repeated collision %d vs %d\n", id, rid); + id = rid; + /* Point the hashes back to us. */ + goto hash_store; + } + } + + if (DEBUGL(1)) + fprintf(stderr, "Collision %d vs %d\n", id, dict->nspatials); + id = 0; + /* dict->collisions++; gets done by addh */ + } + + /* Add new pattern! */ + id = spatial_dict_addc(dict, s); + if (DEBUGL(4)) { + fprintf(stderr, "new spat %d(%d) %s <%"PRIhash"> ", id, s->dist, spatial2str(s), h); + for (unsigned int r = 0; r < 8; r++) + fprintf(stderr,"[%"PRIhash"] ", spatial_hash(r, s)); + fprintf(stderr, "\n"); + } + + /* Store new pattern in the hash. */ +hash_store: + for (unsigned int r = 0; r < PTH__ROTATIONS; r++) + spatial_dict_addh(dict, spatial_hash(r, s), id); + + return id; +} diff --git a/jni/pachi/patternsp.h b/jni/pachi/patternsp.h new file mode 100644 index 0000000..d572eb4 --- /dev/null +++ b/jni/pachi/patternsp.h @@ -0,0 +1,165 @@ +#ifndef PACHI_PATTERNSP_H +#define PACHI_PATTERNSP_H + +/* Matching of spatial pattern features. */ + +#include "board.h" +#include "move.h" +#include "pattern.h" + +/* Spatial stone configuration pattern features - like pattern3 handles + * 3x3-area, this handles general N-area (where N is distance in + * gridcular metric). These routines define the dictionary of spatial + * configurations (accessible by zobrist hashes or indices) and related + * data structures; eventually, they support the FEAT_SPATIAL pattern + * feature implementation in the General Pattern Matcher (pattern.[ch]). */ + +/* Maximum spatial pattern diameter. */ +#define MAX_PATTERN_DIST 7 +/* Maximum number of points in spatial pattern (upper bound). + * TODO: Better upper bound to save more data. */ +#define MAX_PATTERN_AREA (MAX_PATTERN_DIST*MAX_PATTERN_DIST) + +/* For each encountered configuration of stones, we keep it "spelled out" + * in the spatial dictionary records, index them and refer just the indices + * in the feature payloads. This achieves several things: + * * We can handle patterns of arbitrary length. + * * We can recognize isomorphous configurations (color reversions, + * rotations) within the dataset. + * * We can visualise patterns corresponding to chosen features. + * + * Thus, it goes like this: + * + * +----------------+ +----------------+ + * | struct pattern | - | struct feature | + * +----------------+ | payload id | + * +----------------+ + * | FEAT_SPATIAL + * | + * | ,--<--. + * | | | + * +-----------------------------------------+ + * | struct spatial_dict spatials[] hash[] | + * +-----------------------------------------+ + * | + * +----------------+ + * | struct spatial | + * +----------------+ + */ + + +/* Spatial record - single stone configuration. */ + +struct spatial { + /* Gridcular radius of matched pattern. */ + unsigned char dist; + /* The points; each point is two bits, corresponding + * to {enum stone}. Points are ordered in gridcular-defined + * spiral from middle to the edge; the dictionary file has + * a comment describing the ordering at the top. */ + unsigned char points[MAX_PATTERN_AREA / 4]; +#define spatial_point_at(s, i) (((s).points[(i) / 4] >> (((i) % 4) * 2)) & 3) +}; + +/* Fill up the spatial record from @m vincinity, up to full distance + * given by pattern config. */ +struct pattern_config; +void spatial_from_board(struct pattern_config *pc, struct spatial *s, struct board *b, struct move *m); + +/* Compute hash of given spatial pattern. */ +hash_t spatial_hash(unsigned int rotation, struct spatial *s); + +/* Convert given spatial pattern to string. */ +char *spatial2str(struct spatial *s); + +/* Mapping from point sequence to coordinate offsets (to determine + * coordinates relative to pattern center). */ +struct ptcoord { short x, y; } ptcoords[MAX_PATTERN_AREA]; +/* For each radius, starting index in ptcoords[]. */ +unsigned int ptind[MAX_PATTERN_DIST + 2]; + +/* Zobrist hashes used for points in patterns. */ +#define PTH__ROTATIONS 8 +hash_t pthashes[PTH__ROTATIONS][MAX_PATTERN_AREA][S_MAX]; + +#define ptcoords_at(x_, y_, c_, b_, j_) \ + int x_ = coord_x((c_), (b_)) + ptcoords[j_].x; \ + int y_ = coord_y((c_), (b_)) + ptcoords[j_].y; \ + if (x_ >= board_size(b_)) x_ = board_size(b_) - 1; else if (x_ < 0) x_ = 0; \ + if (y_ >= board_size(b_)) y_ = board_size(b_) - 1; else if (y_ < 0) y_ = 0; + +/* Spatial dictionary - collection of stone configurations. */ + +/* Two ways of lookup: (i) by index (ii) by hash of the configuration. */ +struct spatial_dict { + /* Indexed base store */ + unsigned int nspatials; /* Number of records. */ + struct spatial *spatials; /* Actual records. */ + + /* Hashed access; all isomorphous configurations + * are also hashed */ +#define spatial_hash_bits 26 // ~256mib array +#define spatial_hash_mask ((1 << spatial_hash_bits) - 1) + /* Maps to spatials[] indices. The hash function + * used is zobrist hashing with fixed values. */ + uint32_t hash[1 << spatial_hash_bits]; + /* Auxiliary counters for statistics. */ + int fills, collisions; +}; + +/* Initializes spatial dictionary, pre-loading existing records from + * default filename if exists. If will_append is true, it will not + * complain about non-existing file and initialize the dictionary anyway. + * If hash is true, loaded spatials will be added to the hashtable; + * use false if this is to be done later (e.g. by patternprob). */ +struct spatial_dict *spatial_dict_init(bool will_append, bool hash); + +/* Lookup specified spatial pattern in the dictionary; return index + * of the pattern. If the pattern is not found, 0 will be returned. */ +static unsigned int spatial_dict_get(struct spatial_dict *dict, int dist, hash_t h); + +/* Store specified spatial pattern in the dictionary if it is not known yet. + * Returns pattern id. Note that the pattern is NOT written to the underlying + * file automatically. */ +unsigned int spatial_dict_put(struct spatial_dict *dict, struct spatial *s, hash_t); + +/* Readds given rotation of given pattern to the hash. This is useful only + * if you want to tweak hash priority of various patterns. */ +bool spatial_dict_addh(struct spatial_dict *dict, hash_t hash, unsigned int id); + +/* Print stats about the hash to stderr. Companion to spatial_dict_addh(). */ +void spatial_dict_hashstats(struct spatial_dict *dict); + + +/* Spatial dictionary file manipulation. */ + +/* Loading routine is not exported, it is called automatically within + * spatial_dict_init(). */ + +/* Default spatial dict filename to use. */ +extern const char *spatial_dict_filename; + +/* Write comment lines describing the dictionary (e.g. point order + * in patterns) to given file. */ +void spatial_dict_writeinfo(struct spatial_dict *dict, FILE *f); + +/* Append specified spatial pattern to the given file. */ +void spatial_write(struct spatial_dict *dict, struct spatial *s, unsigned int id, FILE *f); + + +static inline unsigned int +spatial_dict_get(struct spatial_dict *dict, int dist, hash_t hash) +{ + unsigned int id = dict->hash[hash]; +#ifdef DEBUG + if (id && dict->spatials[id].dist != dist) { + if (DEBUGL(6)) + fprintf(stderr, "Collision dist %d vs %d (hash [%d]%"PRIhash")\n", + dist, dict->spatials[id].dist, id, hash); + return 0; + } +#endif + return id; +} + +#endif diff --git a/jni/pachi/playout.c b/jni/pachi/playout.c new file mode 100644 index 0000000..048f186 --- /dev/null +++ b/jni/pachi/playout.c @@ -0,0 +1,167 @@ +#define DEBUG +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "move.h" +#include "ownermap.h" +#include "playout.h" + +/* Whether to set global debug level to the same as the playout + * has, in case it is different. This can make sure e.g. tactical + * reading produces proper level of debug prints during simulations. + * But it is safe to enable this only in single-threaded instances! */ +//#define DEBUGL_BY_PLAYOUT + +#define PLDEBUGL(n) DEBUGL_(policy->debug_level, n) + + +coord_t +play_random_move(struct playout_setup *setup, + struct board *b, enum stone color, + struct playout_policy *policy) +{ + coord_t coord = pass; + + if (setup->prepolicy_hook) { + coord = setup->prepolicy_hook(policy, setup, b, color); + // fprintf(stderr, "prehook: %s\n", coord2sstr(coord, b)); + } + + if (is_pass(coord)) { + coord = policy->choose(policy, setup, b, color); + // fprintf(stderr, "policy: %s\n", coord2sstr(coord, b)); + } + + if (is_pass(coord) && setup->postpolicy_hook) { + coord = setup->postpolicy_hook(policy, setup, b, color); + // fprintf(stderr, "posthook: %s\n", coord2sstr(coord, b)); + } + + if (is_pass(coord)) { +play_random: + /* Defer to uniformly random move choice. */ + /* This must never happen if the policy is tracking + * internal board state, obviously. */ + assert(!policy->setboard); + board_play_random(b, color, &coord, (ppr_permit) policy->permit, policy); + + } else { + struct move m; + m.coord = coord; m.color = color; + if (board_play(b, &m) < 0) { + if (PLDEBUGL(4)) { + fprintf(stderr, "Pre-picked move %d,%d is ILLEGAL:\n", + coord_x(coord, b), coord_y(coord, b)); + board_print(b, stderr); + } + goto play_random; + } + } + + return coord; +} + +int +play_random_game(struct playout_setup *setup, + struct board *b, enum stone starting_color, + struct playout_amafmap *amafmap, + struct board_ownermap *ownermap, + struct playout_policy *policy) +{ + assert(setup && policy); + + int gamelen = setup->gamelen - b->moves; + if (gamelen < 10) + gamelen = 10; + + if (policy->setboard) + policy->setboard(policy, b); +#ifdef DEBUGL_BY_PLAYOUT + int debug_level_orig = debug_level; + debug_level = policy->debug_level; +#endif + + enum stone color = starting_color; + + int passes = is_pass(b->last_move.coord) && b->moves > 0; + + while (gamelen-- && passes < 2) { + coord_t coord = play_random_move(setup, b, color, policy); + +#if 0 + /* For UCT, superko test here is downright harmful since + * in superko-likely situation we throw away literally + * 95% of our playouts; UCT will deal with this fine by + * itself. */ + if (unlikely(b->superko_violation)) { + /* We ignore superko violations that are suicides. These + * are common only at the end of the game and are + * rather harmless. (They will not go through as a root + * move anyway.) */ + if (group_at(b, coord)) { + if (DEBUGL(3)) { + fprintf(stderr, "Superko fun at %d,%d in\n", coord_x(coord, b), coord_y(coord, b)); + if (DEBUGL(4)) + board_print(b, stderr); + } + return 0; + } else { + if (DEBUGL(6)) { + fprintf(stderr, "Ignoring superko at %d,%d in\n", coord_x(coord, b), coord_y(coord, b)); + board_print(b, stderr); + } + b->superko_violation = false; + } + } +#endif + + if (PLDEBUGL(7)) { + fprintf(stderr, "%s %s\n", stone2str(color), coord2sstr(coord, b)); + if (PLDEBUGL(8)) + board_print(b, stderr); + } + + if (unlikely(is_pass(coord))) { + passes++; + } else { + passes = 0; + } + if (amafmap) { + assert(amafmap->gamelen < MAX_GAMELEN); + amafmap->is_ko_capture[amafmap->gamelen] = board_playing_ko_threat(b); + amafmap->game[amafmap->gamelen++] = coord; + } + + if (setup->mercymin && abs(b->captures[S_BLACK] - b->captures[S_WHITE]) > setup->mercymin) + break; + + color = stone_other(color); + } + + floating_t score = board_fast_score(b); + int result = (starting_color == S_WHITE ? score * 2 : - (score * 2)); + + if (DEBUGL(6)) { + fprintf(stderr, "Random playout result: %d (W %f)\n", result, score); + if (DEBUGL(7)) + board_print(b, stderr); + } + + if (ownermap) + board_ownermap_fill(ownermap, b); + + if (b->ps) + free(b->ps); + +#ifdef DEBUGL_BY_PLAYOUT + debug_level = debug_level_orig; +#endif + + return result; +} diff --git a/jni/pachi/playout.h b/jni/pachi/playout.h new file mode 100644 index 0000000..591f585 --- /dev/null +++ b/jni/pachi/playout.h @@ -0,0 +1,108 @@ +#ifndef PACHI_PLAYOUT_H +#define PACHI_PLAYOUT_H + +#define MAX_GAMELEN 600 + +struct board; +struct move; +enum stone; +struct prior_map; +struct board_ownermap; + + +/** Playout policy interface: */ + +struct playout_policy; +struct playout_setup; + +/* Initialize policy data structures for new playout; subsequent choose calls + * (but not assess/permit calls!) will all be made on the same board; if + * setboard is used, it is guaranteed that choose will pick all moves played + * on the board subsequently. The routine is expected to initialize b->ps + * with internal data. At the playout end, b->ps will be simply free()d, + * so make sure all data is within single allocated block. */ +typedef void (*playoutp_setboard)(struct playout_policy *playout_policy, struct board *b); + +/* Pick the next playout simulation move. */ +typedef coord_t (*playoutp_choose)(struct playout_policy *playout_policy, struct playout_setup *playout_setup, struct board *b, enum stone to_play); + +/* Set number of won (>0) or lost (<0) games for each considerable + * move (usually a proportion of @games); can leave some untouched + * if policy has no opinion. The number must have proper parity; + * just use uct/prior.h:add_prior_value(). */ +typedef void (*playoutp_assess)(struct playout_policy *playout_policy, struct prior_map *map, int games); + +/* Allow play of randomly selected move. */ +typedef bool (*playoutp_permit)(struct playout_policy *playout_policy, struct board *b, struct move *m); + +/* Tear down the policy state; policy and policy->data will be free()d by caller. */ +typedef void (*playoutp_done)(struct playout_policy *playout_policy); + +struct playout_policy { + int debug_level; + /* We call setboard when we start new playout. + * We call choose when we ask policy about next move. + * We call assess when we ask policy about how good given move is. + * We call permit when we ask policy if we can make a randomly chosen move. */ + playoutp_setboard setboard; + playoutp_choose choose; + playoutp_assess assess; + playoutp_permit permit; + playoutp_done done; + /* Particular playout policy's internal data. */ + void *data; +}; + + +/** Playout engine interface: */ + +/* Engine hook for forcing moves before doing policy decision. + * Return pass to forward to policy. */ +typedef coord_t (*playouth_prepolicy)(struct playout_policy *playout_policy, struct playout_setup *setup, struct board *b, enum stone color); + +/* Engine hook for choosing moves in case policy did not choose + * a move. + * Return pass to forward to uniformly random selection. */ +typedef coord_t (*playouth_postpolicy)(struct playout_policy *playout_policy, struct playout_setup *setup, struct board *b, enum stone color); + +struct playout_setup { + unsigned int gamelen; /* Maximal # of moves in playout. */ + /* Minimal difference between captures to terminate the playout. + * 0 means don't check. */ + int mercymin; + + void *hook_data; // for hook to reference its state + playouth_prepolicy prepolicy_hook; + playouth_postpolicy postpolicy_hook; +}; + + +struct playout_amafmap { + /* We keep record of the game so that we can + * examine nakade moves; really going out of our way to + * implement nakade AMAF properly turns out to be crucial + * when reading some tactical positions in depth (even if + * they are just one-stone-snapback). */ + coord_t game[MAX_GAMELEN]; + bool is_ko_capture[MAX_GAMELEN]; + int gamelen; + /* Our current position in the game sequence; in AMAF, we search + * the range [game_baselen, gamelen[ */ + int game_baselen; +}; + + +/* >0: starting_color wins, <0: starting_color loses; the actual + * number is a DOUBLE of the score difference + * 0: superko inside the game tree (XXX: jigo not handled) */ +int play_random_game(struct playout_setup *setup, + struct board *b, enum stone starting_color, + struct playout_amafmap *amafmap, + struct board_ownermap *ownermap, + struct playout_policy *policy); + +coord_t play_random_move(struct playout_setup *setup, + struct board *b, enum stone color, + struct playout_policy *policy); + +#endif diff --git a/jni/pachi/playout/Makefile b/jni/pachi/playout/Makefile new file mode 100644 index 0000000..546f6bf --- /dev/null +++ b/jni/pachi/playout/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=moggy.o light.o + +all: playout.a +playout.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/playout/light.c b/jni/pachi/playout/light.c new file mode 100644 index 0000000..53e7deb --- /dev/null +++ b/jni/pachi/playout/light.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "playout.h" +#include "playout/light.h" +#include "random.h" + + +#define PLDEBUGL(n) DEBUGL_(p->debug_level, n) + + +coord_t +playout_light_choose(struct playout_policy *p, struct playout_setup *s, struct board *b, enum stone to_play) +{ + return pass; +} + + +struct playout_policy * +playout_light_init(char *arg, struct board *b) +{ + struct playout_policy *p = calloc2(1, sizeof(*p)); + p->choose = playout_light_choose; + + if (arg) + fprintf(stderr, "playout-light: This policy does not accept arguments (%s)\n", arg); + + return p; +} diff --git a/jni/pachi/playout/light.h b/jni/pachi/playout/light.h new file mode 100644 index 0000000..7a7d0b6 --- /dev/null +++ b/jni/pachi/playout/light.h @@ -0,0 +1,9 @@ +#ifndef PACHI_PLAYOUT_LIGHT_H +#define PACHI_PLAYOUT_LIGHT_H + +struct board; +struct playout_policy; + +struct playout_policy *playout_light_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/playout/moggy.c b/jni/pachi/playout/moggy.c new file mode 100644 index 0000000..64fa595 --- /dev/null +++ b/jni/pachi/playout/moggy.c @@ -0,0 +1,1066 @@ +/* Heuristical playout (and tree prior) policy modelled primarily after + * the description of the Mogo engine. */ + +#include +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "joseki/base.h" +#include "mq.h" +#include "pattern3.h" +#include "playout.h" +#include "playout/moggy.h" +#include "random.h" +#include "tactics/1lib.h" +#include "tactics/2lib.h" +#include "tactics/nlib.h" +#include "tactics/ladder.h" +#include "tactics/nakade.h" +#include "tactics/selfatari.h" +#include "uct/prior.h" + +#define PLDEBUGL(n) DEBUGL_(p->debug_level, n) + + +/* In case "seqchoose" move picker is enabled (i.e. no "fullchoose" + * parameter passed), we stochastically apply fixed set of decision + * rules in given order. + * + * In "fullchoose" mode, we instead build a move queue of variously + * tagged candidates, then consider a probability distribution over + * them and pick a move from that. */ + +/* Move queue tags. Some may be even undesirable - these moves then + * receive a penalty; penalty tags should be used only when it is + * certain the move would be considered anyway. */ +enum mq_tag { + MQ_KO = 0, + MQ_LATARI, + MQ_L2LIB, +#define MQ_LADDER MQ_L2LIB /* XXX: We want to fit in char still! */ + MQ_LNLIB, + MQ_PAT3, + MQ_GATARI, + MQ_JOSEKI, + MQ_NAKADE, + MQ_MAX +}; + + +/* Note that the context can be shared by multiple threads! */ + +struct moggy_policy { + unsigned int lcapturerate, atarirate, nlibrate, ladderrate, capturerate, patternrate, korate, josekirate, nakaderate; + unsigned int selfatarirate, eyefillrate, alwaysccaprate; + unsigned int fillboardtries; + int koage; + /* Whether to look for patterns around second-to-last move. */ + bool pattern2; + /* Whether, when self-atari attempt is detected, to play the other + * group's liberty if that is non-self-atari. */ + bool selfatari_other; + /* Whether to read out ladders elsewhere than near the board + * in the playouts. Note that such ladder testing is currently + * a fairly expensive operation. */ + bool middle_ladder; + + /* 1lib settings: */ + /* Whether to always pick from moves capturing all groups in + * global_atari_check(). */ + bool capcheckall; + /* Prior stone weighting. Weight of each stone between + * cap_stone_min and cap_stone_max is (assess*100)/cap_stone_denom. */ + int cap_stone_min, cap_stone_max; + int cap_stone_denom; + + /* 2lib settings: */ + bool atari_def_no_hopeless; + bool atari_miaisafe; + + /* nlib settings: */ + int nlib_count; + + struct joseki_dict *jdict; + struct pattern3s patterns; + + /* Gamma values for queue tags - correspond to probabilities. */ + /* XXX: Tune. */ + double mq_prob[MQ_MAX], tenuki_prob; +}; + + +static char moggy_patterns_src[][11] = { + /* hane pattern - enclosing hane */ + "XOX" + "..." + "???", + /* hane pattern - non-cutting hane */ + "YO." + "..." + "?.?", + /* hane pattern - magari */ + "XO?" + "X.." + "x.?", + /* hane pattern - thin hane */ + "XOO" + "..." + "?.?" "X", + /* generic pattern - katatsuke or diagonal attachment; similar to magari */ + ".Q." + "Y.." + "...", + /* cut1 pattern (kiri) - unprotected cut */ + "XO?" + "O.o" + "?o?", + /* cut1 pattern (kiri) - peeped cut */ + "XO?" + "O.X" + "???", + /* cut2 pattern (de) */ + "?X?" + "O.O" + "ooo", + /* cut keima (not in Mogo) */ + "OX?" + "o.O" + "???", /* o?? has some pathological tsumego cases */ + /* side pattern - chase */ + "X.?" + "O.?" + "##?", + /* side pattern - block side cut */ + "OX?" + "X.O" + "###", + /* side pattern - block side connection */ + "?X?" + "x.O" + "###", + /* side pattern - sagari (SUSPICIOUS) */ + "?XQ" + "x.x" /* Mogo has "x.?" */ + "###" /* Mogo has "X" */, + /* side pattern - throw-in (SUSPICIOUS) */ +#if 0 + "?OX" + "o.O" + "?##" "X", +#endif + /* side pattern - cut (SUSPICIOUS) */ + "?OY" + "Y.O" + "###" /* Mogo has "X" */, + /* side pattern - eye piercing: + * # O O O . + * # O . O . + * # . . . . + * # # # # # */ + /* side pattern - make eye */ + "?X." + "Q.X" + "###", +#if 0 + "Oxx" + "..." + "###", +#endif +}; +#define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0]) + +static inline bool +test_pattern3_here(struct playout_policy *p, struct board *b, struct move *m, bool middle_ladder) +{ + struct moggy_policy *pp = p->data; + /* Check if 3x3 pattern is matched by given move... */ + if (!pattern3_move_here(&pp->patterns, b, m)) + return false; + /* ...and the move is not obviously stupid. */ + if (is_bad_selfatari(b, m->color, m->coord)) + return false; + /* Ladder moves are stupid. */ + group_t atari_neighbor = board_get_atari_neighbor(b, m->coord, m->color); + if (atari_neighbor && is_ladder(b, m->coord, atari_neighbor, middle_ladder) + && !can_countercapture(b, board_at(b, group_base(atari_neighbor)), + atari_neighbor, m->color, NULL, 0)) + return false; + return true; +} + +static void +apply_pattern_here(struct playout_policy *p, struct board *b, coord_t c, enum stone color, struct move_queue *q) +{ + struct moggy_policy *pp = p->data; + struct move m2 = { .coord = c, .color = color }; + if (board_is_valid_move(b, &m2) && test_pattern3_here(p, b, &m2, pp->middle_ladder)) + mq_add(q, c, 1<coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD) + return; + + foreach_8neighbor(b, m->coord) { + apply_pattern_here(p, b, c, stone_other(m->color), q); + } foreach_8neighbor_end; + + if (mm) { /* Second move for pattern searching */ + foreach_8neighbor(b, mm->coord) { + if (coord_is_8adjecent(m->coord, c, b)) + continue; + apply_pattern_here(p, b, c, stone_other(m->color), q); + } foreach_8neighbor_end; + } + + if (PLDEBUGL(5)) + mq_print(q, b, "Pattern"); +} + + +static void +joseki_check(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q) +{ + struct moggy_policy *pp = p->data; + if (!pp->jdict) + return; + + for (int i = 0; i < 4; i++) { + hash_t h = b->qhash[i] & joseki_hash_mask; + coord_t *cc = pp->jdict->patterns[h].moves[to_play]; + if (!cc) continue; + for (; !is_pass(*cc); cc++) { + if (coord_quadrant(*cc, b) != i) + continue; + mq_add(q, *cc, 1<moves > 0 && PLDEBUGL(5)) + mq_print(q, b, "Joseki"); +} + +static void +global_atari_check(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q) +{ + if (b->clen == 0) + return; + + struct moggy_policy *pp = p->data; + if (pp->capcheckall) { + for (int g = 0; g < b->clen; g++) + group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, pp->middle_ladder, 1<clen); + for (int g = g_base; g < b->clen; g++) { + group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, pp->middle_ladder, 1<moves > 0) { + /* XXX: Try carrying on. */ + if (PLDEBUGL(5)) + mq_print(q, b, "Global atari"); + return; + } + } + for (int g = 0; g < g_base; g++) { + group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, pp->middle_ladder, 1<moves > 0) { + /* XXX: Try carrying on. */ + if (PLDEBUGL(5)) + mq_print(q, b, "Global atari"); + return; + } + } + return; +} + +static void +local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q) +{ + struct moggy_policy *pp = p->data; + + /* Did the opponent play a self-atari? */ + if (board_group_info(b, group_at(b, m->coord)).libs == 1) { + group_atari_check(pp->alwaysccaprate, b, group_at(b, m->coord), stone_other(m->color), q, NULL, pp->middle_ladder, 1<coord, { + group_t g = group_at(b, c); + if (!g || board_group_info(b, g).libs != 1) + continue; + group_atari_check(pp->alwaysccaprate, b, g, stone_other(m->color), q, NULL, pp->middle_ladder, 1<coord); + + if (board_group_info(b, group).libs != 2) + return; + + for (int i = 0; i < 2; i++) { + coord_t chase = board_group_info(b, group).lib[i]; + coord_t escape = board_group_info(b, group).lib[1 - i]; + if (wouldbe_ladder(b, group, escape, chase, board_at(b, group))) + mq_add(q, chase, 1<moves > 0 && PLDEBUGL(5)) + mq_print(q, b, "Ladder"); +} + + +static void +local_2lib_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q) +{ + struct moggy_policy *pp = p->data; + group_t group = group_at(b, m->coord), group2 = 0; + + /* Does the opponent have just two liberties? */ + if (board_group_info(b, group).libs == 2) { + group_2lib_check(b, group, stone_other(m->color), q, 1<atari_miaisafe, pp->atari_def_no_hopeless); +#if 0 + /* We always prefer to take off an enemy chain liberty + * before pulling out ourselves. */ + /* XXX: We aren't guaranteed to return to that group + * later. */ + if (q->moves) + return q->move[fast_random(q->moves)]; +#endif + } + + /* Then he took a third liberty from neighboring chain? */ + foreach_neighbor(b, m->coord, { + group_t g = group_at(b, c); + if (!g || g == group || g == group2 || board_group_info(b, g).libs != 2) + continue; + group_2lib_check(b, g, stone_other(m->color), q, 1<atari_miaisafe, pp->atari_def_no_hopeless); + group2 = g; // prevent trivial repeated checks + }); + + if (PLDEBUGL(5)) + mq_print(q, b, "Local 2lib"); +} + +static void +local_nlib_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q) +{ + struct moggy_policy *pp = p->data; + enum stone color = stone_other(m->color); + + /* Attacking N-liberty groups in general is probably + * not feasible. What we are primarily concerned about is + * counter-attacking groups that have two physical liberties, + * but three effective liberties: + * + * . O . . . . # + * O O X X X X # + * . X O O X . # + * . X O . O X # + * . X O O . X # + * # # # # # # # + * + * The time for this to come is when the opponent took a liberty + * of ours, making a few-liberty group. Therefore, we focus + * purely on defense. + * + * There is a tradeoff - down to how many liberties we need to + * be to start looking? nlib_count=3 will work for the left black + * group (2lib-solver will suggest connecting the false eye), but + * not for top black group (it is too late to start playing 3-3 + * capturing race). Also, we cannot prevent stupidly taking an + * outside liberty ourselves; the higher nlib_count, the higher + * the chance we withstand this. + * + * However, higher nlib_count means that we will waste more time + * checking non-urgent or alive groups, and we will play silly + * or wasted moves around alive groups. */ + + group_t group2 = 0; + foreach_8neighbor(b, m->coord) { + group_t g = group_at(b, c); + if (!g || group2 == g || board_at(b, c) != color) + continue; + if (board_group_info(b, g).libs < 3 || board_group_info(b, g).libs > pp->nlib_count) + continue; + group_nlib_defense_check(b, g, color, q, 1<coord, { + if (board_at(b, c) != S_NONE) + continue; + if (is_pass(empty)) { + empty = c; + continue; + } + if (!coord_is_8adjecent(c, empty, b)) { + /* Seems like impossible nakade + * shape! */ + return pass; + } + }); + assert(!is_pass(empty)); + + coord_t nakade = nakade_point(b, empty, stone_other(to_play)); + if (PLDEBUGL(5) && !is_pass(nakade)) + fprintf(stderr, "Nakade: %s\n", coord2sstr(nakade, b)); + return nakade; +} + +coord_t +fillboard_check(struct playout_policy *p, struct board *b) +{ + struct moggy_policy *pp = p->data; + unsigned int fbtries = b->flen / 8; + if (pp->fillboardtries < fbtries) + fbtries = pp->fillboardtries; + + for (unsigned int i = 0; i < fbtries; i++) { + coord_t coord = b->f[fast_random(b->flen)]; + if (immediate_liberty_count(b, coord) != 4) + continue; + foreach_diag_neighbor(b, coord) { + if (board_at(b, c) != S_NONE) + goto next_try; + } foreach_diag_neighbor_end; + return coord; +next_try: + ; + } + return pass; +} + +coord_t +playout_moggy_seqchoose(struct playout_policy *p, struct playout_setup *s, struct board *b, enum stone to_play) +{ + struct moggy_policy *pp = p->data; + + if (PLDEBUGL(5)) + board_print(b, stderr); + + /* Ko fight check */ + if (!is_pass(b->last_ko.coord) && is_pass(b->ko.coord) + && b->moves - b->last_ko_age < pp->koage + && pp->korate > fast_random(100)) { + if (board_is_valid_play(b, to_play, b->last_ko.coord) + && !is_bad_selfatari(b, to_play, b->last_ko.coord)) + return b->last_ko.coord; + } + + /* Local checks */ + if (!is_pass(b->last_move.coord)) { + /* Nakade check */ + if (pp->nakaderate > fast_random(100) + && immediate_liberty_count(b, b->last_move.coord) > 0) { + coord_t nakade = nakade_check(p, b, &b->last_move, to_play); + if (!is_pass(nakade)) + return nakade; + } + + /* Local group in atari? */ + if (pp->lcapturerate > fast_random(100)) { + struct move_queue q; q.moves = 0; + local_atari_check(p, b, &b->last_move, &q); + if (q.moves > 0) + return mq_pick(&q); + } + + /* Local group trying to escape ladder? */ + if (pp->ladderrate > fast_random(100)) { + struct move_queue q; q.moves = 0; + local_ladder_check(p, b, &b->last_move, &q); + if (q.moves > 0) + return mq_pick(&q); + } + + /* Local group can be PUT in atari? */ + if (pp->atarirate > fast_random(100)) { + struct move_queue q; q.moves = 0; + local_2lib_check(p, b, &b->last_move, &q); + if (q.moves > 0) + return mq_pick(&q); + } + + /* Local group reduced some of our groups to 3 libs? */ + if (pp->nlibrate > fast_random(100)) { + struct move_queue q; q.moves = 0; + local_nlib_check(p, b, &b->last_move, &q); + if (q.moves > 0) + return mq_pick(&q); + } + + /* Check for patterns we know */ + if (pp->patternrate > fast_random(100)) { + struct move_queue q; q.moves = 0; + apply_pattern(p, b, &b->last_move, + pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL, + &q); + if (q.moves > 0) + return mq_pick(&q); + } + } + + /* Global checks */ + + /* Any groups in atari? */ + if (pp->capturerate > fast_random(100)) { + struct move_queue q; q.moves = 0; + global_atari_check(p, b, to_play, &q); + if (q.moves > 0) + return mq_pick(&q); + } + + /* Joseki moves? */ + if (pp->josekirate > fast_random(100)) { + struct move_queue q; q.moves = 0; + joseki_check(p, b, to_play, &q); + if (q.moves > 0) + return mq_pick(&q); + } + + /* Fill board */ + if (pp->fillboardtries > 0) { + coord_t c = fillboard_check(p, b); + if (!is_pass(c)) + return c; + } + + return pass; +} + +/* Pick a move from queue q, giving different likelihoods to moves + * based on their tags. */ +coord_t +mq_tagged_choose(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q) +{ + struct moggy_policy *pp = p->data; + + /* First, merge all entries for a move. */ + /* We use a naive O(N^2) since the average length of the queue + * is about 1.4. */ + for (unsigned int i = 0; i < q->moves; i++) { + for (unsigned int j = i + 1; j < q->moves; j++) { + if (q->move[i] != q->move[j]) + continue; + q->tag[i] |= q->tag[j]; + q->moves--; + q->tag[j] = q->tag[q->moves]; + q->move[j] = q->move[q->moves]; + } + } + + /* Now, construct a probdist. */ + fixp_t total = 0; + fixp_t pd[q->moves]; + for (unsigned int i = 0; i < q->moves; i++) { + double val = 1.0; + assert(q->tag[i] != 0); + for (int j = 0; j < MQ_MAX; j++) + if (q->tag[i] & (1<move[i], b), q->tag[i], j, val, pp->mq_prob[j]); + val *= pp->mq_prob[j]; + } + pd[i] = double_to_fixp(val); + total += pd[i]; + } + total += double_to_fixp(pp->tenuki_prob); + + /* Finally, pick a move! */ + fixp_t stab = fast_irandom(total); + for (unsigned int i = 0; i < q->moves; i++) { + //fprintf(stderr, "%s(%x) %f (%f/%f)\n", coord2sstr(q->move[i], b), q->tag[i], fixp_to_double(stab), fixp_to_double(pd[i]), fixp_to_double(total)); + if (stab < pd[i]) + return q->move[i]; + stab -= pd[i]; + } + + /* Tenuki. */ + assert(stab < double_to_fixp(pp->tenuki_prob)); + return pass; +} + +coord_t +playout_moggy_fullchoose(struct playout_policy *p, struct playout_setup *s, struct board *b, enum stone to_play) +{ + struct moggy_policy *pp = p->data; + struct move_queue q; q.moves = 0; + + if (PLDEBUGL(5)) + board_print(b, stderr); + + /* Ko fight check */ + if (!is_pass(b->last_ko.coord) && is_pass(b->ko.coord) + && b->moves - b->last_ko_age < pp->koage) { + if (board_is_valid_play(b, to_play, b->last_ko.coord) + && !is_bad_selfatari(b, to_play, b->last_ko.coord)) + mq_add(&q, b->last_ko.coord, 1<last_move.coord)) { + /* Nakade check */ + if (immediate_liberty_count(b, b->last_move.coord) > 0) { + coord_t nakade = nakade_check(p, b, &b->last_move, to_play); + if (!is_pass(nakade)) + mq_add(&q, nakade, 1<last_move, &q); + + /* Local group trying to escape ladder? */ + local_ladder_check(p, b, &b->last_move, &q); + + /* Local group can be PUT in atari? */ + local_2lib_check(p, b, &b->last_move, &q); + + /* Local group reduced some of our groups to 3 libs? */ + local_nlib_check(p, b, &b->last_move, &q); + + /* Check for patterns we know */ + apply_pattern(p, b, &b->last_move, + pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL, + &q); + } + + /* Global checks */ + + /* Any groups in atari? */ + global_atari_check(p, b, to_play, &q); + + /* Joseki moves? */ + joseki_check(p, b, to_play, &q); + +#if 0 + /* Average length of the queue is 1.4 move. */ + printf("MQL %d ", q.moves); + for (unsigned int i = 0; i < q.moves; i++) + printf("%s ", coord2sstr(q.move[i], b)); + printf("\n"); +#endif + + if (q.moves > 0) + return mq_tagged_choose(p, b, to_play, &q); + + /* Fill board */ + if (pp->fillboardtries > 0) { + coord_t c = fillboard_check(p, b); + if (!is_pass(c)) + return c; + } + + return pass; +} + + +void +playout_moggy_assess_group(struct playout_policy *p, struct prior_map *map, group_t g, int games) +{ + struct moggy_policy *pp = p->data; + struct board *b = map->b; + struct move_queue q; q.moves = 0; + + if (board_group_info(b, g).libs > pp->nlib_count) + return; + + if (PLDEBUGL(5)) { + fprintf(stderr, "ASSESS of group %s:\n", coord2sstr(g, b)); + board_print(b, stderr); + } + + if (board_group_info(b, g).libs > 2) { + if (!pp->nlibrate) + return; + if (board_at(b, g) != map->to_play) + return; // we do only defense + group_nlib_defense_check(b, g, map->to_play, &q, 0); + while (q.moves--) { + coord_t coord = q.move[q.moves]; + if (PLDEBUGL(5)) + fprintf(stderr, "1.0: nlib %s\n", coord2sstr(coord, b)); + int assess = games / 2; + add_prior_value(map, coord, 1, assess); + } + return; + } + + if (board_group_info(b, g).libs == 2) { + if (pp->ladderrate) { + /* Make sure to play the correct liberty in case + * this is a group that can be caught in a ladder. */ + bool ladderable = false; + for (int i = 0; i < 2; i++) { + coord_t chase = board_group_info(b, g).lib[i]; + coord_t escape = board_group_info(b, g).lib[1 - i]; + if (wouldbe_ladder(b, g, escape, chase, board_at(b, g))) { + add_prior_value(map, chase, 1, games); + ladderable = true; + } + } + if (ladderable) + return; // do not suggest the other lib at all + } + + if (!pp->atarirate) + return; + group_2lib_check(b, g, map->to_play, &q, 0, pp->atari_miaisafe, pp->atari_def_no_hopeless); + while (q.moves--) { + coord_t coord = q.move[q.moves]; + if (PLDEBUGL(5)) + fprintf(stderr, "1.0: 2lib %s\n", coord2sstr(coord, b)); + int assess = games / 2; + add_prior_value(map, coord, 1, assess); + } + return; + } + + /* This group, sir, is in atari! */ + + coord_t ladder = pass; + group_atari_check(pp->alwaysccaprate, b, g, map->to_play, &q, &ladder, true, 0); + while (q.moves--) { + coord_t coord = q.move[q.moves]; + + /* _Never_ play here if this move plays out + * a caught ladder. */ + if (coord == ladder && !board_playing_ko_threat(b)) { + /* Note that the opposite is not guarded against; + * we do not advise against capturing a laddered + * group (but we don't encourage it either). Such + * a move can simplify tactical situations if we + * can afford it. */ + if (map->to_play != board_at(b, g)) + continue; + /* FIXME: We give the malus even if this move + * captures another group. */ + if (PLDEBUGL(5)) + fprintf(stderr, "0.0: ladder %s\n", coord2sstr(coord, b)); + add_prior_value(map, coord, 0, games); + continue; + } + + if (!pp->capturerate && !pp->lcapturerate) + continue; + + int assess = games * 2; + if (pp->cap_stone_denom > 0) { + int stones = group_stone_count(b, g, pp->cap_stone_max) - (pp->cap_stone_min-1); + assess += (stones > 0 ? stones : 0) * games * 100 / pp->cap_stone_denom; + } + if (PLDEBUGL(5)) + fprintf(stderr, "1.0 (%d): atari %s\n", assess, coord2sstr(coord, b)); + add_prior_value(map, coord, 1, assess); + } +} + +void +playout_moggy_assess_one(struct playout_policy *p, struct prior_map *map, coord_t coord, int games) +{ + struct moggy_policy *pp = p->data; + struct board *b = map->b; + + if (PLDEBUGL(5)) { + fprintf(stderr, "ASSESS of move %s:\n", coord2sstr(coord, b)); + board_print(b, stderr); + } + + /* Is this move a self-atari? */ + if (pp->selfatarirate) { + if (!board_playing_ko_threat(b) && is_bad_selfatari(b, map->to_play, coord)) { + if (PLDEBUGL(5)) + fprintf(stderr, "0.0: self-atari\n"); + add_prior_value(map, coord, 0, games); + if (!pp->selfatari_other) + return; + /* If we can play on the other liberty of the + * endangered group, do! */ + coord = selfatari_cousin(b, map->to_play, coord, NULL); + if (is_pass(coord)) + return; + if (PLDEBUGL(5)) + fprintf(stderr, "1.0: self-atari redirect %s\n", coord2sstr(coord, b)); + add_prior_value(map, coord, 1.0, games); + return; + } + } + + /* Pattern check */ + if (pp->patternrate) { + struct move m = { .color = map->to_play, .coord = coord }; + if (test_pattern3_here(p, b, &m, true)) { + if (PLDEBUGL(5)) + fprintf(stderr, "1.0: pattern\n"); + add_prior_value(map, coord, 1, games); + } + } + + return; +} + +void +playout_moggy_assess(struct playout_policy *p, struct prior_map *map, int games) +{ + struct moggy_policy *pp = p->data; + + /* First, go through all endangered groups. */ + for (group_t g = 1; g < board_size2(map->b); g++) + if (group_at(map->b, g) == g) + playout_moggy_assess_group(p, map, g, games); + + /* Then, assess individual moves. */ + if (!pp->patternrate && !pp->selfatarirate) + return; + foreach_free_point(map->b) { + if (map->consider[c]) + playout_moggy_assess_one(p, map, c, games); + } foreach_free_point_end; +} + +bool +playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m) +{ + struct moggy_policy *pp = p->data; + + /* The idea is simple for now - never allow self-atari moves. + * They suck in general, but this also permits us to actually + * handle seki in the playout stage. */ + + if (fast_random(100) >= pp->selfatarirate) { + if (PLDEBUGL(5)) + fprintf(stderr, "skipping sar test\n"); + goto sar_skip; + } + bool selfatari = is_bad_selfatari(b, m->color, m->coord); + if (selfatari) { + if (PLDEBUGL(5)) + fprintf(stderr, "__ Prohibiting self-atari %s %s\n", + stone2str(m->color), coord2sstr(m->coord, b)); + if (pp->selfatari_other) { + /* Ok, try the other liberty of the atari'd group. */ + coord_t c = selfatari_cousin(b, m->color, m->coord, NULL); + if (is_pass(c)) return false; + if (PLDEBUGL(5)) + fprintf(stderr, "___ Redirecting to other lib %s\n", + coord2sstr(c, b)); + m->coord = c; + return true; + } + return false; + } +sar_skip: + + /* Check if we don't seem to be filling our eye. This should + * happen only for false eyes, but some of them are in fact + * real eyes with diagonal filled by a dead stone. Prefer + * to counter-capture in that case. */ + if (fast_random(100) >= pp->eyefillrate) { + if (PLDEBUGL(5)) + fprintf(stderr, "skipping eyefill test\n"); + goto eyefill_skip; + } + bool eyefill = board_is_eyelike(b, m->coord, m->color); + if (eyefill) { + foreach_diag_neighbor(b, m->coord) { + if (board_at(b, c) != stone_other(m->color)) + continue; + switch (board_group_info(b, group_at(b, c)).libs) { + case 1: /* Capture! */ + c = board_group_info(b, group_at(b, c)).lib[0]; + if (PLDEBUGL(5)) + fprintf(stderr, "___ Redirecting to capture %s\n", + coord2sstr(c, b)); + m->coord = c; + return true; + case 2: /* Try to switch to some 2-lib neighbor. */ + for (int i = 0; i < 2; i++) { + coord_t l = board_group_info(b, group_at(b, c)).lib[i]; + if (board_is_one_point_eye(b, l, board_at(b, c))) + continue; + if (is_bad_selfatari(b, m->color, l)) + continue; + m->coord = l; + return true; + } + break; + } + } foreach_diag_neighbor_end; + } + +eyefill_skip: + return true; +} + + +struct playout_policy * +playout_moggy_init(char *arg, struct board *b, struct joseki_dict *jdict) +{ + struct playout_policy *p = calloc2(1, sizeof(*p)); + struct moggy_policy *pp = calloc2(1, sizeof(*pp)); + p->data = pp; + p->choose = playout_moggy_seqchoose; + p->assess = playout_moggy_assess; + p->permit = playout_moggy_permit; + + pp->jdict = jdict; + + /* These settings are tuned for 19x19 play with several threads + * on reasonable time limits (i.e., rather large number of playouts). + * XXX: no 9x9 tuning has been done recently. */ + int rate = board_large(b) ? 80 : 90; + + pp->lcapturerate = pp->atarirate = pp->nlibrate + = pp->selfatarirate = pp->josekirate = -1U; + pp->patternrate = 100; + pp->nlibrate = 20; + pp->nakaderate = 20; + pp->pattern2 = true; + pp->lcapturerate = 90; + pp->korate = 20; pp->koage = 4; + pp->alwaysccaprate = 40; + pp->eyefillrate = 60; + pp->selfatari_other = true; + + pp->cap_stone_min = 2; + pp->cap_stone_max = 15; + pp->cap_stone_denom = 200; + + pp->atari_def_no_hopeless = !board_large(b); + pp->atari_miaisafe = true; + pp->nlib_count = 4; + + /* C is stupid. */ + double mq_prob_default[MQ_MAX] = { + [MQ_KO] = 6.0, + [MQ_NAKADE] = 5.5, + [MQ_LATARI] = 5.0, + [MQ_L2LIB] = 4.0, + [MQ_LNLIB] = 3.5, + [MQ_PAT3] = 3.0, + [MQ_GATARI] = 2.0, + [MQ_JOSEKI] = 1.0, + }; + memcpy(pp->mq_prob, mq_prob_default, sizeof(pp->mq_prob)); + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "debug") && optval) { + p->debug_level = atoi(optval); + } else if (!strcasecmp(optname, "lcapturerate") && optval) { + pp->lcapturerate = atoi(optval); + } else if (!strcasecmp(optname, "ladderrate") && optval) { + pp->ladderrate = atoi(optval); + } else if (!strcasecmp(optname, "atarirate") && optval) { + pp->atarirate = atoi(optval); + } else if (!strcasecmp(optname, "nlibrate") && optval) { + pp->nlibrate = atoi(optval); + } else if (!strcasecmp(optname, "capturerate") && optval) { + pp->capturerate = atoi(optval); + } else if (!strcasecmp(optname, "patternrate") && optval) { + pp->patternrate = atoi(optval); + } else if (!strcasecmp(optname, "selfatarirate") && optval) { + pp->selfatarirate = atoi(optval); + } else if (!strcasecmp(optname, "eyefillrate") && optval) { + pp->eyefillrate = atoi(optval); + } else if (!strcasecmp(optname, "korate") && optval) { + pp->korate = atoi(optval); + } else if (!strcasecmp(optname, "josekirate") && optval) { + pp->josekirate = atoi(optval); + } else if (!strcasecmp(optname, "nakaderate") && optval) { + pp->nakaderate = atoi(optval); + } else if (!strcasecmp(optname, "alwaysccaprate") && optval) { + pp->alwaysccaprate = atoi(optval); + } else if (!strcasecmp(optname, "rate") && optval) { + rate = atoi(optval); + } else if (!strcasecmp(optname, "fillboardtries")) { + pp->fillboardtries = atoi(optval); + } else if (!strcasecmp(optname, "koage") && optval) { + pp->koage = atoi(optval); + } else if (!strcasecmp(optname, "pattern2")) { + pp->pattern2 = optval && *optval == '0' ? false : true; + } else if (!strcasecmp(optname, "selfatari_other")) { + pp->selfatari_other = optval && *optval == '0' ? false : true; + } else if (!strcasecmp(optname, "capcheckall")) { + pp->capcheckall = optval && *optval == '0' ? false : true; + } else if (!strcasecmp(optname, "cap_stone_min") && optval) { + pp->cap_stone_min = atoi(optval); + } else if (!strcasecmp(optname, "cap_stone_max") && optval) { + pp->cap_stone_max = atoi(optval); + } else if (!strcasecmp(optname, "cap_stone_denom") && optval) { + pp->cap_stone_denom = atoi(optval); + } else if (!strcasecmp(optname, "atari_miaisafe")) { + pp->atari_miaisafe = optval && *optval == '0' ? false : true; + } else if (!strcasecmp(optname, "atari_def_no_hopeless")) { + pp->atari_def_no_hopeless = optval && *optval == '0' ? false : true; + } else if (!strcasecmp(optname, "nlib_count") && optval) { + pp->nlib_count = atoi(optval); + } else if (!strcasecmp(optname, "middle_ladder")) { + pp->middle_ladder = optval && *optval == '0' ? false : true; + } else if (!strcasecmp(optname, "fullchoose")) { + p->choose = optval && *optval == '0' ? playout_moggy_seqchoose : playout_moggy_fullchoose; + } else if (!strcasecmp(optname, "mqprob") && optval) { + /* KO%LATARI%L2LIB%LNLIB%PAT3%GATARI%JOSEKI%NAKADE */ + for (int i = 0; *optval && i < MQ_MAX; i++) { + pp->mq_prob[i] = atof(optval); + optval += strcspn(optval, "%"); + if (*optval) optval++; + } + } else if (!strcasecmp(optname, "tenukiprob") && optval) { + pp->tenuki_prob = atof(optval); + } else { + fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname); + exit(1); + } + } + } + if (pp->lcapturerate == -1U) pp->lcapturerate = rate; + if (pp->atarirate == -1U) pp->atarirate = rate; + if (pp->nlibrate == -1U) pp->nlibrate = rate; + if (pp->capturerate == -1U) pp->capturerate = rate; + if (pp->patternrate == -1U) pp->patternrate = rate; + if (pp->selfatarirate == -1U) pp->selfatarirate = rate; + if (pp->eyefillrate == -1U) pp->eyefillrate = rate; + if (pp->korate == -1U) pp->korate = rate; + if (pp->josekirate == -1U) pp->josekirate = rate; + if (pp->ladderrate == -1U) pp->ladderrate = rate; + if (pp->nakaderate == -1U) pp->nakaderate = rate; + if (pp->alwaysccaprate == -1U) pp->alwaysccaprate = rate; + + pattern3s_init(&pp->patterns, moggy_patterns_src, moggy_patterns_src_n); + + return p; +} diff --git a/jni/pachi/playout/moggy.h b/jni/pachi/playout/moggy.h new file mode 100644 index 0000000..a590938 --- /dev/null +++ b/jni/pachi/playout/moggy.h @@ -0,0 +1,10 @@ +#ifndef PACHI_PLAYOUT_MOGGY_H +#define PACHI_PLAYOUT_MOGGY_H + +struct board; +struct playout_policy; +struct joseki_dict; + +struct playout_policy *playout_moggy_init(char *arg, struct board *b, struct joseki_dict *jdict); + +#endif diff --git a/jni/pachi/probdist.c b/jni/pachi/probdist.c new file mode 100644 index 0000000..5e00b64 --- /dev/null +++ b/jni/pachi/probdist.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" +#include "move.h" +#include "probdist.h" +#include "random.h" +#include "board.h" + +coord_t +probdist_pick(struct probdist *restrict pd, coord_t *restrict ignore) +{ + fixp_t total = probdist_total(pd); + fixp_t stab = fast_irandom(total); + if (DEBUGL(6)) + fprintf(stderr, "stab %f / %f\n", fixp_to_double(stab), fixp_to_double(total)); + + int r = 1; + coord_t c = board_size(pd->b) + 1; + while (stab > pd->rowtotals[r]) { + if (DEBUGL(6)) + fprintf(stderr, "[%s] skipping row %f (%f)\n", coord2sstr(c, pd->b), fixp_to_double(pd->rowtotals[r]), fixp_to_double(stab)); + + stab -= pd->rowtotals[r]; + r++; assert(r < board_size(pd->b)); + + c += board_size(pd->b); + while (!is_pass(*ignore) && *ignore <= c) + ignore++; + } + + for (; c < board_size2(pd->b); c++) { + if (DEBUGL(6)) + fprintf(stderr, "[%s] %f (%f)\n", coord2sstr(c, pd->b), fixp_to_double(pd->items[c]), fixp_to_double(stab)); + + assert(is_pass(*ignore) || c <= *ignore); + if (c == *ignore) { + if (DEBUGL(6)) + fprintf(stderr, "\tignored\n"); + ignore++; + continue; + } + + if (stab <= pd->items[c]) + return c; + stab -= pd->items[c]; + } + + fprintf(stderr, "overstab %f (total %f)\n", fixp_to_double(stab), fixp_to_double(total)); + assert(0); + return -1; +} diff --git a/jni/pachi/probdist.h b/jni/pachi/probdist.h new file mode 100644 index 0000000..8586786 --- /dev/null +++ b/jni/pachi/probdist.h @@ -0,0 +1,79 @@ +#ifndef PACHI_PROBDIST_H +#define PACHI_PROBDIST_H + +/* Tools for picking an item according to a probability distribution. */ + +/* The probability distribution structure is designed to be once + * initialized, then random items assigned a value repeatedly and + * random items picked repeatedly as well. */ + +#include "fixp.h" +#include "move.h" +#include "util.h" + +struct board; + +/* The interface looks a bit funny-wrapped since we used to switch + * between different probdist representations. */ + +struct probdist { + struct board *b; + fixp_t *items; // [bsize2], [i] = P(pick==i) + fixp_t *rowtotals; // [bsize], [i] = sum of items in row i + fixp_t total; // sum of all items +}; + + +/* Declare pd_ corresponding to board b_ in the local scope. */ +#define probdist_alloca(pd_, b_) \ + fixp_t pd_ ## __pdi[board_size2(b_)] __attribute__((aligned(32))); memset(pd_ ## __pdi, 0, sizeof(pd_ ## __pdi)); \ + fixp_t pd_ ## __pdr[board_size(b_)] __attribute__((aligned(32))); memset(pd_ ## __pdr, 0, sizeof(pd_ ## __pdr)); \ + struct probdist pd_ = { .b = b_, .items = pd_ ## __pdi, .rowtotals = pd_ ## __pdr, .total = 0 }; + +/* Get the value of given item. */ +#define probdist_one(pd, c) ((pd)->items[c]) + +/* Get the cummulative probability value (normalizing constant). */ +#define probdist_total(pd) ((pd)->total) + +/* Set the value of given item. */ +static void probdist_set(struct probdist *pd, coord_t c, fixp_t val); + +/* Remove the item from the totals; this is used when you then + * pass it in the ignore list to probdist_pick(). Of course you + * must restore the totals afterwards. */ +static void probdist_mute(struct probdist *pd, coord_t c); + +/* Pick a random item. ignore is a pass-terminated sorted array of items + * that are not to be considered (and whose values are not in @total). */ +coord_t probdist_pick(struct probdist *pd, coord_t *ignore); + + +/* Now, we do something horrible - include board.h for the inline helpers. + * Yay for us. */ +#include "board.h" + + +static inline void +probdist_set(struct probdist *pd, coord_t c, fixp_t val) +{ + /* We disable the assertions here since this is quite time-critical + * part of code, and also the compiler is reluctant to inline the + * functions otherwise. */ +#if 0 + assert(c >= 0 && c < board_size2(pd->b)); + assert(val >= 0); +#endif + pd->total += val - pd->items[c]; + pd->rowtotals[coord_y(c, pd->b)] += val - pd->items[c]; + pd->items[c] = val; +} + +static inline void +probdist_mute(struct probdist *pd, coord_t c) +{ + pd->total -= pd->items[c]; + pd->rowtotals[coord_y(c, pd->b)] -= pd->items[c]; +} + +#endif diff --git a/jni/pachi/random.c b/jni/pachi/random.c new file mode 100644 index 0000000..0e2309b --- /dev/null +++ b/jni/pachi/random.c @@ -0,0 +1,101 @@ +#include + +#include "random.h" + + +/* Simple Park-Miller */ + +#ifndef NO_THREAD_LOCAL + +static __thread unsigned long pmseed = 29264; + +void +fast_srandom(unsigned long seed_) +{ + pmseed = seed_; +} + +unsigned long +fast_getseed(void) +{ + return pmseed; +} + +uint16_t +fast_random(unsigned int max) +{ + unsigned long hi, lo; + lo = 16807 * (pmseed & 0xffff); + hi = 16807 * (pmseed >> 16); + lo += (hi & 0x7fff) << 16; + lo += hi >> 15; + pmseed = (lo & 0x7fffffff) + (lo >> 31); + return ((pmseed & 0xffff) * max) >> 16; +} + +float +fast_frandom(void) +{ + /* Construct (1,2) IEEE floating_t from our random integer */ + /* http://rgba.org/articles/sfrand/sfrand.htm */ + union { unsigned long ul; floating_t f; } p; + p.ul = (((pmseed *= 16807) & 0x007fffff) - 1) | 0x3f800000; + return p.f - 1.0f; +} + +#else + +/* Thread local storage not supported through __thread, + * use pthread_getspecific() instead. */ + +#include + +static pthread_key_t seed_key; + +static void __attribute__((constructor)) +random_init(void) +{ + pthread_key_create(&seed_key, NULL); + fast_srandom(29264UL); +} + +void +fast_srandom(unsigned long seed_) +{ + pthread_setspecific(seed_key, (void *)seed_); +} + +unsigned long +fast_getseed(void) +{ + return (unsigned long)pthread_getspecific(seed_key); +} + +uint16_t +fast_random(unsigned int max) +{ + unsigned long pmseed = (unsigned long)pthread_getspecific(seed_key); + unsigned long hi, lo; + lo = 16807 * (pmseed & 0xffff); + hi = 16807 * (pmseed >> 16); + lo += (hi & 0x7fff) << 16; + lo += hi >> 15; + pmseed = (lo & 0x7fffffff) + (lo >> 31); + pthread_setspecific(seed_key, (void *)pmseed); + return ((pmseed & 0xffff) * max) >> 16; +} + +float +fast_frandom(void) +{ + /* Construct (1,2) IEEE floating_t from our random integer */ + /* http://rgba.org/articles/sfrand/sfrand.htm */ + unsigned long pmseed = (unsigned long)pthread_getspecific(seed_key); + pmseed *= 16807; + union { unsigned long ul; floating_t f; } p; + p.ul = ((pmseed & 0x007fffff) - 1) | 0x3f800000; + pthread_setspecific(seed_key, (void *)pmseed); + return p.f - 1.0f; +} + +#endif diff --git a/jni/pachi/random.h b/jni/pachi/random.h new file mode 100644 index 0000000..d00a4a4 --- /dev/null +++ b/jni/pachi/random.h @@ -0,0 +1,30 @@ +#ifndef PACHI_RANDOM_H +#define PACHI_RANDOM_H + +#include + +#include "util.h" + +void fast_srandom(unsigned long seed); +unsigned long fast_getseed(void); + +/* Note that only 16bit numbers can be returned. */ +uint16_t fast_random(unsigned int max); +/* Use this one if you want larger numbers. */ +static uint32_t fast_irandom(unsigned int max); + +/* Get random number in [0..1] range. */ +float fast_frandom(); + + +static inline uint32_t +fast_irandom(unsigned int max) +{ + if (max <= 65536) + return fast_random(max); + int himax = (max - 1) / 65536; + uint16_t hi = fast_random(himax + 1); + return ((uint32_t)hi << 16) | fast_random(hi < himax ? 65536 : max % 65536); +} + +#endif diff --git a/jni/pachi/random/Makefile b/jni/pachi/random/Makefile new file mode 100644 index 0000000..4f2b825 --- /dev/null +++ b/jni/pachi/random/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=random.o + +all: random.a +random.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/random/random.c b/jni/pachi/random/random.c new file mode 100644 index 0000000..e593a6a --- /dev/null +++ b/jni/pachi/random/random.c @@ -0,0 +1,47 @@ +#include +#include + +#include "board.h" +#include "engine.h" +#include "move.h" +#include "random/random.h" + +static coord_t * +random_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + /* Play a random coordinate. However, we must also guard + * against suicide moves; repeat playing while it's a suicide + * unless we keep suiciding; in that case, we probably don't + * have any other moves available and we pass. */ + coord_t coord; + int i = 0; bool suicide = false; + + do { + /* board_play_random() actually plays the move too; + * this is desirable for MC simulations but not within + * the genmove. Make a scratch new board for it. */ + struct board b2; + board_copy(&b2, b); + + board_play_random(&b2, color, &coord, NULL, NULL); + + suicide = (coord != pass && !group_at(&b2, coord)); + board_done_noalloc(&b2); + } while (suicide && i++ < 100); + + return coord_copy(suicide ? pass : coord); +} + +struct engine * +engine_random_init(char *arg, struct board *b) +{ + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "RandomMove"; + e->comment = "I just make random moves. I won't pass as long as there is a place on the board where I can play. When we both pass, I will consider all the stones on the board alive."; + e->genmove = random_genmove; + + if (arg) + fprintf(stderr, "Random: I support no engine arguments\n"); + + return e; +} diff --git a/jni/pachi/random/random.h b/jni/pachi/random/random.h new file mode 100644 index 0000000..890904f --- /dev/null +++ b/jni/pachi/random/random.h @@ -0,0 +1,8 @@ +#ifndef PACHI_RANDOM_RANDOM_H +#define PACHI_RANDOM_RANDOM_H + +#include "engine.h" + +struct engine *engine_random_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/replay/Makefile b/jni/pachi/replay/Makefile new file mode 100644 index 0000000..869a345 --- /dev/null +++ b/jni/pachi/replay/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=replay.o + +all: replay.a +replay.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/replay/replay.c~ b/jni/pachi/replay/replay.c similarity index 100% rename from jni/pachi/replay/replay.c~ rename to jni/pachi/replay/replay.c diff --git a/jni/pachi/replay/replay.h b/jni/pachi/replay/replay.h new file mode 100644 index 0000000..14983a0 --- /dev/null +++ b/jni/pachi/replay/replay.h @@ -0,0 +1,8 @@ +#ifndef PACHI_REPLAY_REPLAY_H +#define PACHI_REPLAY_REPLAY_H + +#include "engine.h" + +struct engine *engine_replay_init(char *arg, struct board *b); + +#endif diff --git a/jni/pachi/stats.h b/jni/pachi/stats.h new file mode 100644 index 0000000..bcae0a0 --- /dev/null +++ b/jni/pachi/stats.h @@ -0,0 +1,101 @@ +#ifndef PACHI_STATS_H +#define PACHI_STATS_H + +#include + +/* Move statistics; we track how good value each move has. */ +/* These operations are supposed to be atomic - reasonably + * safe to perform by multiple threads at once on the same stats. + * What this means in practice is that perhaps the value will get + * slightly wrong, but not drastically corrupted. */ + +struct move_stats { + int playouts; // # of playouts + floating_t value; // BLACK wins/playouts +}; + +/* Add a result to the stats. */ +static void stats_add_result(struct move_stats *s, floating_t result, int playouts); + +/* Remove a result from the stats. */ +static void stats_rm_result(struct move_stats *s, floating_t result, int playouts); + +/* Merge two stats together. THIS IS NOT ATOMIC! */ +static void stats_merge(struct move_stats *dest, struct move_stats *src); + +/* Reverse stats parity. */ +static void stats_reverse_parity(struct move_stats *s); + + +/* We actually do the atomicity in a pretty hackish way - we simply + * rely on the fact that int,floating_t operations should be atomic with + * reasonable compilers (gcc) on reasonable architectures (i386, + * x86_64). */ +/* There is a write order dependency - when we bump the playouts, + * our value must be already correct, otherwise the node will receive + * invalid evaluation if that's made in parallel, esp. when + * current s->playouts is zero. */ + +static inline void +stats_add_result(struct move_stats *s, floating_t result, int playouts) +{ + int s_playouts = s->playouts; + floating_t s_value = s->value; + /* Force the load, another thread can work on the + * values in parallel. */ + __sync_synchronize(); /* full memory barrier */ + + s_playouts += playouts; + s_value += (result - s_value) * playouts / s_playouts; + + /* We rely on the fact that these two assignments are atomic. */ + s->value = s_value; + __sync_synchronize(); /* full memory barrier */ + s->playouts = s_playouts; +} + +static inline void +stats_rm_result(struct move_stats *s, floating_t result, int playouts) +{ + if (s->playouts > playouts) { + int s_playouts = s->playouts; + floating_t s_value = s->value; + /* Force the load, another thread can work on the + * values in parallel. */ + __sync_synchronize(); /* full memory barrier */ + + s_playouts -= playouts; + s_value += (s_value - result) * playouts / s_playouts; + + /* We rely on the fact that these two assignments are atomic. */ + s->value = s_value; + __sync_synchronize(); /* full memory barrier */ + s->playouts = s_playouts; + + } else { + /* We don't touch the value, since in parallel, another + * thread can be adding a result, thus raising the + * playouts count after we zero the value. Instead, + * leaving the value as is with zero playouts should + * not break anything. */ + s->playouts = 0; + } +} + +static inline void +stats_merge(struct move_stats *dest, struct move_stats *src) +{ + /* In a sense, this is non-atomic version of stats_add_result(). */ + if (src->playouts) { + dest->playouts += src->playouts; + dest->value += (src->value - dest->value) * src->playouts / dest->playouts; + } +} + +static inline void +stats_reverse_parity(struct move_stats *s) +{ + s->value = 1 - s->value; +} + +#endif diff --git a/jni/pachi/stone.c b/jni/pachi/stone.c new file mode 100644 index 0000000..96a45c1 --- /dev/null +++ b/jni/pachi/stone.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "stone.h" + +char * +stone2str(enum stone s) +{ + switch (s) { + case S_BLACK: return "black"; + case S_WHITE: return "white"; + default: return "none"; + } +} + +enum stone +str2stone(char *str) +{ + switch (tolower(*str)) { + case 'b': return S_BLACK; + case 'w': return S_WHITE; + default: return S_NONE; + } +} diff --git a/jni/pachi/stone.h b/jni/pachi/stone.h new file mode 100644 index 0000000..4afb3dc --- /dev/null +++ b/jni/pachi/stone.h @@ -0,0 +1,47 @@ +#ifndef PACHI_STONE_H +#define PACHI_STONE_H + +enum stone { + S_NONE, + S_BLACK, + S_WHITE, + S_OFFBOARD, + S_MAX, +}; + +static char stone2char(enum stone s); +static enum stone char2stone(char s); +char *stone2str(enum stone s); /* static string */ +enum stone str2stone(char *str); + +static enum stone stone_other(enum stone s); + + +static inline char +stone2char(enum stone s) +{ + return ".XO#"[s]; +} + +static inline enum stone +char2stone(char s) +{ + switch (s) { + case '.': return S_NONE; + case 'X': return S_BLACK; + case 'O': return S_WHITE; + case '#': return S_OFFBOARD; + } + return S_NONE; // XXX +} + +/* Curiously, gcc is reluctant to inline this; I have cofirmed + * there is performance benefit. */ +static inline enum stone __attribute__((always_inline)) +stone_other(enum stone s) +{ + static const enum stone o[S_MAX] = { S_NONE, S_WHITE, S_BLACK, S_OFFBOARD }; + return o[s]; +} + +#endif diff --git a/jni/pachi/t-play/TESTS b/jni/pachi/t-play/TESTS new file mode 100644 index 0000000..b9da85c --- /dev/null +++ b/jni/pachi/t-play/TESTS @@ -0,0 +1,622 @@ +Default settings, 5000 playouts. +Opponent: gnugo --mode gtp --chinese-rules --capture-all-dead --level 10 +GNUGo 3.7.12 + +gnugo 19x19 l1 vs l10 k 7.5: 29% (+-2.3%) +gnugo 19x19 l1 vs l10 k 0.5: 42.2% (+-2.5%) +gnugo 19x19 l1 vs l10 k -7.5: 52.5% (+-2.5%) +gnugo 19x19 l1 vs l10 k -14.5: 62.2% (+-2.4%) +gnugo 19x19 l1 vs l10 k -21.5: 71.5% (+-2.3%) +gnugo 19x19 l1 vs l10 k -28.5: 78.2% (+-2.1%) +gnugo 19x19 l1 vs l10 handi 2: 54.7% (+-2.1%) +gnugo 19x19 l1 vs l10 handi 3: 71.9% (+-2.2%) +gnugo 19x19 l1 vs l10 handi 4: 82.6% (+-1.9%) +gnugo 19x19 l1 vs l10 handi 5: 88.9% (+-1.6%) + + +Unfortunately, some early measurements use 95% confidence interval; all newer +ones use the standard error (/1.96) instead, since that's what gogui-twogtp +provides. + + +Template: +../../gogui-1.1.10/bin/gogui-twogtp -black 'gnugo --mode gtp --chinese-rules --capture-all-dead --level 10' -white './zzgo-77d2d games=5000,policy=ucb1amaf,playout=moggy' -alternate -auto -verbose -size 9 -komi 7.5 -games 100 -sgffile f1-77d2d +for i in *.dat; do ../../gogui-1.1.10/bin/gogui-twogtp -force -analyze $i; done +./test_in_context.sh 9 + +ead8e ucb1amaf 16.5% (+-4.5%) + +b2185 ucb1amaf 50% (+-6%) + +75fff ucb1amaf 49% (+-4%) + +c5bea ucb1amaf 42.5% (+-2.5%) + +95bf3 ucb1amaf 25.2% (+-4%) + +c7249 ucb1amaf 34.2% (+-4.8%) + +9367d ucb1amaf 37% (+-8%) + +ce540 ucb1amaf* 42% (+-2%) selfatarirate=0 +ce540 ucb1amaf 43% (+-2%) + +62592 ucb1amaf* 47% (+-2.5%) selfatarirate=0 +62592 ucb1amaf 44% (+-2.5%) +62592 ucb1amaf*# 45.2% (+-2.5%) selfatarirate=0,rave_prior +62592 ucb1amaf# 47.5% (+-2.5%) rave_prior + +1a1eb ucb1 15% (+-2%) selfatarirate=0:capturerate=0:lcapturerate=0 + +6e22f ucb1amaf 49.5% (+-2.5%) +6e22f ucb1amaf*0 48% (+-2.5%) rave_equiv=6000 +6e22f ucb1amaf*1 49% (+-2.5%) rave_equiv=500 +6e22f ucb1amaf# 43.2% (+-2.5%) explore_p=0 +6e22f ucb1amaf#*0 45.8% (+-2.5%) explore_p=0,rave_equiv=6000 +6e22f ucb1amaf#*1 48.8% (+-2.5%) explore_p=0,rave_equiv=500 +6e22f ucb1amaf#0 49.5% (+-2.5%) explore_p=0.01 +6e22f ucb1amaf#1 24.8% (+-2.5%) explore_p=0.5 +6e22f 19x19 ucb1amaf 0% +6e22f 19x19 ucb1amaf* 0% explore_p=0.01,rave_equiv=100 + +88fe2 ucb1amaf 50.8% (+-2.5%) +88fe2 ucb1amaf* 49.5% (+-2.5%) selfatarirate=0 + +04b7b ucb1amaf 60.5% (+-2.4%) +04b7b ucb1amaf* 60.2% (+-2.4%) selfatarirate=0 +04b7b ucb1amaf%1 58.5% (+-2.5%) rate=75 +04b7b ucb1amaf%2 60.8% (+-2.4%) rate=100 +04b7b ucb1amaf# 54.5% (+-2.5%) explore_p=0 +04b7b ucb1amaf^ 63.5% (+-2.4%) threads=2 +04b7b ucb1amaf+ 71.2% (+-2.3%) games=10000 +04b7b ucb1amaf! 79.5% (+-2%) playout_amaf +04b7b ucb1amaf!& 46% (+-2.5%) playout_amaf,patternrate=0 + +82790 ucb1amaf 54.1% (+-2.9%) +82790 ucb1amaf! 79% (+-2.9%) playout_amaf + +129bb ucb1amaf 64.5% (+-2.4%) +129bb ucb1amaf! 77.2% (+-2.1%) playout_amaf + + + +605dd ucb1amaf 56.2% (+-2.5%) +605dd ucb1amaf! 63.5% (+-2.4%) playout_amaf + + + +c031a ucb1amaf 52.5% (+-4.2%) +c031a ucb1amaf! 63.4% (+-4.2%) playout_amaf + +87b9e ucb1amaf 53.7% (+-2.1%) +87b9e ucb1amaf#1 59.5% (+-1.7%) explore_p_rave=0 +87b9e ucb1amaf#2 59.1% (+-1.7%) explore_p=0.01,explore_p_rave=0.01 +87b9e ucb1amaf% 56.6% (+-2%) local_assess +87b9e ucb1amaf* 19% (+-1.3%) patternrate=0 +87b9e ucb1amaf!$ 62.8% (+-2.4%) playout_amaf,rave_equiv=500 +87b9e ucb1amaf!#1 62.8% (+-1.7%) playout_amaf,explore_p=0.01,explore_p_rave=0.01 +87b9e ucb1amaf!#2 61.6% (+-1.7%) playout_amaf,explore_p_rave=0.01 +87b9e ucb1amaf!& 61.8% (+-2.4%) playout_amaf,eqex=25 +87b9e ucb1amaf! 61.4% (+-2.1%) playout_amaf + + + +093e7 ucb1amaf 60.5% (+-2.4%) +093e7 ucb1amaf! 58.8% (+-2.5%) plaout_amaf=0 +093e7 ucb1amaf% 60.8% (+-2.4%) ladders=0 + + + +635a3 ucb1amaf 65% (+-2.4%) +635a3 ucb1amaf 63% (+-2.4%) explore_p_rave=0.01 + + + +eac23 ucb1amaf 77.4% (+-1.5%) +eac23 ucb1amaf# 76.6% (+-1.5%) explore_p_rave=0.01 +eac23 ucb1amaf! 74.3% (+-1.5%) check_nakade=0 +eac23 ucb1amaf!# 75.5% (+-1.6%) check_nakade=0,explore_p_rave=0.01 +eac23 ucb1amaf!#* 75% (+-1.5%) check_nakade=0,explore_p_rave=0.01,explore_p=0.01 + + + +229b9 ucb1amaf 2k 65.5% (+-2.4%) +229b9 ucb1amaf 5k 77.5% (+-2.1%) +229b9 ucb1amaf 20k 83.8% (+-1.7%) +229b9 ucb1amaf 50k 90% (+-1.5%) +229b9 ucb1amaf! 2k 37.8% (+-2.4%) playout_amaf=0 +229b9 ucb1amaf! 5k 59% (+-2.5%) playout_amaf=0 +229b9 ucb1amaf! 20k 79% (+-2%) playout_amaf=0 +229b9 ucb1amaf! 50k 83.5% (+-1.9%) playout_amaf=0 +229b9 ucb1amaf% 5k 77.2% (+-2.1%) ladders=0 +229b9 ucb1amaf*# 5k 74.9% (+-2.2%) explore_p=0.005,explore_p_rave=0.005 + +self-play: +229b9 ucb1amaf km5.5 B+57.5% (+-2.5%) +229b9 ucb1amaf km7.5 B+48.8% (+-2.5%) + +2d54a ucb1amaf 72.2% (+-2.2%) + +8ac8a ucb1amaf 77% (+-2.1%) +8ac8a ucb1amaf@ 77% (+-2.1%) ladder catcher atari check disabled +8ac8a ucb1amaf 50k 93.5% (+-1.2%) + + +fccfe ucb1amaf 78% (+-2.1%) +fccfe ucb1amaf# 78.5% (+-2.1%) rave_prior=0 +fccfe ucb1amaf*0 76% (+-2.1%) playout_amaf_cutoff=50 +fccfe ucb1amaf*0! 78.5% (+-2.1%) playout_amaf_cutoff=50,playout_amaf_nakade=1 +fccfe ucb1amaf*1 79.8% (+-2%) playout_amaf_cutoff=75 +fccfe ucb1amaf*1! 74% (+-2.2%) playout_amaf_cutoff=75,playout_amaf_nakade=1 +fccfe ucb1amaf*2 76.7% (+-2.1%) playout_amaf_cutoff=85 +fccfe ucb1amaf*2! 77% (+-2.1%) playout_amaf_cutoff=85,playout_amaf_nakade=1 +fccfe ucb1amaf*3 72% (+-2.2%) playout_amaf_cutoff=90 +fccfe ucb1amaf*3! 73% (+-2.2%) playout_amaf_cutoff=90,playout_amaf_nakade=1 +fccfe ucb1amaf*3 72% (+-2.2%) playout_amaf_cutoff=90 +fccfe ucb1amaf*3! 73% (+-2.2%) playout_amaf_cutoff=90,playout_amaf_nakade=1 + +Threading test: +fccfe ucb1amaf 78% (+-2.1%) +fccfe ucb1amaf 10k 79.8% (+-2%) +fccfe ucb1amaf t=2 75.5% (+-2.2%) +569d7 ucb1amaf t=2 82.9% (+-1.9%) + + + + +Fillboard investigations (only 4-lib fillboard yet): + +569d7 9ucb1amaf! 81.2% (+-2%) fillboardtries=10 +569d7 9ucb1amaf 50k 90% (+-1.5%) +569d7 9ucb1amaf! 50k 90.8% (+-1.4%) fillboardtries=10 + +ce382 13ucb1amaf 51.8% (+-2.5%) +ce382 13ucb1amaf 50k 79.6% (+-2.6%) +569d7 13ucb1amaf! 47% (+-2.5%) fillboardtries=10 +569d7 13ucb1amaf! 50k 82.8% (+-2.4%) fillboardtries=10 + + + + +4181b 9ucb1amaf 76% (+-2.4%) +4181b 13ucb1amaf 47.8% (+-2.5%) +4181b 19ucb1amaf 1.6% (+-0.9%) +4181b 19ucb1amaf! 1% (+-0.7%) fillboardtries=10 + + +0965e 9ucb1amaf 77.5% (+-2.1%) +0965e 9ucb1amaf# 9.5% (+-1.5%) sylvain_rave=0 +0965e 9ucb1amaf& 79% (+-2.1%) self-atari protection turned off for single-stone groups + +0965e 13ucb1amaf 45.2% (+-2.5%) +0965e 13ucb1amaf& 52.2% (+-2.5%) self-atari protection turned off for single-stone groups + + + +234b3 9ucb1amaf 77.2% (+-2.1%) +234b3 13ucb1amaf 47% (+-2.5%) + + + + +035fc 9ucb1amaf 70.5% (+-2.3%) +035fc 9ucb1amaf# 79.8% (+-2%) pattern2=0 + + + +74b71 9ucb1amaf 74.2% (+-2.2%) +74b71 9ucb1amaf# 77% (+-2.1%) pattern2=0 +74b71 13ucb1amaf 46.8% (+-2.5%) +74b71 13ucb1amaf# 50.2% (+-2.5%) pattern2=0 + +e34f9 9ucb1amaf 74.2% (+-2.2%) +e34f9 9ucb1amaf# 74% (+-2.2%) pattern2=0 +e34f9 9ucb1amaf* 76% (+-2.1%) atarirate=0 +e34f9 9ucb1amaf#* 79% (+-2%) pattern2=0,atarirate=0 +e34f9 13ucb1amaf 47.2% (+-2.5%) +e34f9 13ucb1amaf# 49.7% (+-2.8%) pattern2=0 +e34f9 13ucb1amaf#* 52.2% (+-2.5%) pattern2=0,atarirate=0 + +ba2e1 9ucb1amaf 78.2% (+-2.1%) +ba2e1 9ucb1amaf# 81.8% (+-1.9%) pattern2=0 +ba2e1 9ucb1amaf* 74.5% (+-2.2%) atarirate=0 +ba2e1 9ucb1amaf#* 75.5% (+-2.2%) pattern2=0,atarirate=0 +ba2e1 9ucb1amaf#*+ 78.5% (+-2.1%) pattern2=0,atarirate=0,prior_cfgd=6 +ba2e1 9ucb1amaf+% 81.2% (+-2%) prior_cfgd=6,prior_b19=6 +ba2e1 9ucb1amaf#+% 83% (+-1.9%) pattern2=0,prior_cfgd=6,prior_b19=6 + +ba2e1 13ucb1amaf+ 57% (+-2.5%) prior_cfgd=6 +ba2e1 13ucb1amaf+% 51.2% (+-2.5%) prior_cfgd=6,prior_b19=6 +ba2e1 13ucb1amaf#*+ 54.8% (+-2.5%) pattern2=0,atarirate=0,prior_cfgd=6 + +ba2e1 19ucb1amaf+! 13.8% (+-1.7%) prior_cfgd=6,fillboard +ba2e1 19ucb1amaf+%2 21.2% (+-2%) prior_cfgd=prior_b19=prior=20 +ba2e1 19ucb1amaf+%2! 20.5% (+-2%) prior_cfgd=prior_b19=prior=20,fillboard +ba2e1 19ucb1amaf+%3 20.8% (+-2%) prior_cfgd=prior_b19=prior=30 +ba2e1 19ucb1amaf+%4 25.8% (+-2.2%) prior_cfgd=prior_b19=prior=40 +ba2e1 19ucb1amaf+%5 23.2% (+-2.1%) prior_cfgd=prior_b19=prior=50 +ba2e1 19ucb1amaf+%4 24.5% (+-2.2%) prior_cfgd=prior_b19=prior=40,amaf_prior=0 +ba2e1 19ucb1amaf#*+%! 9.8% (+-1.5%) pattern2=0,atarirate=0,prior_cfgd=prior_b19=6,fillboard +ba2e1 19ucb1amaf#*+%2 25% (+-2.2%) pattern2=0,atarirate=0,prior_cfgd=prior_b19=prior=20 +ba2e1 19ucb1amaf#+%2 26.5% (+-2.2%) pattern2=0,prior_cfgd=prior_b19=prior=20 +ba2e1 19ucb1amaf#+%3 24% (+-2.2%) pattern2=0,prior_cfgd=prior_b19=prior=30 +ba2e1 19ucb1amaf#+%4 27% (+-2.2%) pattern2=0,prior_cfgd=prior_b19=prior=40 +ba2e1 19ucb1amaf#+%2! 22.5% (+-2.1%) pattern2=0,prior_cfgd=prior_b19=prior=20,fillboard +ba2e1 19ucb1amaf#+%2_ 23.5% (+-2.1%) pattern2=0,prior_cfgd=prior_b19=prior=20,prior_policy=10 +ba2e1 19ucb1amaf#+%2a 1% (+-0.7%) pattern2=0,prior_cfgd=prior_b19=prior=20,playouts=1000 +ba2e1 19ucb1amaf#+%2b 7.2% (+-1.4%) pattern2=0,prior_cfgd=prior_b19=prior=20,playouts=2000 +ba2e1 19ucb1amaf#+%2 28.2% (+-2.3%) pattern2=0,prior_cfgd=prior_b19=prior=20,amaf_prior=0 + + + + +49208 9ucb1amaf 80% (+-2%) +49208 9ucb1amaf& 76.8% (+-2.1%) selfatarirate=0 +49208 9ucb1amaf_ 76.8% (+-2.1%) can_capture_group() always true (really same rate) +49208 19ucb1amaf% 25.4% (+-2.3%) +49208 19ucb1amaf%& 18.8% (+-2%) selfatarirate=0 +49208 19ucb1amaf%_ 19.5% (+-2%) can_capture_group() always true (really same rate) + +aef5c 19ucb1amaf% 24.5% (+-2.4%) + +67561 19ucb1amaf% 29% (+-2.3%) +67561 19ucb1amaf%*2 26.7% (+-2.7%) explore_p=0.02 + +d11b1 9ucb1amaf 81.8% (+-1.9%) +d11b1 9ucb1amaf_ 81.5% (+-1.9%) investigate all nakade neis only in case of snapback (945e5) +d11b1 9ucb1amaf* 81.2% (+-2%) atarirate=0 +d11b1 9ucb1amaf& 78.8% (+-2.1%) explore_p_rave=0 +d11b1 19ucb1amaf% 24% (+-2%) + +f1c52 19ucb1amaf% 26.2% (+-2.4%) +08c0a 19ucb1amaf% 25.5% (+-2.4%) + + +a0f85 9ucb1amaf 80.2% (+-2%) +a0f85 9ucb1amaf*1 80.8% (+-2%) explore_p=0 +a0f85 9ucb1amaf*2 81.2% (+-2%) explore_p=0.01 +a0f85 9ucb1amaf*3 40.2% (+-2.5%) explore_p=0.6 +a0f85 19ucb1amaf% 18.8% (+-2%) + + + +5524c 9ucb1amaf 84.2% (+-1.8%) +5524c 9ucb1amaf^1 70.2% (+-3%) fuego cfgd +5524c 9ucb1amaf^2 77% (+-2.1%) fuego cfgd, 0.1 for pass +5524c 9ucb1amaf^2* 76.5% (+-2.1%) fuego cfgd, 0.1 for pass, prior_cfgd=4 +5524c 9ucb1amaf! 81.2% (+-2%) prior_even is 0.1 for pass + +5524c 19ucb1amaf% 19.8% (+-2%) +5524c 19ucb1amaf%^1 13.2% (+-1.7%) fuego cfgd +5524c 19ucb1amaf%^2 12.5% (+-1.7%) fuego cfgd, 0.1 for pass +5524c 19ucb1amaf%^2* 16.2% (+-1.8%) fuego cfgd, 0.1 for pass, prior_cfgd=5 +5524c 19ucb1amaf! 19.6% (+-2%) prior_even is 0.3 for pass + +2151a 9ucb1amaf 81.5% (+-1.9%) +443e1 9ucb1amaf_1 72.5% (+-2.2%) Priors just like in Fuego +6c6cb 9ucb1amaf_2 70.5% (+-2.3%) Priors just like in Fuego, including equivalent-best-move + +2151a 19ucb1amaf 17.8% (+-1.9%) +443e1 19ucb1amaf_1 12.2% (+-1.6%) Priors just like in Fuego +6c6cb 19ucb1amaf_2 19.5% (+-2%) Priors just like in Fuego, including equivalent-best-move + +03c36 19ucb1amaf%$ 17.2% (+-1.9%) ko=-1, without ko aging yet + +09368 9ucb1amaf 80.2% (+-2%) +09368 9ucb1amaf$ 80% (+-2%) ko=-1 +09368 9ucb1amaf^ 79% (+-2%) eye=0 +09368 9ucb1amaf$^ 77.2% (+-2.1%) ko=-1,eye=0 + +a1353 19u1a%* 61.8% (+-3.2%) 10k +a1353 19u1a%^1 k-14.5 29.2% (+-2.3%) dynkomi=150 +a1353 19u1a% k-14.5 29% (+-2.3%) +a1353 19u1a%^1 k-7.5 30% (+-2.3%) dynkomi=150 +a1353 19u1a% k-7.5 26.5% (+-2.2%) +a1353 19u1a%*^1 k0.5 41.6% (+-3%) 10k, dynkomi=150 +a1353 19u1a%* k0.5 45.1% (+-3%) 10k +a1353 19u1a% k0.5 24% (+-2.1%) +a1353 19u1a%^0 k0.5 25.8% (+-2.2%) dynkomi=100 +a1353 19u1a%^1 k0.5 24.2% (+-2.1%) dynkomi=150 +a1353 19u1a%^2 k0.5 25% (+-2.2%) dynkomi=200 + +a1267 9ucb1amaf 80.1% (+-2%) +a1267 9ucb1amaf~20 20.3% (+-2%) val_scale=0.15,val_points=10 +a1267 9ucb1amaf~21 6% (+-1.2%) val_scale=0.15,val_points=20 +a1267 9ucb1amaf~22 2.5% (+-0.8%) val_scale=0.15,val_points=30 +a1267 9ucb1amaf~00 20.2% (+-1.2%) val_scale=0.05,val_points=10 +a1267 9ucb1amaf~11 5.8% (+-1.2%) val_scale=0.10,val_points=20 +a1267 9ucb1amaf~31 6.5% (+-2%) val_scale=0.20,val_points=20 +a1267 9ucb1amaf~a0 18% (+-1.9%) val_scale=0.01,val_points=10 +a1267 9ucb1amaf~a1 3.5% (+-0.9%) val_scale=0.01,val_points=20 +a1267 9ucb1amaf~b1 6.8% (+-1.3%) val_scale=0.02,val_points=20 +a1267 9ucb1amaf~bx 2.3% (+-0.7$) val_scale=0.02,val_points=80 +a1267 19ucb1amaf% 21.8% (+-2.1%) + +2b830 9ucb1amaf 73% (+-2.2%) +2b830 9ucb1amaf_ 79% (+-2%) global atari check picks moves across all atari groups +2b830 9ucb1amaf~b1 3.8% (+-1%) val_scale=0.02,val_points=20 +2b830 19ucb1amaf% 20.7% (+-3%) +2b830 19ucb1amaf%_ 20.1% (+-2.4%) global group atari check picks move across all groups + + + +cc80e 9ucb1amaf 76.8% (+-2.1%) +cc80e 19ucb1amaf%_ 18.8% (+-2.7%) 8 neighbors are examined instead of 4 in local atari/2lib check + +e2a49 9ucb1amaf 81.2% (+-2%) +e2a49 9ucb1amaf_ 75.8% (+-2.1%) liberal 2-liberty check (take even liberty where opponent can't play) +e2a49 9ucb1amaf 2k 71.2% (+-2.3%) +e2a49 9ucb1amaf 3k 65.5% (+-2.4%) +e2a49 19ucb1amaf% 25.3% (+-2.1%) ??? suspicious bump in percentage +e2a49 19ucb1amaf%*3 25.4% (+-3.7%) explore_p=0 + +f3603 9ucb1amaf 75.8% (+-2.1%) +f3603 9ucb1amaf~b1 7% (+-1.3%) val_scale=0.02,val_points=20 +f3603 19ucb1amaf% 19.4% (+-2.1%) + + + +fba31 9ucb1amaf 40k 94.2% (+-1.2%) +fba31 9ucb1amaf~b1 78% (+-2.1%) val_scale=0.02,val_points=20 +fba31 9ucb1amaf~ax 80.8% (+-2%) val_scale=0.01,val_points=80 +fba31 19ucb1amaf% 20.7% (+-2.2%) +fba31 19ucb1amaf%~a0 20.9% (+-2.2%) val_scale=0.01,val_points=10 +fba31 19ucb1amaf%~ax 23.2% (+-2.1%) val_scale=0.01,val_points=80 +fba31 19ucb1amaf%~b1 22.3% (+-2.2%) val_scale=0.02,val_points=20 + +3d0d9 19k-14.5 a% 29.5% (+-2.3%) +3d0d9 19k-14.5 a%^0 29.9% (+-2.5%) dynkomi=100 +3d0d9 19k-14.5 a%^1 37% (+-2.4%) dynkomi=150 +3d0d9 19k-14.5 a%^2 38.6% (+-2.6%) dynkomi=200 +3d0d9 19k-14.5 a%^3 34.7% (+-2.5%) dynkomi=250 + +c40e2 19h2 u1a% 36.2% (+-2.4%) +c40e2 19h3 u1a% 43% (+-2.5%) +c40e2 19h4 u1a% 50.7% (+-2.9%) + +c035e 9ucb1amaf 77.5% (+-2.1%) +1d4f8 9ucb1amaf 77% (+-2.1%) +1d4f8 9ucb1amaf*1 84% (+-1.8%) explore_p=0.02 +1d4f8 9ucb1amaf*3 84.5% (+-1.8%) explore_p=0.002 +1d4f8 9ucb1amaf%*1 81.8% (+-1.9%) prior=eqex=20,explore_p=0.02 +1d4f8 9ucb1amaf%*2 81.8% (+-1.9%) prior=eqex=20,explore_p=0.002 +1d4f8 9ucb1amaf%*3 85.2% (+-1.9%) prior=eqex=20,explore_p=0 +1d4f8 9km0 u1a 60.2% (+-2.5%) +1d4f8 9km0 u1a*1 61.2% (+-2.4%) explore_p=0.02 +1d4f8 9km0 u1a*3 61% (+-2.4%) explore_p=0 +1d4f8 9km0 u1a%*1 65% (+-2.4%) prior=eqex=20,explore_p=0.02 +1d4f8 9km0 u1a%*3 65.2% (+-2.4%) prior=eqex=20,explore_p=0 + +1d4f8 19u1a% 17.4% (+-2.3%) +1d4f8 19u1a%2 21.4% (+-3.7%) prior=eqex=40 +1d4f8 19u1a%*x 0% (+-0%) explore_p=0.6 +1d4f8 19u1a%*0 13% (+-1.7%) explore_p=0.2 +1d4f8 19u1a%*1 23.4% (+-2.4%) explore_p=0.02 +1d4f8 19u1a%*2 25% (+-2.4%) explore_p=0.002 +1d4f8 19u1a%*3 22.4% (+-2.9%) explore_p=0 +1d4f8 19u1a%2*3 29.4% (+-3.2%) explore_p=0,prior=eqex=40 +1d4f8-67561 19u1a% 10.6% (+-2.3%) 1d4f8 perspective - something deeply rotten, eh? ;) +c035e-67561 19u1a%*1 44.1% (+-2.6%) explore_p=0.02, c035e perspective +1d4f8-67561 19u1a%*1 41.7% (+-2.9%) explore_p=0.02, 1d4f8 perspective - better than old exp., but still regression + +7b125 19u1a% 15.7% (+-2.4%) +16ac5 19u1a% 15.3% (+-4.7%) + +More dynkomi experiments: +1d4f8 19h2ua%*1~ax 31.8% (+-2.5%) explore_p=0.02,val_scale=0.01,val_points=80 +1d4f8 19h2ua%*1~ax^2 38.4% (+-2.6%) explore_p=0.02,val_scale=0.01,val_points=80,dynkomi=200 +1d4f8 19h4ua%*1 49.2% (+-2.5%) explore_p=0.02 +1d4f8 19h4ua%*1^1 59.5% (+-2.5%) explore_p=0.02,dynkomi=150 +1d4f8 19h4ua%*1^2 63.8% (+-2.4%) explore_p=0.02,dynkomi=200 +1d4f8 19h4ua%*1~a1 52.2% (+-2.5%) explore_p=0.02,val_scale=0.01,val_points=20 +1d4f8 19h4ua%*1~a1^1 58.5% (+-2.5%) explore_p=0.02,val_scale=0.01,val_points=20,dynkomi=150 +1d4f8 19h4ua%*1~ax 51.5% (+-2.5%) explore_p=0.02,val_scale=0.01,val_points=80 +1d4f8 19h4ua%*1~ax^1 58.7% (+-2.6%) explore_p=0.02,val_scale=0.01,val_points=80,dynkomi=150 +1d4f8 19h4ua%*1~ax^2 63.5% (+-2.4%) explore_p=0.02,val_scale=0.01,val_points=80,dynkomi=200 +1d4f8 19h4ua%2*3~ax^2 66.8% (+-2.4%) explore_p=0,prior=eqex=40,val_scale=0.01,val_points=80,dynkomi=200 + +2e4fa 19u1a%*1 30.1% (+-2.3%) explore_p=0.02 + + + +d032a 9u1a 82.2% (+-1.9%) +d032a 9u1a%1 85.2% (+-1.8%) prior=eqex=20 +d032a 9k0u1a 66% (+-2.4%) +d032a 9k0u1a@ 62.2% (+-2.4%) prior=ko=-1 +d032a 9k0u1a%1 64.8% (+-2.4%) prior=eqex=20 +d032a 9k0u1a~a1 59.5% (+-2.5%) val_scale=0.01,val_points=20 +d032a 9k0u1a~ax 67.8% (+-2.3%) val_scale=0.01,val_points=80 +d032a 9k0u1a~b1 65.8% (+-2.4%) val_scale=0.02,val_points=20 +d032a 9k0u1a~bx 65.5% (+-2.4%) val_scale=0.02,val_points=80 +d032a 9k0u1a*1 64.2% (+-2.4%) explore_p=0.02 +d032a 9k0u1a*1%1 67% (+-2.4%) explore_p=0.02,prior=eqex=20 +d032a 9k0u1a^10 62.8% (+-2.4%) dynkomi=10 +d032a 9k0u1a^20 64% (+-2.4%) dynkomi=20 +d032a 9k0u1a^40 50.5% (+-2.5%) dynkomi=40 +d032a 9k0u1a^60 25.3% (+-2.5%) dynkomi=60 +d032a 19u1a 33% (+-3.2%) +d032a 19u1a@ 33.5% (+-3.3%) prior=ko=-1 +d032a 19u1a*1%1 30% (+-3.1%) explore_p=0.02,prior=eqex=20 +d032a 19u1a~a20 33% (+-2.8%) val_scale=0.01,val_points=20 +d032a 19u1a~a80 31.2% (+-3.2%) val_scale=0.01,val_points=80 +d032a 19u1a~a150 34.5% (+-2.8%) val_scale=0.01,val_points=150 +d032a 19u1a~a360 33.2% (+-2.8%) val_scale=0.01,val_points=360 + + + +8ce82 9u1a 85.2% (+-1.8%) +8ce82 9k0u1a 69.8% (+-2.3%) +8ce82 9u1a$ 89% (+-1.6%) games=6666 +8ce82 9k0u1a$ 72.8% (+-2.2%) games=6666 +8ce82 19u1a 36.8% (+-2.4%) +8ce82 19u1a@ 34.5% (+-2.4%) prior=ko=-1 +8ce82 19u1a~a361 36.8% (+-2.4%) val_scale=0.01,val_points=361 +8ce82 19u1a~b361 36.5% (+-2.4%) val_scale=0.02,val_points=361 + +<#define NO_DOOMED_GROUPS; this also pertains a speedup, + so we test with original 5k and 6.6k playouts (appropriate + for measured speedup on 19x19)> + +f4cb0 9u1a 83.2% (+-1.9%) +f4cb0 9u1a$ 87.5% (+-1.7%) games=6666 +f4cb0 9k0u1a 64% (+-2.4%) +f4cb0 9k0u1a$ 66.2% (+-2.4%) games=6666 +f4cb0 19u1a 32.5% (+-2.3%) +f4cb0 19u1a~b361 31.8% (+-2.3%) val_scale=0.02,val_points=361 +f4cb0 19u1a$ 42% (+-2.5%) games=6666 +f4cb0 19u1a$~b361 42.5% (+-2.5%) games=6666,val_scale=0.02,val_points=361 + + + +7d1c7 9k0u1a 63.5% (+-2.4%) +7d1c7 9k0u1a$ 68.2% (+-2.3%) games=6666 +7d1c7 9k0u1a~x 61.8% (+-2.4%) val_extra +7d1c7 9k0u1a~10 68.2% (+-2.3%) val_points=10 +7d1c7 9k0u1a~20 71.5% (+-2.3%) val_points=20 +7d1c7 9k0u1a^ 63.8% (+-1.7%) ladders=0 +7d1c7 9k0u1a+ 61.6% (+-1.7%) pattern2=1 + +7d1c7 19u1a 30.5% (+-2.3%) +7d1c7 19u1a$ 40.2% (+-2.2%) games=6666 +7d1c7 19u1a~x 31.2% (+-2.3%) val_extra +7d1c7 19u1a~10 30.2% (+-2.3%) val_points=10 +7d1c7 19u1a~20 30.8% (+-2.3%) val_points=20 +7d1c7 19u1a@ 31.2% (+-2.3%) prior=ko=-1 + +Playout randomization experiments: +7d1c7 9k0u1a%95 64% (+-1.8%) rate=95 +7d1c7 9k0u1a%80 65.8% (+-1.8%) rate=80 +7d1c7 9k0u1a%70 67.1% (+-1.7%) rate=70 +7d1c7 9k0u1a%65 68.5% (+-1.4%) rate=65 +7d1c7 9k0u1a%60 66.4% (+-1.7%) rate=60 +7d1c7 9k0u1a%55 65.8% (+-1.7%) rate=55 +7d1c7 9k0u1a%50 63% (+-1.7%) rate=50 +7d1c7 9k0u1a%45 57.6% (+-1.7%) rate=45 +7d1c7 9k0u1a%65#80 64.7% (+-1.7%) rate=65,patternrate=80 +7d1c7 9k0u1a%65&80 68.1% (+-1.6%) rate=65,selfatarirate=80 +7d1c7 9k0u1a%65&95 64% (+-1.7%) rate=65,selfatarirate=95 +7d1c7 9k0u1a%90!50 66.4% (+-1.7%) rate=90,atarirate=50 +7d1c7 9k0u1a%90=50 64.2% (+-1.7%) rate=90,capturerate=50 +7d1c7 19u1a%99 32.2% (+-2.4%) rate=99 +7d1c7 19u1a%95 32.5% (+-2.3%) rate=95 +7d1c7 19u1a%85 33% (+-2.4%) rate=85 +7d1c7 19u1a%80 25.8% (+-2.1%) rate=80 +7d1c7 19u1a%65 22.2% (+-2.2%) rate=65 + +Even game heuristics experiments: + +7d1c7 9k0u1a! 68.2% (+-1.6%) amaf_prior=0 +7d1c7 9k0u1a!~10 65.8% (+-1.7%) amaf_prior=0,prior=even=10 +7d1c7 9k0u1a/ 55.7% (+-1.8%) +7d1c7 9k0u1a/! 57% (+-1.8%) amaf_prior=0 +7d1c7 9k0u1a/!~0 8.2% (+-1.4%) amaf_prior=0,prior=even=0 +7d1c7 9k0u1a/!~2 66% (+-1.7%) amaf_prior=0,prior=even=2 +7d1c7 9k0u1a/!~4 63.6% (+-1.7%) amaf_prior=0,prior=even=4 +7d1c7 9k0u1a/!~6 61.8% (+-1.7%) amaf_prior=0,prior=even=6 +7d1c7 9k0u1a/!~10 65.4% (+-1.7%) amaf_prior=0,prior=even=10 + +7d1c7 19u1a!~10 28.8% (+-2.3%) amaf_prior=0,prior=even=10 +7d1c7 19u1a/ 13.5% (+-1.7%) +7d1c7 19u1a/! 17.2% (+-1.9%) amaf_prior=0 +7d1c7 19u1a/!~0 0% amaf_prior=0,prior=even=0 +7d1c7 19u1a/!~2 31% (+-2.3%) amaf_prior=0,prior=even=2 +7d1c7 19u1a/!~4 32.8% (+-2.3%) amaf_prior=0,prior=even=4 +7d1c7 19u1a/!~6 32.8% (+-2.3%) amaf_prior=0,prior=even=6 +7d1c7 19u1a/!~10 33.8% (+-2.4%) amaf_prior=0,prior=even=10 +7d1c7 19u1a/!~16 23.5% (+-2.1%) amaf_prior=0,prior=even=16 + +More dynkomi experiments: +7d1c7 19k0u1a 36.2% (+-2.4%) +7d1c7 19k0u1a^ 40.8% (+-2.5%) dynkomi=200 extra komi is 7.5 +7d1c7 19k0u1a^_1 40.2% (+-2.5%) dynkomi=200 extra komi is 5.5 +7d1c7 19k0u1a^_2 40% (+-2.4%) dynkomi=200 extra komi is 8 + +7d1c7 19h2u1a 46.2% (+-2.5%) +7d1c7 19h2u1a^ 50.8% (+-2.5%) dynkomi=200 extra komi is 15 +7d1c7 19h2u1a^_1 47.5% (+-2.5%) dynkomi=200 extra komi is 11.5 +7d1c7 19h2u1a^_2 50.5% (+-2.5%) dynkomi=200 extra komi is 16 + +7d1c7 19h3u1a 54% (+-2.5%) +7d1c7 19h3u1a^ 59.2% (+-2.5%) dynkomi=200 extra komi is 22.5 +7d1c7 19h3u1a^_1 61.5% (+-2.4%) dynkomi=200 extra komi is 18.5 +7d1c7 19h3u1a^_2 58.2% (+-2.5%) dynkomi=200 extra komi is 22 + +7d1c7 19h4u1a 63% (+-2.4%) +7d1c7 19h4u1a^_20 62.5% (+-2.4%) dynkomi=200 extra komi is 80 +7d1c7 19h4u1a^_16 67.5% (+-2.3%) dynkomi=200 extra komi is 64 +7d1c7 19h4u1a^_12 74.5% (+-2.2%) dynkomi=200 extra komi is 48 +7d1c7 19h4u1a^ 71.8% (+-2.4%) dynkomi=200 extra komi is 30 +7d1c7 19h4u1a^_1 67.5% (+-2.3%) dynkomi=200 extra komi is 27.5 +7d1c7 19h4u1a^_2 71.5% (+-2.3%) dynkomi=200 extra komi is 28 + +7d1c7 19h5u1a 69.3% (+-2.4%) +7d1c7 19h5u1a^_20 48.3% (+-2.5%) dynkomi=200 extra komi is 100 +7d1c7 19h5u1a^_16 77.5% (+-2.1%) dynkomi=200 extra komi is 80 +7d1c7 19h5u1a^_12 76.5% (+-2.1%) dynkomi=200 extra komi is 60 +7d1c7 19h5u1a^ 80.4% (+-2.1%) dynkomi=200 extra komi is 37.5 +7d1c7 19h5u1a^_1 81.5% (+-1.9%) dynkomi=200 extra komi is 35.5 +7d1c7 19h5u1a^_2 79.5% (+-2%) dynkomi=200 extra komi is 34 + +a9249 9k0u1a 65.5% (+-2.4%) [prior=cfgd=3%40%20%20] +a9249 9k0u1a*1 64.5% (+-2.4%) prior=cfgd=1%40 +a9249 9k0u1a*2 64.2% (+-2.4%) prior=cfgd=2%40%20 +a9249 9k0u1a*3 63.5% (+-2.4%) prior=cfgd=4%40%30%20%10 +a9249 19u1a 30.5% (+-2.3%) [prior=cfgd=3%40%20%20] +a9249 19u1a*1 23.5% (+-2.1%) prior=cfgd=1%40 +a9249 19u1a*2 25.8% (+-2.2%) prior=cfgd=2%40%20 +a9249 19u1a*3 26.5% (+-2.2%) prior=cfgd=3%40%10%10 +a9249 19u1a*4 23.2% (+-2.1%) prior=cfgd=4%40%30%20%10 +a9249 19u1a*5 23% (+-2.1%) prior=cfgd=4%40%30%30%5 +a9249 19u1a*6 25% (+-2.2%) prior=cfgd=4%40%30%30%20 + +44216 19u1a 30% (+-2.3%) +44216 19u1a+ 24% (+-2.1%) pattern2=1 +44216 19u1a%60 26% (+-2.2%) prior=eqex=60 +44216 19u1a!%10 29.8% (+-2.3%) amaf_prior=0,prior=eqex=10 +44216 19u1a!%20 25% (+-2.4%) amaf_prior=0,prior=eqex=20 +44216 19u1a!%30 32.5% (+-2.3%) amaf_prior=0,prior=eqex=30 +44216 19u1a! 36.8% (+-2.4%) amaf_prior=0,[prior=eqex=40] +44216 19u1a!%50 27.8% (+-2.2%) amaf_prior=0,prior=eqex=50 +44216 19u1a!%60 25.2% (+-2.2%) amaf_prior=0,prior=eqex=60 + +938a4 9k0u1a 65.5% (+-1.7%) +938a4 9k0u1a~0 68.8% (+-2.3%) val_points=0 +938a4 9k0u1a!%30 71.2% (+-2.3%) amaf_prior=0,prior=eqex=30 +938a4 9k0u1a! 71% (+-2.3%) amaf_prior=0,[prior=eqex=40] +938a4 9k0u1a!%50 65% (+-2.4%) amaf_prior=0,prior=eqex=50 +938a4 9k0u1a!%65 64% (+-2.4%) amaf_prior=0,prior=eqex=65 +938a4 9k0u1a#1 20.5% (+-2%) root_heuristic,root_rave=1 +938a4 9k0u1a#0.6 57.2% (+-2.5%) root_heuristic,root_rave=0.6 +938a4 9k0u1a#0.2 63.2% (+-2.4%) root_heuristic,root_rave=0.2 +938a4 9k0u1a#0.02 65.6% (+-2.4%) root_heuristic,root_rave=0.02 +a3010 19u1a 30.2% (+-2.3%) +938a4 19u1a 31.2% (+-2.3%) +938a4 19u1a~10 31.2% (+-2.3%) prior=even=10 +a3010 19u1a#1.6 23.5% (+-2.1%) root_heuristic,root_rave=1.6 +a3010 19u1a#1 26% (+-2.2%) root_heuristic,root_rave=1 +a3010 19u1a#0.6 28% (+-2.2%) root_heuristic,root_rave=0.6 +a3010 19u1a#0.2 29% (+-2.3%) root_heuristic,root_rave=0.2 +938a4 19u1a#0.05 29.2% (+-2.3%) root_heuristic,root_rave=0.05 +938a4 19u1a#0.02 27.2% (+-2.2%) root_heuristic,root_rave=0.02 +938a4 19u1a#0.005 33.8% (+-2.4%) root_heuristic,root_rave=0.005 + +4e89f 19u1 0% +4e89f 19u1a 30.5% (+-2.3%) +4e89f 19u1a#95 28.5% (+-2.3%) rate=95 +4e89f 19u1a#100/95 32.5% (+-2.3%) rate=100,selfatarirate=95 +4e89f 19u1a%02 1% (+-0.5%) explore_p=0.2 +4e89f 19u1a%002 30.5% (+-2.3%) explore_p=0.02 + +4e89f 19u1a*10 22% (+-2.1%) prior=cfgd=3%20%10%5 +4e89f 19u1a*11 25.5% (+-2.1%) prior=cfgd=3%30%10%10 +4e89f 19u1a*12 26.5% (+-2.2%) prior=cfgd=3%30%20%10 +4e89f 19u1a*13 25.5% (+-2.1%) prior=cfgd=3%30%20%20 +4e89f 19u1a 28% (+-2.2%) [prior=cfgd=3%40%20%20] +4e89f 19u1a*15 19.8% (+-2%) prior=cfgd=3%50%20%20 +4e89f 19u1a*16 21.5% (+-2.1%) prior=cfgd=3%50%30%20 +4e89f 19u1a*17 25.5% (+-2.2%) prior=cfgd=3%50%30%30 +4e89f 19u1a*18 25.2% (+-2.2%) prior=cfgd=3%50%40%30 +4e89f 19u1a*19 24.5% (+-2.2%) prior=cfgd=3%50%40%40 +4e89f 19u1a*1a 23% (+-2.1%) prior=cfgd=3%60%30%10 +4e89f 19u1a*1b 25.8% (+-2.2%) prior=cfgd=3%60%40%30 +4e89f 19u1a*1c 26.8% (+-2.2%) prior=cfgd=3%60%50%40 diff --git a/jni/pachi/t-play/autotest/README b/jni/pachi/t-play/autotest/README new file mode 100644 index 0000000..e6ea31a --- /dev/null +++ b/jni/pachi/t-play/autotest/README @@ -0,0 +1,128 @@ +This is an 'autotest' framework for distributed play testing of Go-playing +computer programs. You can run clients on as many computers as you want, +to test many combinations in parallel, then display results of the pairings +in an organized way. + +It should be easily adaptable for testing of performance of other stochastic +programs against a reference "opponent", just replace twogtp invocations +in the scripts with your own programs. + + +BASIC USAGE +----------- + +Create a networked (NFS or FUSE-sshfs) directory, copy the contents of this +directory there, and customize the rc file - it is heavily commented and +should be self-guiding. You will need the twogtp tool from gogui package. + +Then on each computer, change to this directory and run: + + nice -n 18 ./autotest-client `hostname`-1 + +where 1 should be 2, 3, 4, ... for further instances of autotest you would +run on the same machine (no two instaces with the same id must be run). +The `nice -n 18` prefix will make sure the client is being run with low +priority, in case someone wants to use the computer for some real interaction. + +Leave cooking, stir time by time. You can start and stop clients as much +as you wish on the fly, or edit the rc file to change pairings without any +need for restarting clients. + + +Time by time, you will of course want to view the results of pairings. +First, you will want to run: + + ./autotest-gather + +This will collect the results all in one place. Then, you can repeatedly +examine the results using: + + ./autotest-show + +That will show results from all pairings. You can also use e.g. + + ./autotest-show 9-* + +to show only results of 9x9 pairings. To refresh the shown results, you +will need to re-run autotest-gather; if you will always want to just see +fresh results, combine the two commands: + + ./autotest-gather; ./autotest-show + +Note that the S.D. column is standard deviation of the shown winrate; +to get a 95%-confidence interval of the winrate, roughly double the S.D. +value. E.g. WINRATE 30% S.D. 5% means that with 95% probability, the real +winrate is 30% +-10%. The meaning of the first 'S' column is: + + x errors reported by twogtp + ? unknown (not in rc) active pairing (OK if temporary) + / unknown inactive pairing + ! known (in rc) inactive pairing (last refresh > 2 hours ago) + . known active pairing (normal) + +To remove results of old pairings, use: + + ./autotest-prune pairing-name + +pairing-name can be either simply name of the pairing, or a mask matching +many pairings (up to '*' for removal of all pairing results). + +To show status of all known clients, use: + + ./autotest-clients + +(To remove a gone client, just remove its subdirectory in the c/ directory.) +The WFAIL column shows number of worker failures recorded in the log; examine +c//log for details, you can remove the log to reset the WFAIL +number. Usually, worker failures will result from interrupting a client. + + +DISCONNECTED USAGE +------------------ + +If your clients don't share a single filesystem, you can still use autotest +just fine, with two exceptions: + +* You will need to manually synchronize the 'rc' file between all your machines + +* You will need to manually copy over c/*/ directory tree from all your systems +back to the central server (to the appropriate c/*/ directories) every time +before running ./autotest-gather. + +* You will manually need to remove old pairing results on the clients. + + +IMPLEMENTATION +-------------- + +Autotest keeps all the state in plain files within the filesystem. + +./autotest-client will basically simply keep spawning `autotest-worker` +which will read the rc file, pick a random pairing and use gogui-twogtp +to play one game, then store the result in a dat file (and leave the sgf +file archived for future inspection). + +The client workers will create a directory structure like: + + c/drahokam-1/beacon # when was the client last active + c/drahokam-1/log # various events from client's life + c/drahokam-1//game.dat + c/drahokam-1//game-.sgf + +...where drahokam-1 is the ./autotest-client parameter and +is basically straightforwardly mangled version of `pairing` parameters. + + +./autotest-gather will simply go through directories of all clients and rain +down all pairing results to a single place: + + r/.dat + r/.beacon # when was the pairing last active + r/.error # number of games reporting error + +Then, ./autotest-show will call gogui-twogtp -analyze on the selected +produced dat files. + + +Copyright (c) Petr Baudis +Licenced under MIT licence (close to public domain). diff --git a/jni/pachi/t-play/autotest/TODO b/jni/pachi/t-play/autotest/TODO new file mode 100644 index 0000000..d6b1b00 --- /dev/null +++ b/jni/pachi/t-play/autotest/TODO @@ -0,0 +1,13 @@ +* Support for handicap settings + +* Support for automatic checking against loss-by-points + (desirable for UCT players) + +* Caching unchanged results - faster ./autotest-show + +* Nicer support for disconnected usage + +* Support for multi-core systems and multi-threaded players + +* Support for using different(ly optimized) binaries based + on host architecture / processor type diff --git a/jni/pachi/t-play/autotest/autotest-client b/jni/pachi/t-play/autotest/autotest-client new file mode 100644 index 0000000..a71d2de --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-client @@ -0,0 +1,27 @@ +#!/bin/sh +# This is autotest Go-testing framework client. It runs on client +# machines and spawns autotest-worker, which picks a random pairing +# among the ones defined in the rc file, and plays a single game. + +clientid="$1"; shift +if [ -z "$clientid" ]; then + echo "Usage: $0 " >&2 + echo "At any time, only single client with the same client may run!" >&2 + echo "You can use e.g. clientid \`hostname\`-1, increment 1 for" >&2 + echo "further threads started on the host." >&2 + exit 1 +fi +. ./autotest-lib + +mkdir -p "c/$clientid" +log "started client ($clientid)" + +while true; do + mkdir -p "c/$clientid" + date +%s >"c/$clientid/beacon" + if ! ./autotest-worker "$clientid"; then + echo "*** WORKER FAILED ***" + log "worker failed with error code $?" + sleep 1 + fi +done diff --git a/jni/pachi/t-play/autotest/autotest-clients b/jni/pachi/t-play/autotest/autotest-clients new file mode 100644 index 0000000..0835b88 --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-clients @@ -0,0 +1,12 @@ +#!/bin/sh +# This is autotest client lister. + +. ./autotest-lib + +echo -e "LAST ACTIVITY\t\t\tWFAIL#\tNAME" +for c in c/*; do + client="${c#c/}" + beacon="$(date --rfc-3339=seconds -d "1970-01-01 $(cat "$c/beacon") seconds")" + fails="$(grep -c fail "$c/log" || :)" + echo -e "$beacon\t$fails\t$client" +done diff --git a/jni/pachi/t-play/autotest/autotest-gather b/jni/pachi/t-play/autotest/autotest-gather new file mode 100644 index 0000000..fde7a0e --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-gather @@ -0,0 +1,48 @@ +#!/bin/sh +# This is autotest gathering tool, collecting game result info from +# all clients and putting it in single place (which is completely +# wiped otherwise). + +. ./autotest-lib + +mkdir -p "r/" +rm -f r/* + +# First, gather pairing data: + +for dir in c/*/*/; do + pairing="${dir%/}"; pairing="${pairing#c/*/}" + datfile="r/$pairing.dat" + + if [ ! -s "$datfile" ]; then + # Common datfile header, required for twogtp -analyze + { + echo "#" + echo -e "#GAME\tRES_B\tRES_W\tRES_R\tALT\tDUP\tLEN\tTIME_B\tTIME_W\tCPU_B\tCPU_W\tERR\tERR_MSG" + } >"$datfile" + num=0 + else + lastgame="$(tail -n 1 "$datfile")" + num="$((${lastgame%% *} + 1))" + fi + # We cannot simply concatenate all the datfiles because + # twogtp -analyze is pedantic about the games numbering; + # we need to renumber the games to a single sequence. + while read orignum line; do + echo -e "$num\t$line" + num=$((num+1)) + done <"$dir/game.dat" >>"$datfile" +done + + +# Next, fill per-pairing metadata: + +for datfile in r/*.dat; do + pairing="${datfile%.dat}"; pairing="${pairing#r/}" + + # Look at last change in pairing: + stat -c %Z "$(ls --sort=time c/*/"$pairing"/* | head -n 1)" >"r/$pairing.beacon" + + # Check pairing for errors: + tail -n +3 "$datfile" | cut -f 12 | { grep -c -vF 0 >"r/$pairing.error" || :; } +done diff --git a/jni/pachi/t-play/autotest/autotest-lib b/jni/pachi/t-play/autotest/autotest-lib new file mode 100644 index 0000000..41d2c5a --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-lib @@ -0,0 +1,17 @@ +# Library of common shell functions for the autotest framework. + +set -e # errors are fatal + + +# Client helpers. $clientid must be set. + +log() { + echo -e "$(date)\t$*" >>"c/$clientid/log" +} + + +# Generic functions. + +pairid() { + echo "$*" | tr ' ' '-' +} diff --git a/jni/pachi/t-play/autotest/autotest-prune b/jni/pachi/t-play/autotest/autotest-prune new file mode 100644 index 0000000..1bc713c --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-prune @@ -0,0 +1,18 @@ +#!/bin/sh +# This is autotest pruning tool, removing info about specified old +# pairings. Note that this removes only pairing results, you need +# to adjust the rc file manually if you haven't removed the pairing +# specifiers from there yet. + +mask="$1" +if [ -z "$mask" ]; then + echo "Usage: $0 MASK" >&2 + echo "MASK can be pairing name, or a glob pattern (*, ?)." >&2 + echo "Use \`$0 *\` to remove all pairings." >&2 + exit 1 +fi +. ./autotest-lib + +echo "Removing $(ls r/$mask.dat | wc -l) result pairs..." +rm -f r/$mask.dat r/$mask.summary.dat r/$mask.html r/$mask.beacon r/$mask.error +rm -rf c/*/$mask diff --git a/jni/pachi/t-play/autotest/autotest-show b/jni/pachi/t-play/autotest/autotest-show new file mode 100644 index 0000000..cde680b --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-show @@ -0,0 +1,54 @@ +#!/bin/sh +# This is autotest display tool, showing gathered information in +# a hopefully meaningful way. + +mask="$1" +[ -n "$mask" ] || mask="*" +. ./autotest-lib + +if [ ! -d "r" ]; then + echo "Did you run autotest-gather?" >&2 + exit 1 +fi + +# Load rc quickly: +player() { :; } +pknown=" " +pairing() { pknown="$pknown$(pairid "$@") "; } +. ./rc + +color_stale="\033[33m" +color_bad="\033[31m" +color_fresh="\033[1m" +color_stop="\033[0m" + +pairing_status() { + # See README for explanation of flags. + error=0; + if [ -s "r/$pairing.error" ] && [ "$(cat "r/$pairing.error")" -gt 0 ]; then + error=1; + fi + known=1; [ "${pknown#* $pairing *}" != "$pknown" ] || known=0 + active=1; ([ -s "r/$pairing.beacon" ] && [ "$(cat "r/$pairing.beacon")" -ge "$(($(date +%s) - 60*60*2))" ]) || active=0 + case $error$known$active in + 001) status="${color_stale}?";; + 000) status="${color_stale}/";; + 011) status="${color_fresh}.";; + 010) status="${color_fresh}${color_bad}!";; + 1*0) status="${color_bad}x";; + 1*1) status="${color_fresh}${color_bad}X";; + esac +} + +rm -f r/*.summary.dat r/*.html + +echo -e "S GAMES\tWINRATE\tS.D.\tPAIRING" +for pairing in $((echo "$pknown" | tr ' ' '\n'; ls r/*.dat | sed 's#^r/##; s#\.dat$##') | sort | uniq); do + [ ! -s "r/$pairing.dat" ] || $twogtp_path -analyze "r/$pairing.dat" + pairing_status "$pairing" # sets status + if [ -s "r/$pairing.summary.dat" ]; then + echo -e "$status $(cat "r/${pairing}".summary.dat | cut -f 1,7,8 | tail -n +2)\t$pairing$color_stop" + else + echo -e "$status 0\t-\t-\t$pairing$color_stop" + fi +done diff --git a/jni/pachi/t-play/autotest/autotest-worker b/jni/pachi/t-play/autotest/autotest-worker new file mode 100644 index 0000000..d9ae960 --- /dev/null +++ b/jni/pachi/t-play/autotest/autotest-worker @@ -0,0 +1,123 @@ +#!/bin/sh +# This is autotest Go-testing framework worker. It picks a random +# pairing among the ones defined in the rc file, and plays a single game. + +clientid="$1"; shift +. ./autotest-lib + + +# Load rc file. First, prepare functions for the endeavor: +echo "Loading rc file..." + +player() { + playerid="$1"; shift + playerspec="$1"; shift + playerargc="$1"; shift + + eval "${playerid}_spec='$playerspec'" + eval "${playerid}_argc='$playerargc'" +} +player_param() { # Accessor method + playerid="$1"; shift + playerattr="$1"; shift + playervar="${playerid}_${playerattr}" + # XXX: ${!...} is bash-specific? + eval "echo \$$playervar" +} + +pairs=0 +pairing() { + eval "pair${pairs}_id='$(pairid "$@")'" + eval "pair${pairs}_bsize='$1'"; shift + eval "pair${pairs}_komi='$1'"; shift + eval "pair${pairs}_black='$1'"; shift + + player="$1"; shift + args=""; argc=0 + while [ "$argc" -lt "$(player_param $player argc)" ] && [ "$#" -gt 0 ]; do + argc=$((argc+1)) + args="$args $1"; shift + done + eval "pair${pairs}_player1='$player'" + eval "pair${pairs}_player1_args='$args'" + + player="$1"; shift + args=""; argc=0 + while [ "$argc" -lt "$(player_param $player argc)" ] && [ "$#" -gt 0 ]; do + argc=$((argc+1)) + args="$args $1"; shift + done + eval "pair${pairs}_player2='$player'" + eval "pair${pairs}_player2_args='$args'" + + pairs=$((pairs+1)) +} +pairing_param() { # Accessor method + pairid="$1"; shift + pairattr="$1"; shift + pairvar="pair${pairid}_${pairattr}" + # XXX: ${!...} is bash-specific? + eval "echo \$$pairvar" +} + +. ./rc + + +echo "Loaded $pairs pairings" + +pair="$((RANDOM % $pairs))" +pairid="$(pairing_param $pair id)" +echo "Picked pairing #$pair: $pairid" + +log "starting game with pairing #$pair: $pairid" + + +# Assign players to final colors +black="$(pairing_param ${pair} black)" +[ "$black" != "a" ] || black="$((1 + RANDOM % 2))" +white="$((1+2-black))" +p_black="$(player_param $(pairing_param $pair player$black) spec)"; pa_black="$(pairing_param $pair player${black}_args)" +p_white="$(player_param $(pairing_param $pair player$white) spec)"; pa_white="$(pairing_param $pair player${white}_args)" + + +# Create player commandlines +prun_get() { + name="$1"; shift + cmdline="$1"; shift + # Rest of arguments are in $1, $2, ... + eval "$name=\"$cmdline\"" +} +prun_get prun_black "$p_black" $pa_black +prun_get prun_white "$p_white" $pa_white +komi="$(pairing_param $pair komi | sed 's/^h/0.5 -handicap /')" + + +# Run twogtp! +rm -f "c/$clientid"/scratch* +echo $twogtp_path -black "$prun_black" -white "$prun_white" -auto -verbose -size "$(pairing_param $pair bsize)" -komi $komi -games 1 -sgffile "c/$clientid/scratch" +$twogtp_path -black "$prun_black" -white "$prun_white" -auto -verbose -size "$(pairing_param $pair bsize)" -komi $komi -games 1 -sgffile "c/$clientid/scratch" + +# Get count of our game +mkdir -p "c/$clientid/$pairid" +if [ -s "c/$clientid/$pairid/game.dat" ]; then + gameno="$(($(tail -n 1 "c/$clientid/$pairid/game.dat" | cut -f 1) + 1))" +else + gameno=0 +fi +log "game over, storing as #$gameno; black=player$black (recording black=player2)" + +# Move result from scratchspace +cat "c/$clientid"/scratch.dat | grep -v '^#' | { + read GAME RES_B RES_W RES_R ALT DUP LEN TIME_B TIME_W CPU_B CPU_W ERR ERR_MSG + GAME="$gameno" + if [ "$black" -ne 2 ]; then + # Reverse colors + _S="$RES_B"; RES_B="$(echo $RES_W | tr 'BW' 'WB')"; RES_W="$(echo $_S | tr 'BW' 'WB')" + RES_R="$(echo "$RES_R" | tr 'BW' 'WB')" + ALT=1 + _S="$TIME_B"; TIME_B="$TIME_W"; TIME_W="$_S" + _S="$CPU_B"; CPU_B="$CPU_W"; CPU_W="$_S" + fi + echo -e "$GAME\t$RES_B\t$RES_W\t$RES_R\t$ALT\t$DUP\t$LEN\t$TIME_B\t$TIME_W\t$CPU_B\t$CPU_W\t$ERR\t$ERR_MSG" +} >>"c/$clientid/$pairid/game.dat" +mv "c/$clientid/scratch-0.sgf" "c/$clientid/$pairid/game-$gameno.sgf" diff --git a/jni/pachi/t-play/autotest/rc b/jni/pachi/t-play/autotest/rc new file mode 100644 index 0000000..dc78d1b --- /dev/null +++ b/jni/pachi/t-play/autotest/rc @@ -0,0 +1,58 @@ +# This is rc file for the autotest framework. It is interpreted as a shell +# snippet, so sh/bash evaluation rules apply. + +# Here, we define some general execution environment configuration for +# autotest, the players that will get paired, and then actual pairings +# to perform. autotest clients will repeatedly pick a random pairing +# from the defined ones, play a game and record the result. + +### General parameters: + +# twogtp path - we need the gogui twogtp variant (the one that takes -black, +# not --black as parameter) +twogtp_path="/nfs/drahokam/pasky/gogui-1.1.10/bin/gogui-twogtp" +# If you want handicap support, you will need a custom patch from: +# https://sourceforge.net/tracker/?func=detail&atid=489966&aid=2878719&group_id=59117 + + +### Players: +# The player specification is later eval'd during the payring. You can use +# $1, $2, ... placeholders within the specification to substitute various +# parameters at the pairing time. The number parameter after player command +# specification specifies how many parameters the specification takes. + +# Warning! If you change player definition, you WILL NOT be able to determine +# what definition your historical results used. Once you define a player of +# some id, it's bad idea to tweak the definition in any way. + +# This is the reference player we use for our tests. +player gnugo10 'gnugo --mode gtp --chinese-rules --capture-all-dead --level 10' 0 + +# This is generic Pachi player that is being tested, we substitute particular +# revision and specific parameters in the pairings. +# ${2:+,$2} means that if second parameter is passed, it will be inserted here, +# comma-prepended, otherwise nothing will be inserted. +player zamafmoggy5k './pachi-$1 games=5000,pass_all_alive,policy=ucb1amaf,playout=moggy${2:+,$2}' 2 + + +### Pairings: +# The pairing call looks somewhat elaborate: +# +# pairing BOARDSIZE KOMI BLACK PLAYER1 PLAYER1ARGS PLAYER2 PLAYER2ARGS +# BOARDSIZE: 9, 19, or whatever flavor you fancy... +# KOMI: 7.5, 0.5, ... OR h2, h6, ... (handicap amount, komi is forced to 0.5) +# BLACK: 'a' - black is chosen randomly, useful only if komi is 7.5 +# '1' - black is PLAYER1, '2' - black is PLAYER2 +# PLAYER: Name of the player as defined above. +# PLAYERARGS: Parameters for the player, substituted for $1, $2, ... +# Parameters must not contain whitespace. +# +# Win rates are always stored from PLAYER2 perspective, no matter +# what the player colors are - we assume that PLAYER1 is your reference +# player while PLAYER2 varies based on the particular configuration you +# want to test. + +# You will be editing this all the time. :) + +pairing 9 0.5 1 gnugo10 zamafmoggy5k 77d2d +pairing 9 0.5 1 gnugo10 zamafmoggy5k 77d2d amaf_prior=0 diff --git a/jni/pachi/t-play/resum b/jni/pachi/t-play/resum new file mode 100644 index 0000000..7eddf69 --- /dev/null +++ b/jni/pachi/t-play/resum @@ -0,0 +1,9 @@ +#!/bin/sh +rm *summ* +brm="$1" +[ -n "$brm" ] || brm="*dat" +for i in $(ls $brm); do + printf "%-40s\t" "${i%.dat}.summary.dat"; + ../../gogui-1.1.10/bin/gogui-twogtp -force -analyze $i; + cat ${i%.dat}.summary.dat | cut -f 1,7,8 | tail -n +2 +done diff --git a/jni/pachi/t-play/test_in_context.sh b/jni/pachi/t-play/test_in_context.sh new file mode 100644 index 0000000..5564f54 --- /dev/null +++ b/jni/pachi/t-play/test_in_context.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# $1: path to test results +# $2: board size + +(cd "$1" && ls f$2-*summ*dat) | sed 's/^f'$2'-//; s/[-.].*$//;' | xargs git log --pretty=oneline --no-walk | while read c s; do + echo + echo ================================================ + git log -1 --pretty=medium $c + for t in $(cd "$1" && ls f$2-${c:0:5}*summ*); do + echo + echo $t + cat "$1/$t" | cut -f 1,2,7,8,12,13 + done +done diff --git a/jni/pachi/t-regress/README b/jni/pachi/t-regress/README new file mode 100644 index 0000000..937c7bb --- /dev/null +++ b/jni/pachi/t-regress/README @@ -0,0 +1,42 @@ +This is Pachi's regression suite. It contains SGF game records that can +be automatically probed with current Pachi version for particular +behavior (usually observed in older Pachi versions). We do not anticipate +that Pachi would pass all of the tests - when it will, it will be probably +pro strength. ;-) The goal is simply to steadily reduce these and verify +that modifications do not reintroduce bad behavior. + +The game SGF records are in the games/ subdirectory, and also cross-linked +in by-*/ subdirectories based on categories. Each SGF record must contain +a GC[] tag ("game comment") describing the test-case in a manner that is +both human-readable and machine-readable. The description lists specific +moves and how they should be tested, one move per line, using this syntax: + + :[,:,...] + + movenumber: Number of the tested move + movepos: A move coordinate (just for consistency) + classification: Comma-separated list of move classifications, + no whitespaces + comment: Ignored by the testsuite. + +These classifications are available for now: + + bad This move should not be chosen as the next move. + bad! This move is so useless that it should not be in the top + considered moves. (This is a bit feeble class.) + must This move is essential and must be made immediately. + alive This group should be considered alive. + dead This group should be considered dead. + +Classifications may be prefixed by various letters: + + + An issue that is already supposed to be fixed. + / An intermittent issue that happens only in case of some + (mis)fortune in the search. + +Classifications may be postfixed by category name in {curly brackets}. + +Example: + + 40 C3:bad non-working invasion + 41 B2:bad{tsumego},+A1:bad{tsumego} makes C3 alive diff --git a/jni/pachi/t-regress/TODO b/jni/pachi/t-regress/TODO new file mode 100644 index 0000000..eb77d92 --- /dev/null +++ b/jni/pachi/t-regress/TODO @@ -0,0 +1,88 @@ +A list of games to put in the regression suite. + +This list can be processed automatically: + + sed -ne 's/^\* //p' #;B0@(#X T)yWJQ44FW+dO$tt3~3AiDK`wz literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-falseeye/2011-06-05-tazaki-pachi30s.sgf b/jni/pachi/t-regress/by-falseeye/2011-06-05-tazaki-pachi30s.sgf new file mode 100644 index 0000000000000000000000000000000000000000..6db277e14353174319078afd4dd4417c2af8c9b8 GIT binary patch literal 86 zcmeawE2;4D^Jdgz&|}bNNM}f7$Yn@nCH1636R%_s(%k;VW3i^C3K literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-falseeye/2011-06-09-botkiller2-pachi30s.sgf b/jni/pachi/t-regress/by-falseeye/2011-06-09-botkiller2-pachi30s.sgf new file mode 100644 index 0000000000000000000000000000000000000000..9f047b378900b1145b9f0bf4872647270e5b3feb GIT binary patch literal 94 zcmW-Z*$IF!07IVxwO$}Nf++4#r5<=<`{DQo2_zvTY0mg|eVbS;DKm}(PmnLj$OR*< dey+s>7m^%6CVkl58D6$ literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-falseeye/2011-06-10-pachi30s-samba-2.sgf b/jni/pachi/t-regress/by-falseeye/2011-06-10-pachi30s-samba-2.sgf new file mode 100644 index 0000000000000000000000000000000000000000..2fab59ebc71a5c7b2ec21a9710338fd6ba4a12b4 GIT binary patch literal 88 zcmW-YT?#-@6ofwuI)_WygUB8bmj_bEgXNnvHGj=?*YgfzUpP)lX5wTe565UqanAR|1!W3UHoo35`cer_z5mW`E@NS1eW^n#HN YV#g?c)ClYx$_CHPFCVmd^^oq6N%3^@$>3D TBr{|%WCGRd0rjLaq%i;hHdhT) literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-ladder/2011-06-06-tyzef-pachi30s.sgf b/jni/pachi/t-regress/by-ladder/2011-06-06-tyzef-pachi30s.sgf new file mode 100644 index 0000000000000000000000000000000000000000..e64222adb6de4942e62bc0cdfe77b48bfdc36749 GIT binary patch literal 84 zcmeawE2;4D^Jdgz&|}bNNM}f7$Yn@nC$))4zB>0094P4441_ literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-ladder/2012-03-16-IMC-pachi2.sgf b/jni/pachi/t-regress/by-ladder/2012-03-16-IMC-pachi2.sgf new file mode 100644 index 0000000000000000000000000000000000000000..1afa4798900d9971b0a8bcb775b31c27e9c20e33 GIT binary patch literal 76 zcmWNHK@LDL6a~kDd@YF!ST&`05TPvENLV<&x0#v6g!+DSij84(WL640kGjX<#a13f T&nN~KCi&L6adPP}{h5Ow^ezk% literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-semeai/2011-01-15-pachi2-rollingon.sgf b/jni/pachi/t-regress/by-semeai/2011-01-15-pachi2-rollingon.sgf new file mode 100644 index 0000000000000000000000000000000000000000..407138117d6f00bb8893875be7dfe5d25d70d7da GIT binary patch literal 88 zcmW-Y+X;X`5Cg}8c71?Fh++>SdLHPVs2`SZkU%n!q)X2`jD6uaC4q^9g#;B0@(#X T)yWJQ44FW+dO$tt3~3AiDK`wz literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-semeai/2011-06-05-tazaki-pachi30s.sgf b/jni/pachi/t-regress/by-semeai/2011-06-05-tazaki-pachi30s.sgf new file mode 100644 index 0000000000000000000000000000000000000000..6db277e14353174319078afd4dd4417c2af8c9b8 GIT binary patch literal 86 zcmeawE2;4D^Jdgz&|}bNNM}f7$Yn@nCH1636R%_s(%k;VW3i^C3K literal 0 HcmV?d00001 diff --git a/jni/pachi/t-regress/by-semeai/2011-06-18-dorabon-pachi2-4.sgf b/jni/pachi/t-regress/by-semeai/2011-06-18-dorabon-pachi2-4.sgf new file mode 100644 index 0000000000000000000000000000000000000000..dc5728bb3833cf19b100b36f3ccc24b0f0189bfc GIT binary patch literal 88 zcmWNJ+X;X$00Yl~TCIq move 171 0.2600]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [3d\]: GTP Engine for pachi2 (white): Pachi Distributed Engine version 6.99 (Chihaku-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +rollingon [3k\]: hi +]RE[B+Resign] +;B[pp]BL[1194.299] +;W[hc]WL[1191.693] +;B[dp]BL[1192.752] +;W[nc]WL[1165.267] +;B[dd]BL[1189.068] +;W[jp]WL[1139.23] +;B[pd]BL[1178.777] +;W[qh]WL[1113.379] +;B[qf]BL[1175.966] +;W[cm]WL[1087.969] +;B[fq]BL[1172.377] +;W[gp]WL[1062.581] +;B[iq]BL[1159.069] +;W[ip]WL[1055.255] +;B[gq]BL[1155.274] +;W[jq]WL[1029.792] +;B[ir]BL[1151.321] +;W[ng]WL[1004.287] +;B[ne]BL[1134.289] +;W[kc]WL[978.132] +;B[oc]BL[1130.427] +;W[cg]WL[952.597] +;B[dj]BL[1123.05] +;W[ce]WL[927.108] +;B[cd]BL[1117.418] +;W[gi]WL[901.458] +;B[eh]BL[1112.274] +;W[ef]WL[893.406] +;B[gh]BL[1106.846] +;W[hh]WL[885.174] +;B[gg]BL[1101.662] +;W[fi]WL[858.913] +;B[eg]BL[1093.05] +;W[de]WL[850.442] +;B[fe]BL[1086.767] +;W[ff]WL[824.813] +;B[gf]BL[1080.589] +;W[ed]WL[816.94] +;B[ec]BL[1060.204] +;W[fd]WL[814.721] +;B[db]BL[1055.35] +;W[ei]WL[789.174] +;B[di]BL[1045.752] +;W[dh]WL[781.154] +;B[ge]BL[1034.812] +;W[bd]WL[755.729] +;B[bc]BL[1032.468] +;W[cc]WL[730.387] +;B[dc]BL[1026.858] +;W[bb]WL[704.922] +;B[cb]BL[1022.043] +;W[hq]WL[679.361] +;B[ee]BL[1015.004] +;W[dk]WL[653.746] +;B[ej]BL[1006.612] +;W[ek]WL[628.319] +;B[fj]BL[1001.141] +;W[fk]WL[617.416] +;B[gj]BL[989.994] +;W[hj]WL[609.466] +;B[gk]BL[988.257] +;W[ik]WL[582.729] +;B[fl]BL[964.697] +;W[ck]WL[557.274] +;B[ci]BL[956.089] +;W[bh]WL[531.801] +;B[bi]BL[945.361] +;W[bf]WL[523.41] +;B[ad]BL[937.367] +;W[ai]WL[497.848] +;B[aj]BL[930.88] +;W[ah]WL[489.82] +;B[bj]BL[920.251] +;W[df]WL[463.295] +;B[be]BL[907.823] +;W[dg]WL[456.831] +;B[af]BL[882.205] +;W[if]WL[430.345] +;B[gd]BL[860.355] +;W[cp]WL[403.623] +;B[cq]BL[854.98] +;W[do]WL[388.193] +;B[bp]BL[838.312] +;W[po]WL[362.302] +;B[op]BL[829.922] +;W[qp]WL[336.38] +;B[qq]BL[827.795] +;W[oo]WL[310.44] +;B[np]BL[818.767] +;W[co]WL[284.171] +;B[ep]BL[814.876] +;W[bq]WL[258.052] +;B[br]BL[808.336] +;W[dq]WL[250.067] +;B[cr]BL[802.273] +;W[gm]WL[224.063] +;B[gl]BL[797.893] +;W[fm]WL[216.078] +;B[el]BL[774.735] +;W[em]WL[190.561] +;B[dl]BL[745.905] +;W[cl]WL[164.075] +;B[ch]BL[734.007] +;W[bg]WL[157.394] +;B[hl]BL[720.769] +;W[jm]WL[136.06] +;B[mn]BL[691.192] +;W[bo]WL[108.637] +;B[aq]BL[687.379] +;W[qo]WL[81.964] +;B[pm]BL[680.356] +;W[no]WL[73.477] +;B[mo]BL[678.226] +;W[om]WL[64.969] +;B[pj]BL[670.31] +;W[mp]WL[56.073] +;B[mq]BL[666.637] +;W[lp]WL[47.273] +;B[ol]BL[660.526] +;W[nm]WL[38.469] +;B[ph]BL[646.425] +;W[pg]WL[31.417] +;B[qg]BL[641.669] +;W[bk]WL[21.572] +;B[fg]BL[634.729] +;W[rq]WL[12.231] +;B[qr]BL[632.278] +;W[pi]WL[2.33] +;B[qi]BL[624.563] +;W[oh]WL[10]OW[3] +;B[rh]BL[623.167] +;W[oj]WL[10]OW[3] +;B[qj]BL[618.261] +;W[hr]WL[10]OW[3] +;B[rr]BL[615.223] +;W[il]WL[10]OW[3] +;B[hm]BL[612.613] +;W[hn]WL[10]OW[3] +;B[ae]BL[605.797] +;W[hk]WL[10]OW[3] +;B[le]BL[596.851] +;W[ok]WL[10]OW[3] +;B[pl]BL[589.569] +;W[nl]WL[10]OW[3] +;B[jd]BL[582.188] +;W[id]WL[10]OW[3] +;B[jc]BL[578.97] +;W[je]WL[10]OW[3] +;B[kd]BL[577.259] +;W[pk]WL[10]OW[3] +;B[qk]BL[573.189] +;W[mf]WL[10]OW[3] +;B[me]BL[567.31] +;W[lf]WL[10]OW[3] +;B[ke]BL[560.72] +;W[kf]WL[10]OW[3] +;B[lq]BL[557.002] +;W[kq]WL[10]OW[3] +;B[gr]BL[553.307] +;W[cj]WL[10]OW[3] +;B[hs]BL[541.766] +;W[hp]WL[10]OW[3] +;B[eq]BL[531.866] +;W[hb]WL[10]OW[3] +;B[gc]BL[523.772] +;W[rm]WL[10]OW[3] +;B[rl]BL[521.958] +;W[jb]WL[10]OW[3] +;B[kb]BL[519.82] +;W[dm]WL[10]OW[3] +;B[ag]BL[515.699] +;W[im]WL[10]OW[3] +;B[cf]BL[513.788] +;W[ob]WL[10]OW[3] +;B[pb]BL[510.944] +;W[od]WL[10]OW[3] +;B[pc]BL[504.808] +;W[oe]WL[10]OW[3] +;B[nd]BL[489.682] +;W[pe]WL[10]OW[3] +;B[qe]BL[480.1] +;W[lb]WL[10]OW[3] +;B[lc]BL[476.135]C[jlg [?\]: thanks for the game +]) diff --git a/jni/pachi/t-regress/games/2011-06-05-Zen19-pachi2.sgf b/jni/pachi/t-regress/games/2011-06-05-Zen19-pachi2.sgf new file mode 100644 index 0000000..657ef92 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-05-Zen19-pachi2.sgf @@ -0,0 +1,429 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[1740]OT[10/30 Canadian] +PW[Zen19]PB[pachi2]WR[4d]BR[3d]DT[2011-06-05]GC[185 /S15:bad should just connect at T13 +211 T18:bad!{semeai} useless, pachi still thinks the semeai is undecided but white has a lot more liberties. How can the playouts not see this? +217 +S3:bad!{falseeye} pachi must not hope white will end up with a semi-false eye and fill it instead of capturing the cutting stone +219 +S2:alive{falseeye} another verification of the S3 bug above]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [3d\]: GTP Engine for pachi2 (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +Zen19 [4d\]: GTP Engine for Zen19 (white): Zen version 7.7 +]RE[W+Resign] +;B[oc]BL[1739.323] +;W[pp]WL[1722.852] +;B[pn]BL[1738.474] +;W[np]WL[1714.566] +;B[qk]BL[1726.189] +;W[dd]WL[1697.475] +;B[co]BL[1688.04] +;W[dq]WL[1680.439] +;B[ep]BL[1665.598] +;W[eq]WL[1672.315] +;B[fp]BL[1653.635] +;W[gq]WL[1663.862] +;B[fc]BL[1616.76] +;W[cf]WL[1647.203] +;B[ch]BL[1581.562] +;W[qc]WL[1639.882] +;B[gp]BL[1547.708] +;W[hq]WL[1624.675] +;B[hp]BL[1514.792] +;W[pe]WL[1616.61] +;B[iq]BL[1503.413] +;W[lc]WL[1599.754] +;B[jc]BL[1468.749] +;W[eg]WL[1582.984] +;B[md]BL[1455.95] +;W[ld]WL[1568.011] +;B[le]BL[1444.971] +;W[jd]WL[1551.555] +;B[id]BL[1433.958] +;W[ke]WL[1541.906] +;B[mc]BL[1423.011] +;W[lf]WL[1528.974] +;B[me]BL[1411.757] +;W[lb]WL[1521.223] +;B[kf]BL[1399.469] +;W[jf]WL[1510.346] +;B[kg]BL[1388.745] +;W[jg]WL[1502.247] +;B[kh]BL[1357.129] +;W[jh]WL[1485.818]C[CCY [8k\]: is this a game in a tournament? +] +;B[ki]BL[1346.522] +;W[ic]WL[1478.305]C[NOTA [6k\]: ZvP +ajahuang [6d\]: pachi's opening is good now +] +;B[hc]BL[1319.763] +;W[ib]WL[1463.995] +;B[ie]BL[1309.263]C[Pinguschaf [?\]: j15? +Pinguschaf [?\]: this is gonna be interesting +] +;W[hb]WL[1449.091] +;B[je]BL[1297.716] +;W[kd]WL[1435.253] +;B[hg]BL[1280.962]C[Pinguschaf [?\]: this fight looks better for black +] +;W[ji]WL[1425.213]C[Pinguschaf [?\]: with the thickness waiting on lower side +] +;B[jj]BL[1270.483] +;W[gc]WL[1409.738] +;B[gd]BL[1254.169] +;W[hd]WL[1395.36]C[ajahuang [6d\]: ya black is better +] +;B[he]BL[1243.888] +;W[kj]WL[1387.793]C[DaniPerrin [2k\]: black is fighting well +] +;B[mh]BL[1213.093]C[Pinguschaf [?\]: but im not sure if positional judgement by humans matters at all +] +;W[ij]WL[1379.924] +;B[jk]BL[1202.542] +;W[gh]WL[1371.967]C[dsmic [13k?\]: to bad winrate not working during tournament +] +;B[dg]BL[1171.017]C[jlg [-\]: 53% for black +ajahuang [6d\]: things changed +] +;W[df]WL[1355.899]C[ajahuang [6d\]: L10 was good +dsmic [13k?\]: thx +] +;B[eh]BL[1160.642] +;W[fh]WL[1348.45]C[Bogin [-\]: looks like Zen enters its famous "kill mode" and sets its eye on J15... +] +;B[ef]BL[1131.513] +;W[fg]WL[1334.522] +;B[ik]BL[1119.765] +;W[ei]WL[1320.716]C[ajahuang [6d\]: suddenly pachi is losing +guxxan [6d\]: zEN -> kill mode +DaniPerrin [2k\]: but zen not knows mercy +guxxan [6d\]: cool +] +;B[cj]BL[1088.326] +;W[hh]WL[1313.571] +;B[fe]BL[1059.185]C[Meepy [-\]: h12 didn't look necessary +] +;W[dk]WL[1297.852]C[guxxan [6d\]: yeah, h12 is a waste move +] +;B[gb]BL[1015.543] +;W[hc]WL[1284.494] +;B[cc]BL[979.374]C[DaniPerrin [2k\]: black will live +] +;W[dh]WL[1268.938]C[CCY [8k\]: state of the art tenuki +] +;B[cg]BL[964.824] +;W[dc]WL[1254.751]C[Meepy [-\]: lol +] +;B[eb]BL[950.552] +;W[gf]WL[1243.208]C[guxxan [6d\]: Human does tenuki +] +;B[ge]BL[936.456] +;W[ga]WL[1227.968] +;B[db]BL[922.721]C[CCY [8k\]: well, im just finding a chance to tell this joke :| +Meepy [-\]: whoops +] +;W[fb]WL[1212.781] +;B[ec]BL[909.462]C[DaniPerrin [2k\]: stupid not to connect +ajahuang [6d\]: oh +] +;W[hk]WL[1197.657]C[ajahuang [6d\]: what is zen doing? +Bogin [-\]: dying :( +] +;B[ii]BL[896.302]C[Meepy [-\]: everything swings around, this is fun :) +] +;W[hj]WL[1182.711]C[jlg [-\]: 57% for black +k81 [4d\]: well - this zen runs on mac so... :) +guxxan [6d\]: oooooooooo +] +;B[ih]BL[883.293] +;W[ck]WL[1175.028]C[Pinguschaf [?\]: maybe zen will win by time :) +] +;B[dj]BL[869.746]C[DaniPerrin [2k\]: zen should resign +] +;W[ej]WL[1160.136]C[CCY [8k\]: winrate? +Bogin [-\]: nah, Zen will kill C12 ;) +] +;B[bi]BL[849.026]C[jlg [-\]: don't understimate the ability of pachi to lose won games +] +;W[il]WL[1153.591]C[ZenAuthor [-\]: 44% +] +;B[jl]BL[836.752]C[CCY [8k\]: thx +] +;W[jm]WL[1139.155] +;B[im]BL[824.962]C[ajahuang [6d\]: Zen was winning to moon +ajahuang [6d\]: now it is losing +] +;W[hl]WL[1125.38] +;B[if]BL[813.419]C[LexC [3k\]: what was the wrong move? +] +;W[kl]WL[1110.935]C[CCY [8k\]: too many state of the art move lol +] +;B[ig]BL[801.95]C[dsmic [13k?\]: f18? +] +;W[kk]WL[1102.327]C[ajahuang [6d\]: D9 was wrong +] +;B[ji]BL[791.102]C[ajahuang [6d\]: should played one more move in the corner +] +;W[bk]WL[1095.355] +;B[lp]BL[760.402]C[Pinguschaf [?\]: hehe +Pinguschaf [?\]: b13 +] +;W[ir]WL[1081.697] +;B[jr]BL[730.747] +;W[in]WL[1067.461] +;B[km]BL[720.614] +;W[jn]WL[1053.351] +;B[jp]BL[710.773] +;W[bg]WL[1043.716]C[DaniPerrin [2k\]: zen found b13 +] +;B[mn]BL[684.29] +;W[nn]WL[1037.198]C[jlg [-\]: pachi expected b13 next when playing k4 +] +;B[mo]BL[674.874] +;W[on]WL[1030.818] +;B[bh]BL[664.895] +;W[bf]WL[1017.719]C[CCY [8k\]: how do you know? are there any logs shown this information? +guxxan [6d\]: I have a question, why bots like to play in one place, then jump to another place, then jump back? +] +;B[pl]BL[639.486]C[me12k: guxxan, because of move "value" +] +;W[lm]WL[1003.871] +;B[mm]BL[628.683]C[guxxan [6d\]: yes, A->B->A +] +;W[qi]WL[991.577]C[guxxan [6d\]: after play B, A value becomes big again? +] +;B[ph]BL[604.8]C[Nickless [-\]: so like +Nickless [-\]: a = 12 points +] +;W[qh]WL[978.931]C[Nickless [-\]: b = 11 points +DaniPerrin [2k\]: because, urgency of these moves alternates globaly +me12k: yes, that's how good bots are made +Nickless [-\]: a2 = 10 points +Nickless [-\]: so it plays a then b then a2 +] +;B[qg]BL[583.629]C[guxxan [6d\]: I see +] +;W[pg]WL[966.328] +;B[rg]BL[561.639] +;W[qf]WL[953.978] +;B[og]BL[552.566]C[Bogin [-\]: or could it be that they are similarly big and because of randomness in the playouts it switches back and forth? +DaniPerrin [2k\]: but us, human, we focus too much on local fight +] +;W[pf]WL[941.717] +;B[pi]BL[551.374] +;W[qq]WL[929.951]C[me12k: bogin, not all bots use playouts, but they all associate a "value" to a move +] +;B[ri]BL[530.308] +;W[rf]WL[917.888]C[Bogin [-\]: I see, I was focussing on MC bots +] +;B[kn]BL[510.064] +;W[bq]WL[912.075]C[me12k: MC are the most common, but not all +Bogin [-\]: bots with score-based evaluation functions behave differently of course +] +;B[nr]BL[490.463] +;W[mq]WL[900.848]C[quicksand [1d\]: zen is wrapping up the game +] +;B[fq]BL[471.751]C[DaniPerrin [2k\]: who is winning now ? +] +;W[fr]WL[888.916] +;B[er]BL[464.511]C[CCY [8k\]: there still have points on MC bots. the move with the most playouts will be played +] +;W[gr]WL[877.268]C[quicksand [1d\]: w +me12k: white seems to be winning (but my 12k specticles might be wrong) +CCY [8k\]: the number of playouts is the value of moves +] +;B[lr]BL[446.863] +;W[qn]WL[869.344] +;B[qm]BL[440.96]C[ajahuang [6d\]: pachi played too many small moves +Meepy [-\]: c2 works? +] +;W[rn]WL[857.592]C[guxxan [6d\]: so so +] +;B[cr]BL[425.51]C[me12k: let's see +] +;W[dr]WL[845.541] +;B[cq]BL[424.381] +;W[dp]WL[832.611]C[Nickless [-\]: zen shouldve played c3 +] +;B[cp]BL[418.522]C[ajahuang [6d\]: Zen should connect C3 +] +;W[do]WL[824.074] +;B[dn]BL[412.613] +;W[cs]WL[818.245]C[Nickless [-\]: it was onlly capturing j2 sente then +] +;B[po]BL[404.317] +;W[qo]WL[805.567] +;B[oq]BL[391.568]C[DaniPerrin [2k\]: zen will cut at c6 +] +;W[oo]WL[794.774] +;B[eo]BL[385.759] +;W[es]WL[783.321] +;B[pm]BL[371.955] +;W[br]WL[770.879]C[Meepy [-\]: more back and forth +] +;B[op]BL[357.997]C[guxxan [6d\]: ooo, exchange +guxxan [6d\]: exiciting +] +;W[en]WL[759.065] +;B[pq]BL[348.693] +;W[qp]WL[748.048]C[ajahuang [6d\]: w is good +] +;B[gm]BL[335.269] +;W[em]WL[736.177] +;B[mb]BL[330.112] +;W[ee]WL[724.049] +;B[kb]BL[324.716] +;W[la]WL[717.082] +;B[ma]BL[318.817]C[LexC [3k\]: :) +] +;W[bc]WL[710.286] +;B[cd]BL[303.605]C[Nickless [-\]: zen doesnt see snapback ? +] +;W[bd]WL[698.183] +;B[rh]BL[288.783]C[Pinguschaf [?\]: he doesnt "see" anything +] +;W[gn]WL[686.269]C[Nickless [-\]: oh you smartass +] +;B[sf]BL[275.326]C[Pinguschaf [?\]: :) +Pinguschaf [?\]: but i think he played it because of j19 +] +;W[ko]WL[674.295]C[Pinguschaf [?\]: to avoid Ko +] +;B[ln]BL[270.491] +;W[kq]WL[668.802] +;B[jq]BL[256.103] +;W[se]WL[661.466]C[DaniPerrin [2k\]: this game is really confusing +ajahuang [6d\]: strange +me12k: zen might be stronger, but pachi has a nice pic +] +;B[re]BL[242.121]C[Nickless [-\]: im wondering why he played off after n18 +] +;W[sg]WL[655.879]C[ajahuang [6d\]: wow pachi +] +;B[bb]BL[229.109] +;W[mr]WL[645.037] +;B[qr]BL[224.751] +;W[rr]WL[635.098] +;B[pr]BL[224.05]C[Imp20 [3k\]: zen gonna win in the end game with moves like s15 +] +;W[rs]WL[624.907] +;B[hm]BL[211.128] +;W[hn]WL[618.964]C[Pinguschaf [?\]: maybe s15 is preparing amazing endgame for black :) +] +;B[rc]BL[201.786] +;W[pc]WL[609.27] +;B[qd]BL[195.989]C[Meepy [-\]: guess pachi lost +me12k: doubt it, pachi seems disperate +] +;W[kp]WL[599.018] +;B[lq]BL[191.922]C[ajahuang [6d\]: interesting +] +;W[pd]WL[592.962]C[jlg [-\]: no pachi stil thinks it is winning 62% +] +;B[qb]BL[187.816] +;W[pb]WL[587.694] +;B[rb]BL[186.803] +;W[sd]WL[582.258] +;B[fn]BL[175.017] +;W[fm]WL[574.431] +;B[rd]BL[171.025]C[ajahuang [6d\]: pachi should play T16 first +] +;W[qa]WL[569.25] +;B[ra]BL[170.004] +;W[pa]WL[560.013] +;B[sb]BL[165.451] +;W[kr]WL[554.906] +;B[ks]BL[161.27]C[me12k: pachi seems to loose with 80% acc +] +;W[qe]WL[549.875] +;B[bs]BL[153.781] +;W[as]WL[544.846] +;B[rq]BL[146.855]C[Imp20 [3k\]: what was b1 +] +;W[ff]WL[535.255] +;B[ka]BL[135.861] +;W[jb]WL[529.751]C[CCY [8k\]: zen is in cleanup mode +me12k: b1 was: please ignore this move so I can win +] +;B[rp]BL[125.496]C[ajahuang [6d\]: seems pachi misevaluates the top-right group +] +;W[ro]WL[524.61] +;B[sr]BL[114.857]C[DaniPerrin [2k\]: k5 is big +] +;W[sp]WL[519.656]C[me12k: he failed to count libs +] +;B[qs]BL[107.776] +;W[sq]WL[512.927] +;B[rq]BL[106.606] +;W[rp]WL[507.952]C[CCY [8k\]: T18 is temporally not dead, so it maybe wrongly evaluate winrate +] +;B[sm]BL[96.962] +;W[ja]WL[502.583] +;B[kc]BL[90.713] +;W[jo]WL[497.408]C[me12k: w+res ? +] +;B[ha]BL[82.474] +;W[fa]WL[492.245] +;B[el]BL[75.003] +;W[dm]WL[487.181] +;B[dl]BL[68.233]C[ajahuang [6d\]: this game is over +] +;W[cm]WL[480.37] +;B[cl]BL[62.191] +;W[bl]WL[471.868] +;B[be]BL[56.739] +;W[ce]WL[463.689] +;B[ac]BL[51.795] +;W[cb]WL[457.424] +;B[hs]BL[47.063] +;W[is]WL[448.059] +;B[js]BL[43.134] +;W[hr]WL[443.298]C[Imp20 [3k\]: b lost in end game +] +;B[ca]BL[38.204] +;W[ob]WL[438.812] +;B[oe]BL[33.159] +;W[of]WL[428.918] +;B[ls]BL[28.38] +;W[od]WL[424.501] +;B[gl]BL[24.064] +;W[nc]WL[420.147]C[me12k: ,-) +] +;B[fl]BL[19.822] +;W[fo]WL[415.82] +;B[hi]BL[17.742] +;W[gj]WL[411.471] +;B[gi]BL[16.91] +;W[fi]WL[407.105] +;B[bm]BL[12.213]C[jlg [-\]: yes pachi didn't evaluate top right correctly +] +;W[bn]WL[402.688] +;B[gk]BL[7.498] +;W[fj]WL[398.423] +;B[cn]BL[2.49] +;W[am]WL[394.105] +;B[go]BL[29.89]OB[9] +;W[fn]WL[389.853] +;B[nd]BL[27.379]OB[8] +;W[nb]WL[385.616] +;B[na]BL[25.068]OB[7] +;W[nf]WL[381.454] +;B[ne]BL[23.188]OB[6] +;W[mf]WL[377.21] +;B[bj]BL[19.865]OB[5] +;W[di]WL[373.114] +;B[cc]BL[16.473]OB[4] +;W[ed]WL[369.063] +;B[ad]BL[13.179]OB[3] +;W[ae]WL[364.887] +;B[cd]BL[9.436]OB[2] +;W[cb]WL[360.771] +;B[sn]BL[5.37]OB[1] +;W[cc]WL[356.726] +;B[lg]BL[30]OB[10] +;W[fd]WL[352.645] +;B[mg]BL[27.985]OB[9] +;W[da]WL[348.52] +;B[sh]BL[26.196]OB[8] +;W[sf]WL[344.297] +;B[ng]BL[25.365]OB[7] +;W[sc]WL[340.217]C[me12k: no res yet? +jlg [-\]: thanks for the game +ZenAuthor [-\]: thanks for the game +DaniPerrin [2k\]: incredible game +]) diff --git a/jni/pachi/t-regress/games/2011-06-05-tazaki-pachi30s.sgf b/jni/pachi/t-regress/games/2011-06-05-tazaki-pachi30s.sgf new file mode 100644 index 0000000..d612037 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-05-tazaki-pachi30s.sgf @@ -0,0 +1,401 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]HA[2]KM[0.50]TM[1800]OT[5x30 byo-yomi] +PW[tazaki]PB[pachi30s]WR[1k]DT[2011-06-05]GC[195 J16:must{semeai} pachi doesn't see even one move ahead in a semeai, it expects an answer outside the semeai. +244 +S8:alive{falseeye} T13 doesn't make sense. This is due to wrong estimate of white's right group.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [-\]: GTP Engine for pachi30s (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Resign] +;B[dp]BL[1795.911] +;B[pd]BL[1795.696] +;W[pq]WL[1797.84] +;B[qn]BL[1795.015] +;W[dc]WL[1790.345] +;B[de]BL[1794.244] +;W[ee]WL[1787.748] +;B[ef]BL[1766.122] +;W[ed]WL[1785.039] +;B[gf]BL[1737.873] +;W[df]WL[1782.6] +;B[eg]BL[1669.299] +;W[cf]WL[1758.018] +;B[dg]BL[1605.697] +;W[bh]WL[1712.968] +;B[cg]BL[1577.322] +;W[bg]WL[1708.665] +;B[nq]BL[1548.819] +;W[np]WL[1678.5] +;B[mp]BL[1520.377] +;W[op]WL[1663.135] +;B[mn]BL[1451.337] +;W[mq]WL[1653.05] +;B[lp]BL[1413.295] +;W[mr]WL[1629.072] +;B[lq]BL[1385.45] +;W[lr]WL[1623.48] +;B[kr]BL[1357.352] +;W[nr]WL[1610.707] +;B[ci]BL[1330.141] +;W[bi]WL[1602.535] +;B[cj]BL[1301.976] +;W[fp]WL[1564.782] +;B[fo]BL[1219.657] +;W[hp]WL[1477.006] +;B[go]BL[1191.462] +;W[gp]WL[1375.517] +;B[ho]BL[1164.262] +;W[dq]WL[1364.583] +;B[cq]BL[1135.862] +;W[eq]WL[1334.681] +;B[cp]BL[1066.957] +;W[jq]WL[1301.872] +;B[on]BL[988.247] +;W[ph]WL[1272.218] +;B[mh]BL[958.945] +;W[pk]WL[1238.18] +;B[oj]BL[882.77] +;W[pj]WL[1232.951] +;B[jo]BL[832.345] +;W[kq]WL[1226.932] +;B[pf]BL[800.151] +;W[pm]WL[1180.938] +;B[pn]BL[771.312] +;W[nl]WL[1178.281] +;B[mk]BL[742.374] +;W[ml]WL[1173.437] +;B[kk]BL[662.311] +;W[kf]WL[1061.759] +;B[ql]BL[585.055] +;W[qm]WL[1014.397] +;B[pl]BL[556.459] +;W[om]WL[835.234] +;B[rm]BL[527.857] +;W[ok]WL[804.271] +;B[rj]BL[455.149] +;W[nn]WL[754.127] +;B[no]BL[425.727] +;W[nm]WL[752.006] +;B[oo]BL[396.23] +;W[ri]WL[667.092] +;B[ip]BL[331.895] +;W[iq]WL[663.406] +;B[qi]BL[286.904] +;W[qj]WL[646.199] +;B[rh]BL[258.752] +;W[rk]WL[642.759] +;B[si]BL[185.952] +;W[rl]WL[640.578] +;B[pi]BL[156.211] +;W[rn]WL[634.944] +;B[ro]BL[124.528] +;W[sm]WL[631.075] +;B[jc]BL[49.528] +;W[he]WL[567.914] +;B[ge]BL[16.555] +;W[hd]WL[534.945] +;B[gd]BL[30]OB[5] +;W[gc]WL[532.19] +;B[fc]BL[30]OB[5] +;W[hc]WL[527.941] +;B[je]BL[30]OB[5] +;W[jf]WL[525.456] +;B[if]BL[30]OB[5] +;W[ie]WL[513.709] +;B[hf]BL[30]OB[5] +;W[ke]WL[489.366] +;B[jd]BL[30]OB[5] +;W[lc]WL[445.958] +;B[ld]BL[30]OB[5] +;W[kd]WL[440.974] +;B[kc]BL[30]OB[5] +;W[lb]WL[437.616] +;B[jg]BL[30]OB[5] +;W[mf]WL[384.114] +;B[md]BL[30]OB[5] +;W[kb]WL[304.486] +;B[jb]BL[30]OB[5] +;W[ib]WL[285.009] +;B[bf]BL[30]OB[5] +;W[ce]WL[281.041] +;B[lf]BL[30]OB[5] +;W[kg]WL[270.835] +;B[kh]BL[30]OB[5] +;W[jh]WL[267.772] +;B[be]BL[30]OB[5] +;W[cd]WL[264.858] +;B[cc]BL[30]OB[5] +;W[bd]WL[259.942] +;B[lg]BL[30]OB[5] +;W[ig]WL[248.036] +;B[le]BL[30]OB[4] +;W[jg]WL[245.597] +;B[ja]BL[30]OB[4] +;W[ia]WL[227.789] +;B[pp]BL[30]OB[4] +;W[oq]WL[223.243] +;B[ji]BL[30]OB[4] +;W[ii]WL[208.913] +;B[sn]BL[30]OB[4] +;W[so]WL[201.299] +;B[ij]BL[30]OB[4] +;W[hi]WL[180.605] +;B[gi]BL[30]OB[4] +;W[hj]WL[174.39] +;B[hg]BL[30]OB[4] +;W[ih]WL[172.074] +;B[hr]BL[30]OB[4] +;W[gr]WL[151.984] +;B[hk]BL[30]OB[4] +;W[gj]WL[144.852] +;B[bc]BL[30]OB[4] +;W[af]WL[117.221] +;B[ad]BL[30]OB[4] +;W[ae]WL[110.758] +;B[fj]BL[30]OB[4] +;W[gk]WL[108.209] +;B[hq]BL[30]OB[4] +;W[gq]WL[96.759] +;B[ir]BL[30]OB[4] +;W[jr]WL[91.282] +;B[rp]BL[30]OB[4] +;W[bk]WL[57.513] +;B[sk]BL[30]OB[4] +;W[nc]WL[44.118] +;B[mc]BL[30]OB[4] +;W[mb]WL[40.613] +;B[db]BL[30]OB[4] +;W[eb]WL[30]OW[5] +;B[bj]BL[30]OB[4] +;W[aj]WL[30]OW[5] +;B[ec]BL[30]OB[4] +;W[fb]WL[30]OW[4] +;B[fd]BL[30]OB[4] +;W[dd]WL[30]OW[4] +;B[gb]BL[30]OB[4] +;W[ga]WL[30]OW[4] +;B[fa]BL[30]OB[4] +;W[ea]WL[30]OW[4] +;B[da]BL[30]OB[4] +;W[hb]WL[30]OW[4] +;B[fk]BL[30]OB[4] +;W[gl]WL[30]OW[4] +;B[fl]BL[30]OB[4] +;W[bm]WL[30]OW[4] +;B[hl]BL[30]OB[4] +;W[gm]WL[30]OW[4] +;B[oc]BL[30]OB[4] +;W[nb]WL[30]OW[4] +;B[fm]BL[30]OB[4] +;W[jj]WL[30]OW[4] +;B[ki]BL[30]OB[4] +;W[hm]WL[30]OW[4] +;B[km]BL[30]OB[4] +;W[im]WL[30]OW[4] +;B[jk]BL[30]OB[4] +;W[ik]WL[30]OW[4] +;B[nd]BL[30]OB[4] +;W[bo]WL[30]OW[4] +;B[ob]BL[30]OB[4] +;W[nj]WL[30]OW[4] +;B[la]BL[30]OB[4] +;W[ic]WL[30]OW[4] +;B[ka]BL[30]OB[4] +;W[id]WL[30]OW[4] +;B[oi]BL[30]OB[4] +;W[mj]WL[30]OW[4] +;B[lk]BL[30]OB[4] +;W[nk]WL[30]OW[4] +;B[ai]BL[30]OB[4] +;W[ah]WL[30]OW[4] +;B[cl]BL[30]OB[4] +;W[bl]WL[30]OW[4] +;B[cn]BL[30]OB[4] +;W[bn]WL[30]OW[4] +;B[ak]BL[30]OB[4] +;W[al]WL[30]OW[4] +;B[ba]BL[30]OB[4] +;W[ab]WL[30]OW[4] +;B[ac]BL[30]OB[4] +;W[bb]WL[30]OW[4] +;B[cb]BL[30]OB[4] +;W[ca]WL[30]OW[4] +;B[kj]BL[30]OB[4] +;W[ck]WL[30]OW[4] +;B[ng]BL[30]OB[4] +;W[ch]WL[30]OW[4] +;B[ei]BL[30]OB[4] +;W[dh]WL[30]OW[4] +;B[qq]BL[30]OB[4] +;W[gh]WL[30]OW[4] +;B[eh]BL[30]OB[4] +;W[dk]WL[30]OW[4] +;B[dm]BL[30]OB[4] +;W[ej]WL[30]OW[4] +;B[fi]BL[30]OB[4] +;W[di]WL[30]OW[4] +;B[gs]BL[30]OB[4] +;W[cr]WL[30]OW[4] +;B[ep]BL[30]OB[4] +;W[fr]WL[30]OW[4] +;B[er]BL[30]OB[4] +;W[es]WL[30]OW[4] +;B[qr]BL[30]OB[4] +;W[bq]WL[30]OW[4] +;B[js]BL[30]OB[4] +;W[ks]WL[30]OW[4] +;B[dr]BL[30]OB[4] +;W[ds]WL[30]OW[4] +;B[hh]BL[30]OB[4] +;W[co]WL[30]OW[4] +;B[sj]BL[30]OB[4] +;W[sl]WL[30]OW[4] +;B[sg]BL[30]OB[4] +;W[ma]WL[30]OW[4] +;B[ln]BL[30]OB[4] +;W[dn]WL[30]OW[4] +;B[en]BL[30]OB[4] +;W[cm]WL[30]OW[4] +;B[gn]BL[30]OB[4] +;W[dl]WL[30]OW[4] +;B[mg]BL[30]OB[4] +;W[oa]WL[30]OW[4] +;B[pa]BL[30]OB[4] +;W[pb]WL[30]OW[4] +;B[na]BL[30]OB[4] +;W[bp]WL[30]OW[4] +;B[eo]BL[30]OB[4] +;W[oa]WL[30]OW[4] +;B[qa]BL[30]OB[4] +;W[pc]WL[30]OW[4] +;B[na]BL[30]OB[4] +;W[ff]WL[30]OW[4] +;B[fg]BL[30]OB[4] +;W[oa]WL[30]OW[4] +;B[od]BL[30]OB[4] +;W[rb]WL[30]OW[4] +;B[na]BL[30]OB[4] +;W[fe]WL[30]OW[4] +;B[gg]BL[30]OB[4] +;W[oa]WL[30]OW[4] +;B[qd]BL[30]OB[4] +;W[qb]WL[30]OW[4] +;B[na]BL[30]OB[4] +;W[pr]WL[30]OW[3] +;B[oa]BL[30]OB[4] +;W[qs]WL[30]OW[3] +;B[ij]BL[30]OB[4] +;W[jl]WL[30]OW[3] +;B[ps]BL[30]OB[4] +;W[os]WL[30]OW[3] +;B[il]BL[30]OB[4] +;W[ik]WL[30]OW[3] +;B[jm]BL[30]OB[4] +;W[jp]WL[30]OW[3] +;B[rr]BL[30]OB[4] +;W[io]WL[30]OW[3] +;B[em]BL[30]OB[4] +;W[jn]WL[30]OW[3] +;B[ko]BL[30]OB[4] +;W[rs]WL[30]OW[3] +;B[kl]BL[30]OB[4] +;W[il]WL[30]OW[3] +;B[el]BL[30]OB[4] +;W[ek]WL[30]OW[3] +;B[jj]BL[30]OB[4] +;W[sp]WL[30]OW[2] +;B[sq]BL[30]OB[4] +;W[sn]WL[30]OW[2] +;B[oh]BL[30]OB[4] +;W[sr]WL[30]OW[2] +;B[ss]BL[30]OB[4] +;W[rd]WL[30]OW[2] +;B[ps]BL[30]OB[4] +;W[ra]WL[30]OW[2] +;B[sd]BL[30]OB[4] +;W[se]WL[30]OW[2] +;B[re]BL[30]OB[4] +;W[sc]WL[30]OW[2] +;B[sf]BL[30]OB[4] +;W[qc]WL[30]OW[2] +;B[sd]BL[30]OB[4] +;W[qp]WL[30]OW[1] +;B[rc]BL[30]OB[4] +;W[qs]WL[30]OW[1] +;B[qo]BL[30]OB[4] +;W[li]WL[30]OW[1] +;B[in]BL[30]OB[4] +;W[hn]WL[30]OW[1] +;B[fh]BL[30]OB[4] +;W[ni]WL[30]OW[1] +;B[kn]BL[30]OB[4] +;W[in]WL[30]OW[1] +;B[cb]BL[30]OB[4] +;W[aa]WL[30]OW[1] +;B[rs]BL[30]OB[4] +;W[rd]WL[30]OW[1] +;B[sb]BL[30]OB[4] +;W[se]WL[30]OW[1] +;B[aq]BL[30]OB[4] +;W[sa]WL[30]OW[1] +;B[sd]BL[30]OB[4] +;W[do]WL[30]OW[1] +;B[rc]BL[30]OB[4] +;W[ps]WL[30]OW[1] +;B[da]BL[30]OB[4] +;W[mi]WL[30]OW[1] +;B[ba]BL[30]OB[4] +;W[cc]WL[30]OW[1] +;B[bc]BL[30]OB[4] +;W[ac]WL[30]OW[1] +;B[se]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[og]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[po]BL[30]OB[4] +;W[lh]WL[30]OW[1] +;B[kc]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[of]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[ka]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[pg]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[jb]BL[30]OB[4] +;W[jc]WL[30]OW[1] +;B[me]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[bs]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[br]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[cs]BL[30]OB[4] +;W[dr]WL[30]OW[1] +;B[ap]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[hl]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[qg]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[ar]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[an]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[rf]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[lm]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[ao]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[lo]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[as]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[hs]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[qh]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[nf]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[rg]BL[30]OB[4] +;W[]WL[30]OW[1] +;B[mf]BL[30]OB[4] +;W[lj]WL[30]OW[1]C[jlg [-\]: thanks for the game, which exposed several bugs in pachi. Sorry it didn't resign sooner. +]) diff --git a/jni/pachi/t-regress/games/2011-06-06-tyzef-pachi30s.sgf b/jni/pachi/t-regress/games/2011-06-06-tyzef-pachi30s.sgf new file mode 100644 index 0000000..e55abfe --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-06-tyzef-pachi30s.sgf @@ -0,0 +1,31 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[1800]OT[5x30 byo-yomi] +PW[tyzef]PB[pachi30s]WR[7k]DT[2011-06-06]GC[17 +D6:bad!{ladder} plays out ladder]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [-\]: GTP Engine for pachi30s (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Resign] +;B[qc]BL[1796.606] +;W[ep]WL[1796.485] +;B[pq]BL[1718.228] +;W[po]WL[1763.099] +;B[mq]BL[1643.515] +;W[pl]WL[1750.088] +;B[cp]BL[1562.141] +;W[do]WL[1521.137] +;B[cn]BL[1512.02] +;W[co]WL[1448.438] +;B[bo]BL[1484.653] +;W[bp]WL[1403.785] +;B[bq]BL[1456.25] +;W[bn]WL[1398.747] +;B[ap]BL[1427.902] +;W[cm]WL[1359.821] +;B[dn]BL[1399.52] +;W[en]WL[1316.795] +;B[dm]BL[1371.136] +;W[dl]WL[1292.431] +;B[em]BL[1342.671] +;W[fm]WL[1240.348] +;B[el]BL[1314.255] +;W[ek]WL[1237.356] +;B[fl]BL[1285.74] +;W[]WL[1237.355] +;B[fn]BL[1257.253]) diff --git a/jni/pachi/t-regress/games/2011-06-07-fidibus-pachi30s.sgf b/jni/pachi/t-regress/games/2011-06-07-fidibus-pachi30s.sgf new file mode 100644 index 0000000..1cf366b --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-07-fidibus-pachi30s.sgf @@ -0,0 +1,233 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[1800]OT[5x30 byo-yomi] +PW[fidibus]PB[pachi30s]WR[2d]BR[2d]DT[2011-06-07]GC[91 /T3:bad useless move (wastes ko threat) +189 B15:bad! useless move (and totally gote)]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [2d\]: GTP Engine for pachi30s (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Resign] +;B[pc]BL[1796.354] +;W[qp]WL[1798.083] +;B[op]BL[1795.541] +;W[pn]WL[1794.386] +;B[cq]BL[1794.701] +;W[cd]WL[1791.764] +;B[jp]BL[1709.301] +;W[pe]WL[1764.168] +;B[ck]BL[1624.174] +;W[dp]WL[1751.283] +;B[cp]BL[1581.71] +;W[do]WL[1746.648] +;B[co]BL[1553.624] +;W[dn]WL[1741.578] +;B[fl]BL[1467.095] +;W[dq]WL[1709.031] +;B[fr]BL[1386.438] +;W[fq]WL[1695.683] +;B[gq]BL[1358.318] +;W[er]WL[1689.224] +;B[fp]BL[1330.1] +;W[eq]WL[1685.43] +;B[cm]BL[1247.654] +;W[gr]WL[1675.065] +;B[hr]BL[1219.345] +;W[fs]WL[1671.136] +;B[iq]BL[1144.181] +;W[gp]WL[1650.924] +;B[hq]BL[1115.298] +;W[go]WL[1640.739] +;B[mc]BL[1029.745] +;W[qc]WL[1626.656] +;B[ec]BL[943.116] +;W[de]WL[1615.369] +;B[hc]BL[868.261] +;W[dc]WL[1608.867] +;B[qg]BL[787.554] +;W[pb]WL[1572.609] +;B[qd]BL[759.285] +;W[pd]WL[1563.659] +;B[oc]BL[730.792] +;W[ob]WL[1551.15] +;B[nb]BL[651.828] +;W[rd]WL[1540.357] +;B[ql]BL[622.817] +;W[qj]WL[1519.01] +;B[ol]BL[594.133] +;W[oj]WL[1503.774] +;B[nj]BL[518.569] +;W[ni]WL[1490.329] +;B[mj]BL[490.737] +;W[ok]WL[1457.08] +;B[nl]BL[462.617] +;W[nn]WL[1446.062] +;B[oi]BL[375.72] +;W[pi]WL[1413.318] +;B[oh]BL[347.735] +;W[ph]WL[1409.16] +;B[pg]BL[307.44] +;W[og]WL[1405.791] +;B[nh]BL[277.623] +;W[ng]WL[1401.151] +;B[mh]BL[247.758] +;W[pf]WL[1398.375] +;B[mg]BL[217.878] +;W[ne]WL[1386.247] +;B[qb]BL[187.902] +;W[qe]WL[1375.755] +;B[qq]BL[147.477] +;W[rq]WL[1366.208] +;B[fo]BL[67.057] +;W[fn]WL[1360.862] +;B[pq]BL[36.474] +;W[ro]WL[1346.657] +;B[gn]BL[5.985] +;W[gm]WL[1339.727] +;B[hn]BL[30]OB[5] +;W[fm]WL[1332.204] +;B[qn]BL[30]OB[5] +;W[po]WL[1304.503] +;B[ho]BL[30]OB[5] +;W[cr]WL[1289.48] +;B[rr]BL[30]OB[5] +;W[sr]WL[1286.276] +;B[br]BL[30]OB[5] +;W[bs]WL[1282.297] +;B[no]BL[30]OB[5] +;W[mn]WL[1265.061] +;B[on]BL[30]OB[5] +;W[oo]WL[1253.958] +;B[sq]BL[30]OB[5] +;W[sp]WL[1245.353] +;B[om]BL[30]OB[5] +;W[np]WL[1239.051] +;B[mo]BL[30]OB[5] +;W[oq]WL[1232.8] +;B[nf]BL[30]OB[5] +;W[of]WL[1229.557] +;B[or]BL[30]OB[5] +;W[pp]WL[1222.076] +;B[ri]BL[30]OB[5] +;W[qh]WL[1208.836] +;B[cg]BL[30]OB[5] +;W[nr]WL[1195.857] +;B[mq]BL[30]OB[5] +;W[mp]WL[1191.019] +;B[lo]BL[30]OB[5] +;W[lp]WL[1189.266] +;B[nq]BL[30]OB[5] +;W[mr]WL[1138.42] +;B[rg]BL[30]OB[5] +;W[rh]WL[1134.625] +;B[lq]BL[30]OB[5] +;W[op]WL[1126.817] +;B[lr]BL[30]OB[5] +;W[pr]WL[1122.523] +;B[ee]BL[30]OB[5] +;W[eb]WL[1111.628] +;B[fb]BL[30]OB[5] +;W[ed]WL[1107.52] +;B[fc]BL[30]OB[5] +;W[fd]WL[1092.505] +;B[gd]BL[30]OB[5] +;W[fe]WL[1086.68] +;B[ef]BL[30]OB[5] +;W[ko]WL[1084.426] +;B[gf]BL[30]OB[5] +;W[ff]WL[1073.357] +;B[fg]BL[30]OB[5] +;W[eg]WL[1069.974] +;B[ln]BL[30]OB[5] +;W[kn]WL[1066.493] +;B[lm]BL[30]OB[5] +;W[kp]WL[1059.246] +;B[df]BL[30]OB[5] +;W[gg]WL[1053.774] +;B[fh]BL[30]OB[5] +;W[cf]WL[1048.381] +;B[dg]BL[30]OB[5] +;W[ge]WL[1043.759] +;B[hf]BL[30]OB[5] +;W[hd]WL[1042.566] +;B[db]BL[30]OB[5] +;W[cb]WL[1036.9] +;B[qr]BL[30]OB[5] +;W[os]WL[1032.567] +;B[id]BL[30]OB[5] +;W[gc]WL[1025.96] +;B[gb]BL[30]OB[5] +;W[ic]WL[1012.608] +;B[hb]BL[30]OB[5] +;W[bf]WL[979.698] +;B[he]BL[30]OB[5] +;W[mf]WL[959.872] +;B[gd]BL[30]OB[5] +;W[dd]WL[956.063] +;B[pk]BL[30]OB[5] +;W[pj]WL[941.807] +;B[sh]BL[30]OB[5] +;W[rk]WL[918.464] +;B[rl]BL[30]OB[5] +;W[sj]WL[890.616] +;B[kq]BL[30]OB[5] +;W[rb]WL[859.806] +;B[bg]BL[30]OB[5] +;W[cn]WL[845.913] +;B[bn]BL[30]OB[5] +;W[kf]WL[816.072] +;B[af]BL[30]OB[5] +;W[ae]WL[805.256] +;B[bd]BL[30]OB[5] +;W[bc]WL[787.366] +;B[lf]BL[30]OB[5] +;W[le]WL[780.641] +;B[si]BL[30]OB[5] +;W[rj]WL[772.019] +;B[qk]BL[30]OB[5] +;W[rf]WL[766.417] +;B[nk]BL[30]OB[5] +;W[kc]WL[743.992] +;B[jc]BL[30]OB[5] +;W[lc]WL[722.744] +;B[kb]BL[30]OB[5] +;W[lb]WL[708.027] +;B[bp]BL[30]OB[5] +;W[dm]WL[697.426] +;B[dl]BL[30]OB[5] +;W[ag]WL[691.081] +;B[be]BL[30]OB[5] +;W[ah]WL[639.931] +;B[ds]BL[30]OB[5] +;W[cs]WL[635.045] +;B[da]BL[30]OB[5] +;W[ca]WL[626.339] +;B[ea]BL[30]OB[5] +;W[bi]WL[623.765] +;B[lg]BL[30]OB[5] +;W[km]WL[607.075] +;B[md]BL[30]OB[5] +;W[me]WL[592.118] +;B[kl]BL[30]OB[5] +;W[jl]WL[577.798] +;B[ke]BL[30]OB[5] +;W[je]WL[559.7] +;B[kd]BL[30]OB[5] +;W[ld]WL[533.293] +;B[jd]BL[30]OB[5] +;W[ll]WL[531.789] +;B[la]BL[30]OB[5] +;W[ma]WL[518.979] +;B[mm]BL[30]OB[5] +;W[ml]WL[514.311] +;B[nm]BL[30]OB[5] +;W[kk]WL[512.681] +;B[sg]BL[30]OB[5] +;W[sf]WL[509.097] +;B[mb]BL[30]OB[5] +;W[jb]WL[485.987] +;B[ib]BL[30]OB[5] +;W[ka]WL[482.822] +;B[qa]BL[30]OB[5] +;W[ra]WL[475.154] +;B[jj]BL[30]OB[5] +;W[pm]WL[470.7] +;B[sk]BL[30]OB[5] +;W[sl]WL[463.965] +;B[sm]BL[30]OB[5] +;W[qf]WL[459.617]) diff --git a/jni/pachi/t-regress/games/2011-06-09-botkiller2-pachi30s.sgf b/jni/pachi/t-regress/games/2011-06-09-botkiller2-pachi30s.sgf new file mode 100644 index 0000000..51108ca --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-09-botkiller2-pachi30s.sgf @@ -0,0 +1,372 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[0.50]TM[1800]OT[5x30 byo-yomi] +PW[botkiller2]PB[pachi30s]WR[3d]BR[2d]DT[2011-06-09]GC[177 +A15:alive{falseeye} threw away a won game. pachi thought until the end that top left white group was dead, so it made silly moves elsewhere. The playouts must be prevented from filling the eye at A17.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [2d\]: GTP Engine for pachi30s (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Resign] +;B[oc]BL[1795.834] +;W[po]WL[1797.078] +;B[pq]BL[1794.768] +;W[qq]WL[1792.596] +;B[cd]BL[1710.418] +;W[pe]WL[1774.01] +;B[pd]BL[1624.575] +;W[oe]WL[1758.204] +;B[lc]BL[1596.321] +;W[qd]WL[1736.896] +;B[qc]BL[1568.351] +;W[qe]WL[1731.824] +;B[mq]BL[1534.624] +;W[pj]WL[1721.911] +;B[cp]BL[1450.193] +;W[ep]WL[1697.131] +;B[cm]BL[1421.916] +;W[cq]WL[1692.154] +;B[gp]BL[1337.56] +;W[bp]WL[1673.282] +;B[dq]BL[1279.549] +;W[dp]WL[1663.673] +;B[co]BL[1250.889] +;W[cr]WL[1625.821] +;B[bo]BL[1174.614] +;W[gq]WL[1623.721] +;B[hq]BL[1146.334] +;W[fq]WL[1609.989] +;B[ii]BL[1067.772] +;W[ed]WL[1601.257] +;B[cg]BL[987.938] +;W[cc]WL[1585.272] +;B[gc]BL[959.551] +;W[dd]WL[1577.813] +;B[ce]BL[931.124] +;W[db]WL[1557.645] +;B[mn]BL[849.326] +;W[hp]WL[1458.603] +;B[iq]BL[778.614] +;W[go]WL[1449.373] +;B[ip]BL[746.792] +;W[ho]WL[1446.345] +;B[io]BL[665.848] +;W[hm]WL[1411.145] +;B[qp]BL[637.627] +;W[pp]WL[1408.213] +;B[rq]BL[609.261] +;W[qr]WL[1406.547] +;B[rr]BL[561.352] +;W[rp]WL[1379.414] +;B[qo]BL[532.481] +;W[qn]WL[1377.746] +;B[ro]BL[503.301] +;W[rn]WL[1375.763] +;B[sp]BL[422.194] +;W[oq]WL[1364.534] +;B[pn]BL[394.627] +;W[pr]WL[1357.605] +;B[on]BL[365.049] +;W[nr]WL[1208.843] +;B[qm]BL[335.509] +;W[qk]WL[1173.009] +;B[op]BL[305.905] +;W[oo]WL[1133.275] +;B[np]BL[276.31] +;W[no]WL[1129.637] +;B[mo]BL[246.314] +;W[mr]WL[1121.077] +;B[lr]BL[195.007] +;W[os]WL[1103.737] +;B[nn]BL[165.744] +;W[rc]WL[1084.344] +;B[ge]BL[129.87] +;W[ef]WL[1069.213] +;B[cj]BL[52.075] +;W[hd]WL[1005.424] +;B[gd]BL[18.397] +;W[if]WL[949.574] +;B[hf]BL[30]OB[5] +;W[jd]WL[906.733] +;B[jb]BL[30]OB[5] +;W[ib]WL[898.152] +;B[ie]BL[30]OB[5] +;W[je]WL[885.418] +;B[jf]BL[30]OB[5] +;W[ig]WL[882.2] +;B[jg]BL[30]OB[5] +;W[he]WL[878.955] +;B[ic]BL[30]OB[5] +;W[id]WL[842.045] +;B[hc]BL[30]OB[5] +;W[ld]WL[807.996] +;B[md]BL[30]OB[5] +;W[kb]WL[800.53] +;B[jc]BL[30]OB[5] +;W[kc]WL[798.578] +;B[lb]BL[30]OB[5] +;W[ja]WL[784.866] +;B[la]BL[30]OB[5] +;W[ka]WL[781.191] +;B[me]BL[30]OB[5] +;W[jh]WL[716.35] +;B[ih]BL[30]OB[5] +;W[hg]WL[713.662] +;B[gf]BL[30]OB[5] +;W[kh]WL[606.672] +;B[lg]BL[30]OB[5] +;W[gh]WL[593.164] +;B[gg]BL[30]OB[5] +;W[hh]WL[590.093] +;B[fh]BL[30]OB[5] +;W[gj]WL[585.823] +;B[eg]BL[30]OB[5] +;W[le]WL[495.812] +;B[rb]BL[30]OB[5] +;W[qb]WL[490] +;B[pb]BL[30]OB[5] +;W[pc]WL[488.02] +;B[lf]BL[30]OB[5] +;W[od]WL[412.816] +;B[nc]BL[30]OB[5] +;W[mf]WL[381.398] +;B[lh]BL[30]OB[5] +;W[nf]WL[371.927] +;B[hj]BL[30]OB[5] +;W[fi]WL[366.844] +;B[hk]BL[30]OB[5] +;W[fl]WL[309.866] +;B[qa]BL[30]OB[5] +;W[ob]WL[273.597] +;B[ei]BL[30]OB[5] +;W[gk]WL[174.378] +;B[qc]BL[30]OB[5] +;W[kj]WL[152.331] +;B[lj]BL[30]OB[5] +;W[qb]WL[148.994] +;B[kd]BL[30]OB[5] +;W[ke]WL[139.712] +;B[qc]BL[30]OB[5] +;W[jk]WL[63.246] +;B[lk]BL[30]OB[5] +;W[qb]WL[60.501] +;B[hl]BL[30]OB[5] +;W[pa]WL[45.979] +;B[im]BL[30]OB[5] +;W[mi]WL[30.999] +;B[li]BL[30]OB[5] +;W[gm]WL[18.905] +;B[il]BL[30]OB[5] +;W[rm]WL[30]OW[5] +;B[pq]BL[30]OB[5] +;W[df]WL[30]OW[5] +;B[cf]BL[30]OB[5] +;W[ej]WL[30]OW[5] +;B[di]BL[30]OB[5] +;W[aq]WL[30]OW[5] +;B[nh]BL[30]OB[5] +;W[oi]WL[30]OW[5] +;B[ec]BL[30]OB[5] +;W[dc]WL[30]OW[5] +;B[eb]BL[30]OB[5] +;W[bd]WL[30]OW[4] +;B[da]BL[30]OB[5] +;W[ca]WL[30]OW[4] +;B[bb]BL[30]OB[5] +;W[bc]WL[30]OW[4] +;B[be]BL[30]OB[5] +;W[ab]WL[30]OW[4] +;B[ba]BL[30]OB[5] +;W[ea]WL[30]OW[4] +;B[hb]BL[30]OB[5] +;W[ad]WL[30]OW[4] +;B[ia]BL[30]OB[5] +;W[ie]WL[30]OW[4] +;B[kd]BL[30]OB[5] +;W[ae]WL[30]OW[4] +;B[cl]BL[30]OB[5] +;W[ag]WL[30]OW[4] +;B[bh]BL[30]OB[5] +;W[ah]WL[30]OW[4] +;B[rl]BL[30]OB[5] +;W[ql]WL[30]OW[4] +;B[hi]BL[30]OB[5] +;W[gi]WL[30]OW[4] +;B[bi]BL[30]OB[5] +;W[ai]WL[30]OW[4] +;B[pm]BL[30]OB[5] +;W[rk]WL[30]OW[4] +;B[af]BL[30]OB[5] +;W[bf]WL[30]OW[4] +;B[oh]BL[30]OB[5] +;W[ph]WL[30]OW[4] +;B[ni]BL[30]OB[5] +;W[nj]WL[30]OW[4] +;B[gl]BL[30]OB[5] +;W[em]WL[30]OW[4] +;B[fm]BL[30]OB[5] +;W[fn]WL[30]OW[4] +;B[qs]BL[30]OB[5] +;W[ms]WL[30]OW[4] +;B[sl]BL[30]OB[5] +;W[sk]WL[30]OW[4] +;B[ok]BL[30]OB[5] +;W[oj]WL[30]OW[4] +;B[sn]BL[30]OB[5] +;W[sm]WL[30]OW[4] +;B[pg]BL[30]OB[5] +;W[og]WL[30]OW[4] +;B[ng]BL[30]OB[5] +;W[of]WL[30]OW[4] +;B[qh]BL[30]OB[5] +;W[pi]WL[30]OW[4] +;B[gr]BL[30]OB[5] +;W[fr]WL[30]OW[4] +;B[sl]BL[30]OB[5] +;W[rl]WL[30]OW[4] +;B[aj]BL[30]OB[5] +;W[kr]WL[30]OW[4] +;B[nk]BL[30]OB[5] +;W[kq]WL[30]OW[4] +;B[lq]BL[30]OB[5] +;W[ls]WL[30]OW[4] +;B[qg]BL[30]OB[5] +;W[ri]WL[30]OW[4] +;B[qi]BL[30]OB[5] +;W[qj]WL[30]OW[4] +;B[si]BL[30]OB[5] +;W[mj]WL[30]OW[4] +;B[rh]BL[30]OB[5] +;W[rf]WL[30]OW[4] +;B[mk]BL[30]OB[5] +;W[kp]WL[30]OW[4] +;B[kn]BL[30]OB[5] +;W[bk]WL[30]OW[4] +;B[nq]BL[30]OB[5] +;W[pp]WL[30]OW[4] +;B[rj]BL[30]OB[5] +;W[po]WL[30]OW[4] +;B[oo]BL[30]OB[5] +;W[pq]WL[30]OW[4] +;B[so]BL[30]OB[5] +;W[sg]WL[30]OW[4] +;B[sh]BL[30]OB[5] +;W[rg]WL[30]OW[4] +;B[pl]BL[30]OB[5] +;W[qf]WL[30]OW[4] +;B[br]BL[30]OB[5] +;W[pf]WL[30]OW[4] +;B[bq]BL[30]OB[5] +;W[bs]WL[30]OW[4] +;B[el]BL[30]OB[5] +;W[dl]WL[30]OW[4] +;B[ap]BL[30]OB[5] +;W[bj]WL[30]OW[4] +;B[dk]BL[30]OB[5] +;W[dr]WL[30]OW[4] +;B[dm]BL[30]OB[5] +;W[fk]WL[30]OW[4] +;B[bl]BL[30]OB[5] +;W[hr]WL[30]OW[4] +;B[de]BL[30]OB[5] +;W[ee]WL[30]OW[4] +;B[en]BL[30]OB[5] +;W[fm]WL[30]OW[4] +;B[ir]BL[30]OB[5] +;W[gs]WL[30]OW[4] +;B[fb]BL[30]OB[5] +;W[in]WL[30]OW[4] +;B[hn]BL[30]OB[5] +;W[jn]WL[30]OW[4] +;B[jo]BL[30]OB[5] +;W[jm]WL[30]OW[4] +;B[jl]BL[30]OB[5] +;W[km]WL[30]OW[4] +;B[kl]BL[30]OB[5] +;W[lm]WL[30]OW[4] +;B[fo]BL[30]OB[5] +;W[fp]WL[30]OW[4] +;B[ko]BL[30]OB[5] +;W[ll]WL[30]OW[4] +;B[kk]BL[30]OB[5] +;W[dn]WL[30]OW[4] +;B[do]BL[30]OB[5] +;W[eo]WL[30]OW[4] +;B[bg]BL[30]OB[5] +;W[af]WL[30]OW[4] +;B[ck]BL[30]OB[5] +;W[ak]WL[30]OW[4] +;B[nb]BL[30]OB[5] +;W[dg]WL[30]OW[4] +;B[al]BL[30]OB[5] +;W[aj]WL[30]OW[4] +;B[jr]BL[30]OB[5] +;W[dh]WL[30]OW[4] +;B[ci]BL[30]OB[5] +;W[lp]WL[30]OW[4] +;B[pk]BL[30]OB[5] +;W[sj]WL[30]OW[4] +;B[mh]BL[30]OB[5] +;W[ri]WL[30]OW[4] +;B[cs]BL[30]OB[5] +;W[ds]WL[30]OW[4] +;B[sc]BL[30]OB[5] +;W[kc]WL[30]OW[4] +;B[rd]BL[30]OB[5] +;W[qc]WL[30]OW[4] +;B[ln]BL[30]OB[5] +;W[kb]WL[30]OW[4] +;B[fg]BL[30]OB[5] +;W[ka]WL[30]OW[4] +;B[ja]BL[30]OB[5] +;W[kd]WL[30]OW[4] +;B[mm]BL[30]OB[5] +;W[gn]WL[30]OW[4] +;B[ga]BL[30]OB[5] +;W[ps]WL[30]OW[4] +;B[ml]BL[30]OB[5] +;W[rs]WL[30]OW[4] +;B[ks]BL[30]OB[5] +;W[js]WL[30]OW[4] +;B[sr]BL[30]OB[5] +;W[hn]WL[30]OW[4] +;B[ki]BL[30]OB[5] +;W[is]WL[30]OW[4] +;B[ar]BL[30]OB[5] +;W[ek]WL[30]OW[4] +;B[cn]BL[30]OB[5] +;W[fd]WL[30]OW[4] +;B[an]BL[30]OB[5] +;W[kf]WL[30]OW[4] +;B[ji]BL[30]OB[5] +;W[kg]WL[30]OW[4] +;B[qh]BL[30]OB[5] +;W[en]WL[30]OW[4] +;B[mp]BL[30]OB[5] +;W[dl]WL[30]OW[4] +;B[ss]BL[30]OB[5]C[botkiller2 [3d\]: bot thinks it can win... +] +;W[qs]WL[30]OW[4] +;B[na]BL[30]OB[5] +;W[nd]WL[30]OW[4] +;B[sd]BL[30]OB[5] +;W[ne]WL[30]OW[4] +;B[oa]BL[30]OB[5] +;W[pb]WL[30]OW[4] +;B[pg]BL[30]OB[5] +;W[mc]WL[30]OW[4] +;B[md]BL[30]OB[5] +;W[mb]WL[30]OW[4] +;B[mg]BL[30]OB[5] +;W[ma]WL[30]OW[4] +;B[el]BL[30]OB[5] +;W[cs]WL[30]OW[4] +;B[dj]BL[30]OB[5] +;W[da]WL[30]OW[4] +;B[gb]BL[30]OB[5] +;W[dl]WL[30]OW[4] +;B[jj]BL[30]OB[5] +;W[el]WL[30]OW[4] +;B[es]BL[30]OB[5] +;W[er]WL[30]OW[4] +;B[sh]BL[30]OB[5] +;W[eh]WL[30]OW[4]C[botkiller2 [3d\]: terrible bot +] +;B[ol]BL[30]OB[5] +;W[]WL[30]OW[4] +;B[lo]BL[30]OB[5] +;W[cb]WL[30]OW[4]) diff --git a/jni/pachi/t-regress/games/2011-06-09-jinen-pachi30s-2.sgf b/jni/pachi/t-regress/games/2011-06-09-jinen-pachi30s-2.sgf new file mode 100644 index 0000000..38c7942 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-09-jinen-pachi30s-2.sgf @@ -0,0 +1,257 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]HA[3]KM[0.50]TM[1800]OT[5x30 byo-yomi] +PW[jinen]PB[pachi30s]WR[4d]BR[1d]DT[2011-06-09]GC[164 +G5:bad{ko} Playing the ko threat before the ko.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [1d\]: GTP Engine for pachi30s (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Resign] +;W[qp]WL[1798.299] +;B[oq]BL[1766.122] +;W[lp]WL[1782.42] +;B[po]BL[1737.702] +;W[qo]WL[1773.017] +;B[lo]BL[1693.737] +;W[mp]WL[1733.221] +;B[pn]BL[1665.942] +;W[qn]WL[1706.279] +;B[kp]BL[1637.843] +;W[mo]WL[1699.004] +;B[kq]BL[1609.935] +;W[mn]WL[1691.408] +;B[mr]BL[1581.682] +;W[pm]WL[1680.31] +;B[om]BL[1553.616] +;W[pl]WL[1547.219] +;B[ol]BL[1526.166] +;W[pk]WL[1482.22] +;B[ml]BL[1443.306] +;W[lm]WL[1421.515] +;B[ll]BL[1415.201] +;W[km]WL[1409.94] +;B[kl]BL[1386.688] +;W[im]WL[1376.486] +;B[jm]BL[1357.232] +;W[jn]WL[1371.829] +;B[jl]BL[1328.765] +;W[ko]WL[1323.001] +;B[jo]BL[1300.294] +;W[kn]WL[1305.945] +;B[ip]BL[1220.398] +;W[io]WL[1299.561] +;B[hl]BL[1183.817] +;W[jp]WL[1291.942] +;B[iq]BL[1155.313] +;W[hm]WL[1260.725] +;B[gp]BL[1080.782] +;W[gl]WL[1246.768] +;B[hk]BL[1003.258] +;W[fn]WL[1213.701] +;B[gk]BL[975.247] +;W[fl]WL[1201.033] +;B[jc]BL[893.372] +;W[cf]WL[1171.348] +;B[ch]BL[811.163] +;W[cc]WL[787.527] +;B[cd]BL[782.877] +;W[dc]WL[775.14] +;B[ed]BL[753.671] +;W[bd]WL[769.46] +;B[be]BL[725.313] +;W[bc]WL[765.898] +;B[ce]BL[696.983] +;W[fc]WL[761.594] +;B[fk]BL[627.44] +;W[el]WL[755.745] +;B[fd]BL[551.675] +;W[gc]WL[751.256] +;B[gd]BL[523.253] +;W[hc]WL[744.075] +;B[id]BL[486.936] +;W[dj]WL[716.751] +;B[ph]BL[457.78] +;W[oj]WL[702.172] +;B[ni]BL[428.97] +;W[pc]WL[687.701] +;B[oc]BL[400.856] +;W[ob]WL[683.792] +;B[qc]BL[372.691] +;W[pb]WL[679.778] +;B[nc]BL[344.541] +;W[qd]WL[674.185] +;B[ek]BL[316.277] +;W[dk]WL[670.681] +;B[dl]BL[287.937] +;W[dm]WL[655.313] +;B[ec]BL[214.541] +;W[eb]WL[651.168] +;B[qe]BL[174.606] +;W[rd]WL[647.561] +;B[cl]BL[145.868] +;W[cm]WL[640.059] +;B[db]BL[104.714] +;W[cb]WL[632.348] +;B[bl]BL[26.533] +;W[bj]WL[476.533] +;B[fb]BL[30]OB[5] +;W[da]WL[472.652] +;B[bm]BL[30]OB[5] +;W[co]WL[457.804] +;B[di]BL[30]OB[5] +;W[ej]WL[243.199] +;B[cj]BL[30]OB[5] +;W[ck]WL[238.754] +;B[bk]BL[30]OB[5] +;W[ci]WL[234.805] +;B[ei]BL[30]OB[5] +;W[fj]WL[224.57] +;B[fi]BL[30]OB[5] +;W[gj]WL[219.833] +;B[gm]BL[30]OB[5] +;W[fm]WL[208.215] +;B[ho]BL[30]OB[5] +;W[hn]WL[201.252] +;B[bi]BL[30]OB[5] +;W[cj]WL[194.984] +;B[bh]BL[30]OB[5] +;W[aj]WL[51.892] +;B[hi]BL[30]OB[5] +;W[hj]WL[35.43] +;B[ij]BL[30]OB[5] +;W[ii]WL[19.549] +;B[jj]BL[30]OB[5] +;W[gi]WL[16.454] +;B[hh]BL[30]OB[5] +;W[gh]WL[13.99] +;B[gg]BL[30]OB[5] +;W[hg]WL[11.669] +;B[ih]BL[30]OB[5] +;W[fg]WL[7.122] +;B[gf]BL[30]OB[5] +;W[dh]WL[30]OW[5] +;B[dg]BL[30]OB[5] +;W[eh]WL[30]OW[5] +;B[cp]BL[30]OB[5] +;W[bn]WL[30]OW[5] +;B[pe]BL[30]OB[5] +;W[rc]WL[30]OW[5] +;B[qj]BL[30]OB[5] +;W[nb]WL[30]OW[4] +;B[mc]BL[30]OB[5] +;W[oi]WL[30]OW[4] +;B[oh]BL[30]OB[5] +;W[nj]WL[30]OW[4] +;B[mj]BL[30]OB[5] +;W[nk]WL[30]OW[4] +;B[qq]BL[30]OB[5] +;W[rq]WL[30]OW[4] +;B[pq]BL[30]OB[5] +;W[mk]WL[30]OW[4] +;B[lj]BL[30]OB[5] +;W[bp]WL[30]OW[3] +;B[rr]BL[30]OB[5] +;W[rk]WL[30]OW[2] +;B[qk]BL[30]OB[5] +;W[ql]WL[30]OW[2] +;B[rj]BL[30]OB[5] +;W[rm]WL[30]OW[2] +;B[rp]BL[30]OB[5] +;W[ro]WL[30]OW[2] +;B[eg]BL[30]OB[5] +;W[fh]WL[30]OW[2] +;B[bq]BL[30]OB[5] +;W[br]WL[30]OW[1] +;B[ap]BL[30]OB[5] +;W[ao]WL[30]OW[1] +;B[bo]BL[30]OB[5] +;W[jh]WL[30]OW[1] +;B[ig]BL[30]OB[5] +;W[bp]WL[30]OW[1] +;B[aq]BL[30]OB[5] +;W[cq]WL[30]OW[1] +;B[go]BL[30]OB[5] +;W[gn]WL[30]OW[1] +;B[pj]BL[30]OB[5] +;W[ok]WL[30]OW[1] +;B[bo]BL[30]OB[5] +;W[if]WL[30]OW[1] +;B[jg]BL[30]OB[5] +;W[bp]WL[30]OW[1] +;B[sk]BL[30]OB[5] +;W[rl]WL[30]OW[1] +;B[bo]BL[30]OB[5] +;W[kg]WL[30]OW[1] +;B[ji]BL[30]OB[5] +;W[bp]WL[30]OW[1] +;B[gb]BL[30]OB[5] +;W[hb]WL[30]OW[1] +;B[bo]BL[30]OB[5] +;W[jf]WL[30]OW[1] +;B[hf]BL[30]OB[5] +;W[bp]WL[30]OW[1] +;B[qb]BL[30]OB[5] +;W[ar]WL[30]OW[1] +;B[er]BL[30]OB[5] +;W[pi]WL[30]OW[1] +;B[qh]BL[30]OB[5] +;W[qi]WL[30]OW[1] +;B[rb]BL[30]OB[5] +;W[sb]WL[30]OW[1] +;B[ri]BL[30]OB[5] +;W[re]WL[30]OW[1] +;B[rf]BL[30]OB[5] +;W[mb]WL[30]OW[1] +;B[sc]BL[30]OB[5] +;W[sd]WL[30]OW[1] +;B[ra]BL[30]OB[5] +;W[pa]WL[30]OW[1] +;B[ep]BL[30]OB[5] +;W[lc]WL[30]OW[1] +;B[ld]BL[30]OB[5] +;W[kc]WL[30]OW[1] +;B[kd]BL[30]OB[5] +;W[jb]WL[30]OW[1] +;B[kb]BL[30]OB[5] +;W[lb]WL[30]OW[1] +;B[sp]BL[30]OB[5] +;W[ic]WL[30]OW[1] +;B[jd]BL[30]OB[5] +;W[pp]WL[30]OW[1] +;B[sf]BL[30]OB[5] +;W[qa]WL[30]OW[1] +;B[nl]BL[30]OB[5] +;W[lr]WL[30]OW[1] +;B[kr]BL[30]OB[5] +;W[nr]WL[30]OW[1] +;B[lq]BL[30]OB[5] +;W[nq]WL[30]OW[1] +;B[ff]BL[30]OB[5] +;W[dr]WL[30]OW[1] +;B[or]BL[30]OB[5] +;W[es]WL[30]OW[1] +;B[fr]BL[30]OB[5] +;W[fs]WL[30]OW[1] +;B[gs]BL[30]OB[5] +;W[ds]WL[30]OW[1] +;B[gr]BL[30]OB[5] +;W[lk]WL[30]OW[1] +;B[kk]BL[30]OB[5] +;W[il]WL[30]OW[1] +;B[ik]BL[30]OB[5] +;W[os]WL[30]OW[1] +;B[ps]BL[30]OB[5] +;W[ns]WL[30]OW[1] +;B[ea]BL[30]OB[5] +;W[fa]WL[30]OW[1] +;B[dn]BL[30]OB[5] +;W[do]WL[30]OW[1] +;B[eo]BL[30]OB[5] +;W[en]WL[30]OW[1] +;B[ib]BL[30]OB[5] +;W[ia]WL[30]OW[1] +;B[ga]BL[30]OB[5] +;W[ha]WL[30]OW[1] +;B[mq]BL[30]OB[5] +;W[op]WL[30]OW[1] +;B[nn]BL[30]OB[5] +;W[no]WL[30]OW[1] +;B[ms]BL[30]OB[5] +;W[np]WL[30]OW[1] +;B[on]BL[30]OB[5] +;W[qs]WL[30]OW[1]) diff --git a/jni/pachi/t-regress/games/2011-06-10-pachi30s-samba-2.sgf b/jni/pachi/t-regress/games/2011-06-10-pachi30s-samba-2.sgf new file mode 100644 index 0000000..e9bb2ca --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-10-pachi30s-samba-2.sgf @@ -0,0 +1,335 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[0.50]TM[1800]OT[5x30 byo-yomi] +PW[pachi30s]PB[samba]WR[1d]BR[1k]DT[2011-06-10]GC[258 +C18:alive{falseeye} pachi considers the top left black group as unsettled although it is clearly alive. So it makes silly moves elsewhere.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [1d\]: GTP Engine for pachi30s (white): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Resign] +;B[pd]BL[1798.278] +;W[mc]WL[1796.021] +;B[dp]BL[1770.546] +;W[dn]WL[1711.416] +;B[fp]BL[1762.906] +;W[ci]WL[1626.642] +;B[pq]BL[1732.268] +;W[op]WL[1541.96] +;B[pp]BL[1679.338] +;W[oo]WL[1514.114] +;B[po]BL[1636.081] +;W[on]WL[1486.72] +;B[pn]BL[1626.493] +;W[om]WL[1458.229] +;B[pl]BL[1579.026] +;W[iq]WL[1378.443] +;B[cn]BL[1564.864] +;W[cm]WL[1350.431] +;B[co]BL[1522.501] +;W[pm]WL[1312.622] +;B[qm]BL[1520.374] +;W[ql]WL[1283.841] +;B[qk]BL[1470.869] +;W[rl]WL[1256.193] +;B[rm]BL[1468.285] +;W[pk]WL[1218.46] +;B[ol]BL[1435.613] +;W[rk]WL[1216.551] +;B[qj]BL[1432.12] +;W[pj]WL[1188.272] +;B[rj]BL[1418.6] +;W[nl]WL[1159.967] +;B[sk]BL[1403.481] +;W[ok]WL[1131.479] +;B[sl]BL[1401.673] +;W[gc]WL[1052.223] +;B[dd]BL[1387.403] +;W[cf]WL[972.5] +;B[df]BL[1379.738] +;W[dg]WL[944.538] +;B[ef]BL[1371.919] +;W[ce]WL[866.251] +;B[cd]BL[1160.369] +;W[jc]WL[795.898] +;B[fd]BL[1141.829] +;W[fh]WL[736.959] +;B[dm]BL[1134.01] +;W[cl]WL[708.177] +;B[dl]BL[1086.979] +;W[pl]WL[637.222] +;B[ck]BL[1080.537] +;W[bk]WL[608.87] +;B[dk]BL[1055.746] +;W[bj]WL[531.906] +;B[bn]BL[1052.32] +;W[qi]WL[502.917] +;B[ri]BL[1044.782] +;W[qh]WL[474.008] +;B[rh]BL[1042.858] +;W[qg]WL[417.405] +;B[rg]BL[1016.719] +;W[sj]WL[344.539] +;B[si]BL[1013.449] +;W[qf]WL[269.238] +;B[re]BL[1011.248] +;W[rf]WL[240.025] +;B[sf]BL[1002.756] +;W[sg]WL[169.802] +;B[rd]BL[966.303] +;W[se]WL[99.141] +;B[nd]BL[935.976] +;W[oc]WL[22.817] +;B[nc]BL[925.861] +;W[nb]WL[30]OW[5] +;B[od]BL[917.912] +;W[pb]WL[30]OW[5] +;B[qb]BL[904.895] +;W[oq]WL[30]OW[5] +;B[io]BL[889.848] +;W[jp]WL[30]OW[5] +;B[or]BL[881.074] +;W[nr]WL[30]OW[5] +;B[pr]BL[877.948] +;W[jm]WL[30]OW[5] +;B[jo]BL[864.437] +;W[ko]WL[30]OW[5] +;B[mf]BL[854.686] +;W[kf]WL[30]OW[5] +;B[md]BL[840.646] +;W[lc]WL[30]OW[5] +;B[pc]BL[838.335] +;W[ec]WL[30]OW[5] +;B[fc]BL[806.989] +;W[fb]WL[30]OW[5] +;B[db]BL[805.828] +;W[ed]WL[30]OW[5] +;B[eb]BL[792.012] +;W[fe]WL[30]OW[5] +;B[bd]BL[765.285] +;W[de]WL[30]OW[5] +;B[mh]BL[752.372] +;W[lh]WL[30]OW[5] +;B[lg]BL[739.686] +;W[kg]WL[30]OW[5] +;B[li]BL[725.529] +;W[kh]WL[30]OW[5] +;B[ob]BL[719.163] +;W[mi]WL[30]OW[5] +;B[jk]BL[707.333] +;W[hm]WL[30]OW[5] +;B[hk]BL[702.073] +;W[ho]WL[30]OW[5] +;B[hq]BL[675.142] +;W[hp]WL[30]OW[5] +;B[hr]BL[672.289] +;W[gk]WL[30]OW[5] +;B[gj]BL[660.375] +;W[fk]WL[30]OW[5] +;B[fj]BL[637.561] +;W[ej]WL[30]OW[5] +;B[ek]BL[626.398] +;W[fm]WL[30]OW[5] +;B[ir]BL[580.559] +;W[hj]WL[30]OW[5] +;B[gn]BL[572.103] +;W[gm]WL[30]OW[5] +;B[kq]BL[539.535] +;W[kp]WL[30]OW[5] +;B[lq]BL[534.085] +;W[qn]WL[30]OW[5] +;B[rn]BL[531.072] +;W[dc]WL[30]OW[5] +;B[cb]BL[520.793] +;W[nh]WL[30]OW[5] +;B[mg]BL[514.479] +;W[ro]WL[30]OW[5] +;B[qo]BL[508.407] +;W[jj]WL[30]OW[5] +;B[ni]BL[480.038] +;W[mj]WL[30]OW[5] +;B[oh]BL[477.645] +;W[og]WL[30]OW[5] +;B[ng]BL[469.94] +;W[sm]WL[30]OW[5] +;B[sn]BL[460.559] +;W[pi]WL[30]OW[5] +;B[ke]BL[430.464] +;W[le]WL[30]OW[5] +;B[ld]BL[419.902] +;W[kd]WL[30]OW[5] +;B[je]BL[410.3] +;W[ie]WL[30]OW[5] +;B[jd]BL[394.157] +;W[kc]WL[30]OW[5] +;B[id]BL[326.729] +;W[hd]WL[30]OW[5] +;B[ic]BL[287.285] +;W[ib]WL[30]OW[5] +;B[lf]BL[264.884] +;W[hc]WL[30]OW[5] +;B[jf]BL[253.374] +;W[if]WL[30]OW[5] +;B[jg]BL[251.112] +;W[jh]WL[30]OW[5] +;B[ig]BL[245.852] +;W[hg]WL[30]OW[5] +;B[ih]BL[241.883] +;W[ii]WL[30]OW[5] +;B[hh]BL[231.392] +;W[hi]WL[30]OW[5] +;B[gh]BL[222.232] +;W[gg]WL[30]OW[5] +;B[me]BL[211.701] +;W[gi]WL[30]OW[5] +;B[le]BL[209.231] +;W[rp]WL[30]OW[5] +;B[rq]BL[198.641] +;W[rr]WL[30]OW[5] +;B[sq]BL[170.326] +;W[qq]WL[30]OW[5] +;B[qp]BL[141.176] +;W[os]WL[30]OW[5] +;B[ps]BL[132.531] +;W[sr]WL[30]OW[5] +;B[qr]BL[121.165] +;W[bb]WL[30]OW[5] +;B[cc]BL[110.593] +;W[gd]WL[30]OW[5] +;B[be]BL[77.965] +;W[bf]WL[30]OW[5] +;B[ba]BL[73.905] +;W[ab]WL[30]OW[5] +;B[ac]BL[48.666] +;W[mq]WL[30]OW[5] +;B[lp]BL[44.392] +;W[ae]WL[30]OW[5] +;B[da]BL[38.655] +;W[lr]WL[30]OW[5] +;B[lo]BL[0.962] +;W[kr]WL[30]OW[5] +;B[jq]BL[30]OB[5] +;W[ip]WL[30]OW[5] +;B[jr]BL[30]OB[5] +;W[gp]WL[30]OW[5] +;B[fq]BL[30]OB[5] +;W[bm]WL[30]OW[5] +;B[am]BL[30]OB[5] +;W[gq]WL[30]OW[5] +;B[gr]BL[30]OB[5] +;W[bl]WL[30]OW[5] +;B[en]BL[30]OB[5] +;W[ln]WL[30]OW[5] +;B[mn]BL[30]OB[5] +;W[kn]WL[30]OW[5] +;B[mo]BL[30]OB[5] +;W[mb]WL[30]OW[5] +;B[mm]BL[30]OB[5] +;W[ml]WL[30]OW[5] +;B[lm]BL[30]OB[5] +;W[ll]WL[30]OW[5] +;B[km]BL[30]OB[5] +;W[kl]WL[30]OW[5] +;B[nj]BL[30]OB[5] +;W[fn]WL[30]OW[5] +;B[fo]BL[30]OB[5] +;W[em]WL[30]OW[5] +;B[do]BL[30]OB[5] +;W[cr]WL[30]OW[5] +;B[bq]BL[30]OB[5] +;W[er]WL[30]OW[5] +;B[fr]BL[30]OB[5] +;W[sd]WL[30]OW[5] +;B[rc]BL[30]OB[5] +;W[go]WL[30]OW[5] +;B[mk]BL[30]OB[5] +;W[lj]WL[30]OW[5] +;B[lk]BL[30]OB[5] +;W[kj]WL[30]OW[5] +;B[nk]BL[30]OB[5] +;W[kk]WL[30]OW[5] +;B[fg]BL[30]OB[5] +;W[hf]WL[30]OW[5] +;B[dj]BL[30]OB[5] +;W[ei]WL[30]OW[5] +;B[di]BL[30]OB[5] +;W[dh]WL[30]OW[5] +;B[ns]BL[30]OB[5] +;W[al]WL[30]OW[5] +;B[an]BL[30]OB[5] +;W[br]WL[30]OW[5] +;B[ar]BL[30]OB[5] +;W[cq]WL[30]OW[5] +;B[bp]BL[30]OB[5] +;W[na]WL[30]OW[5] +;B[oa]BL[30]OB[5] +;W[el]WL[30]OW[5] +;B[of]BL[30]OB[5] +;W[no]WL[30]OW[5] +;B[ms]BL[30]OB[5] +;W[cj]WL[30]OW[5] +;B[dn]BL[30]OB[5] +;W[mr]WL[30]OW[5] +;B[mp]BL[30]OB[5] +;W[ga]WL[30]OW[5] +;B[pg]BL[30]OB[5] +;W[js]WL[30]OW[5] +;B[is]BL[30]OB[5] +;W[ls]WL[30]OW[5] +;B[pe]BL[30]OB[5] +;W[os]WL[30]OW[5] +;B[ns]BL[30]OB[5] +;W[oi]WL[30]OW[5] +;B[qe]BL[30]OB[5] +;W[ph]WL[30]OW[5] +;B[pf]BL[30]OB[5] +;W[nm]WL[30]OW[5] +;B[sc]BL[30]OB[5] +;W[sf]WL[30]OW[5] +;B[sm]BL[30]OB[5] +;W[oj]WL[30]OW[5] +;B[nh]BL[30]OB[5] +;W[ms]WL[30]OW[5] +;B[ks]BL[30]OB[5] +;W[nq]WL[30]OW[5] +;B[js]BL[30]OB[5] +;W[dq]WL[30]OW[5] +;B[bc]BL[30]OB[4] +;W[os]WL[30]OW[5] +;B[ki]BL[30]OB[4] +;W[sh]WL[30]OW[5] +;B[sj]BL[30]OB[3] +;W[ji]WL[30]OW[5] +;B[ns]BL[30]OB[3] +;W[ds]WL[30]OW[5] +;B[bs]BL[30]OB[3] +;W[os]WL[30]OW[5] +;B[he]BL[30]OB[3] +;W[ge]WL[30]OW[5] +;B[ns]BL[30]OB[3] +;W[aq]WL[30]OW[5] +;B[ap]BL[30]OB[3] +;W[os]WL[30]OW[5] +;B[gf]BL[30]OB[3] +;W[he]WL[30]OW[5] +;B[ns]BL[30]OB[3] +;W[pa]WL[30]OW[5] +;B[qa]BL[30]OB[3] +;W[os]WL[30]OW[5] +;B[gl]BL[30]OB[3] +;W[fi]WL[30]OW[5] +;B[ns]BL[30]OB[3] +;W[sp]WL[30]OW[5] +;B[so]BL[30]OB[3] +;W[os]WL[30]OW[5] +;B[jn]BL[30]OB[3] +;W[in]WL[30]OW[5] +;B[ns]BL[30]OB[3] +;W[ep]WL[30]OW[5] +;B[eo]BL[30]OB[3] +;W[os]WL[30]OW[5] +;B[ad]BL[30]OB[3] +;W[af]WL[30]OW[5] +;B[ns]BL[30]OB[3] +;W[np]WL[30]OW[5] +;B[os]BL[30]OB[3] +;W[ea]WL[30]OW[5] +;B[fa]BL[30]OB[3] +;W[nn]WL[30]OW[5] +;B[aa]BL[30]OB[3] +;W[ea]WL[30]OW[5] +;B[jb]BL[30]OB[3]) diff --git a/jni/pachi/t-regress/games/2011-06-13-pachi30s-Jep.sgf b/jni/pachi/t-regress/games/2011-06-13-pachi30s-Jep.sgf new file mode 100644 index 0000000..c767230 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-13-pachi30s-Jep.sgf @@ -0,0 +1,188 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]HA[5]KM[0.50]TM[1800]OT[5x30 byo-yomi] +PW[pachi30s]PB[Jep]WR[1d]BR[5k]DT[2011-06-13]GC[104 +Q3:bad!{ladder} moves 102..108 playing a broken ladder]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi30s [1d\]: GTP Engine for pachi30s (white): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Resign] +;B[pp]BL[1797.56] +;B[dp]BL[1795.465] +;B[dd]BL[1793.98] +;B[pd]BL[1791.143] +;B[jj]BL[1788.249] +;W[ic]WL[1797.602] +;B[gc]BL[1755.819] +;W[pn]WL[1714.567] +;B[nq]BL[1730.722] +;W[ch]WL[1631.67] +;B[ql]BL[1673.051] +;W[qj]WL[1546.717] +;B[ol]BL[1645.586] +;W[dm]WL[1464.228] +;B[jp]BL[1615.996]C[Jep [5k\]: winrate +] +;W[oj]WL[1378.576] +;B[pi]BL[1565.337] +;W[pj]WL[1350.825] +;B[qg]BL[1561.324] +;W[mj]WL[1277.04] +;B[ml]BL[1548.624] +;W[lk]WL[1249.561] +;B[qp]BL[1535.535] +;W[jl]WL[1167.415] +;B[lm]BL[1479.199] +;W[ig]WL[1084.741] +;B[cd]BL[1398.785] +;W[hp]WL[1056.768] +;B[io]BL[1356.133] +;W[gn]WL[1026.085] +;B[cq]BL[1336.24] +;W[nd]WL[950.346] +;B[nc]BL[1309.747] +;W[mc]WL[922.25] +;B[oc]BL[1307.419] +;W[md]WL[892.11] +;B[ge]BL[1208.15] +;W[co]WL[808.089] +;B[fq]BL[1200.255] +;W[ho]WL[779.821] +;B[fh]BL[1139.185] +;W[kn]WL[709.981] +;B[lo]BL[1122.791] +;W[fj]WL[630.273] +;B[bp]BL[1054.26] +;W[pl]WL[550.36] +;B[pm]BL[1037.028] +;W[cf]WL[522.408] +;B[bn]BL[978.857] +;W[bo]WL[435.795] +;B[ao]BL[975.164] +;W[cl]WL[354.842] +;B[gg]BL[950.352] +;W[ie]WL[272.63] +;B[hi]BL[925.971] +;W[gb]WL[215.617] +;B[fb]BL[896.938] +;W[qe]WL[140.814] +;B[pe]BL[853.382] +;W[rc]WL[76.136] +;B[qc]BL[800.52] +;W[qb]WL[30]OW[5] +;B[qd]BL[751.136] +;W[rd]WL[30]OW[5] +;B[pb]BL[690.076] +;W[rb]WL[30]OW[5] +;B[mb]BL[658.27] +;W[lb]WL[30]OW[5] +;B[nb]BL[641.767] +;W[ln]WL[30]OW[5] +;B[mo]BL[618.525] +;W[mn]WL[30]OW[5] +;B[nn]BL[614.884] +;W[nm]WL[30]OW[5] +;B[om]BL[581.941] +;W[on]WL[30]OW[5] +;B[no]BL[572.867] +;W[nl]WL[30]OW[5] +;B[nk]BL[529.506] +;W[mm]WL[30]OW[5] +;B[ok]BL[516.151] +;W[qn]WL[30]OW[5] +;B[qm]BL[505.167] +;W[nj]WL[30]OW[5] +;B[rn]BL[485.026] +;W[ff]WL[30]OW[5] +;B[gf]BL[478.05] +;W[fd]WL[30]OW[5] +;B[gd]BL[435.912] +;W[hh]WL[30]OW[5] +;B[fi]BL[394.097] +;W[ro]WL[30]OW[5] +;B[rm]BL[386.57] +;W[gh]WL[30]OW[5] +;B[fg]BL[377.261] +;W[gi]WL[30]OW[5] +;B[ef]BL[314.955] +;W[qf]WL[30]OW[5] +;B[pf]BL[310.307] +;W[rp]WL[30]OW[5] +;B[rq]BL[264.594] +;W[qq]WL[30]OW[5] +;B[qr]BL[252.471] +;W[pq]WL[30]OW[5] +;B[oq]BL[235.986] +;W[pr]WL[30]OW[5] +;B[or]BL[223.018] +;W[ps]WL[30]OW[5] +;B[qs]BL[202.651] +;W[bd]WL[30]OW[5] +;B[bc]BL[196.779] +;W[od]WL[30]OW[5] +;B[rg]BL[178.739] +;W[se]WL[30]OW[5] +;B[ri]BL[159.173] +;W[qi]WL[30]OW[5] +;B[qh]BL[151.978] +;W[og]WL[30]OW[5] +;B[pg]BL[142.592] +;W[rj]WL[30]OW[5] +;B[sh]BL[103.744] +;W[sf]WL[30]OW[5] +;B[rf]BL[93.044] +;W[re]WL[30]OW[5] +;B[sg]BL[87.387] +;W[eb]WL[30]OW[5] +;B[hb]BL[76.373] +;W[fc]WL[30]OW[5] +;B[ga]BL[68.945] +;W[cc]WL[30]OW[5] +;B[bb]BL[56.235] +;W[de]WL[30]OW[5] +;B[ee]BL[43.892] +;W[cp]WL[30]OW[5] +;B[bq]BL[32.196] +;W[eq]WL[30]OW[5] +;B[er]BL[8.273] +;W[fr]WL[30]OW[5] +;B[ep]BL[30]OB[5] +;W[gq]WL[30]OW[5] +;B[fp]BL[30]OB[5] +;W[dr]WL[30]OW[5] +;B[es]BL[30]OB[5] +;W[dq]WL[30]OW[5] +;B[cr]BL[30]OB[5] +;W[rr]WL[30]OW[5] +;B[os]BL[30]OB[5] +;W[be]WL[30]OW[5] +;B[dc]BL[30]OB[5] +;W[db]WL[30]OW[5] +;B[cb]BL[30]OB[5] +;W[ko]WL[30]OW[5] +;B[kp]BL[30]OB[5] +;W[ma]WL[30]OW[5] +;B[jb]BL[30]OB[5] +;W[ib]WL[30]OW[5] +;B[oa]BL[30]OB[5] +;W[in]WL[30]OW[5] +;B[ej]BL[30]OB[5] +;W[of]WL[30]OW[5] +;B[cj]BL[30]OB[5] +;W[ei]WL[30]OW[5] +;B[di]BL[30]OB[5] +;W[ek]WL[30]OW[5] +;B[eh]BL[30]OB[5] +;W[fa]WL[30]OW[5] +;B[ea]BL[30]OB[5] +;W[bj]WL[30]OW[5] +;B[bi]BL[30]OB[5] +;W[ci]WL[30]OW[5] +;B[bk]BL[30]OB[5] +;W[aj]WL[30]OW[5] +;B[ai]BL[30]OB[5] +;W[bh]WL[30]OW[5] +;B[ak]BL[30]OB[5] +;W[ds]WL[30]OW[5] +;B[cs]BL[30]OB[5]C[Jep [4k\]: Yes :-) +jlg [-\]: thanks for the game +Jep [4k\]: thanks for watching +Jep [4k\]: cu :-) +Jep [4k\]: Was I actually winning? +Jep [4k\]: I thought I was loosing +]) diff --git a/jni/pachi/t-regress/games/2011-06-18-dorabon-pachi2-4.sgf b/jni/pachi/t-regress/games/2011-06-18-dorabon-pachi2-4.sgf new file mode 100644 index 0000000..a525c31 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-06-18-dorabon-pachi2-4.sgf @@ -0,0 +1,344 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]HA[4]KM[0.50]TM[0]OT[5x15 byo-yomi] +PW[dorabon]PB[pachi2]WR[5d]BR[3d]DT[2011-06-18]GC[222 B1:dead{semeai},+R2:alive{falseeye} pachi considers the bottom left semeai won by black (but black loses it) and the bottom right as undecided (white is clearly alive). +At move 270 L4 it still thinks it is leading by 80% although the game is lost.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [3d\]: GTP Engine for pachi2 (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Resign] +;B[dp]BL[15]OB[5] +;B[pd]BL[15]OB[5] +;B[dd]BL[15]OB[5] +;B[pp]BL[15]OB[5] +;W[qf]WL[15]OW[5] +;B[pf]BL[15]OB[5] +;W[pg]WL[15]OW[5] +;B[og]BL[15]OB[5] +;W[pe]WL[15]OW[5] +;B[of]BL[15]OB[5] +;W[qe]WL[15]OW[5] +;B[oe]BL[15]OB[5] +;W[ph]WL[15]OW[5] +;B[oh]BL[15]OB[5] +;W[pi]WL[15]OW[5] +;B[qd]BL[15]OB[5] +;W[od]WL[15]OW[5] +;B[nd]BL[15]OB[5] +;W[oc]WL[15]OW[5] +;B[nc]BL[15]OB[5] +;W[pb]WL[15]OW[5] +;B[qb]BL[15]OB[5] +;W[qc]WL[15]OW[5] +;B[rd]BL[15]OB[5] +;W[pc]WL[15]OW[5] +;B[rb]BL[15]OB[5] +;W[rc]WL[15]OW[5] +;B[sc]BL[15]OB[5] +;W[re]WL[15]OW[5] +;B[sd]BL[15]OB[5] +;W[sb]WL[15]OW[5] +;B[kh]BL[15]OB[5] +;W[ic]WL[15]OW[5] +;B[hc]BL[15]OB[5] +;W[hd]WL[15]OW[5] +;B[id]BL[15]OB[5] +;W[gc]WL[15]OW[5] +;B[hb]BL[15]OB[5] +;W[gd]WL[15]OW[5] +;B[jc]BL[15]OB[5] +;W[ie]WL[15]OW[5] +;B[jd]BL[15]OB[5] +;W[gb]WL[15]OW[5] +;B[ff]BL[15]OB[5] +;W[ee]WL[15]OW[5] +;B[df]BL[15]OB[5] +;W[de]WL[15]OW[5] +;B[ce]BL[15]OB[5] +;W[cf]WL[15]OW[5] +;B[cg]BL[15]OB[5] +;W[cd]WL[15]OW[4] +;B[bf]BL[15]OB[5] +;W[cc]WL[15]OW[4] +;B[bd]BL[15]OB[5] +;W[bc]WL[15]OW[4] +;B[dc]BL[15]OB[5] +;W[db]WL[15]OW[4] +;B[kq]BL[15]OB[5] +;W[cn]WL[15]OW[4] +;B[cl]BL[15]OB[5] +;W[dl]WL[15]OW[4] +;B[dk]BL[15]OB[5] +;W[dm]WL[15]OW[4] +;B[ek]BL[15]OB[5] +;W[fp]WL[15]OW[4] +;B[fm]BL[15]OB[5] +;W[eo]WL[15]OW[3] +;B[hq]BL[15]OB[5] +;W[dq]WL[15]OW[3] +;B[cq]BL[15]OB[5] +;W[eq]WL[15]OW[3] +;B[bp]BL[15]OB[5] +;W[co]WL[15]OW[3] +;B[cp]BL[15]OB[5] +;W[fn]WL[15]OW[2] +;B[gn]BL[15]OB[5] +;W[br]WL[15]OW[2] +;B[cr]BL[15]OB[5] +;W[dr]WL[15]OW[2] +;B[go]BL[15]OB[5] +;W[ep]WL[15]OW[2] +;B[he]BL[15]OB[5] +;W[ge]WL[15]OW[2] +;B[hf]BL[15]OB[5] +;W[gf]WL[15]OW[2] +;B[gg]BL[15]OB[5] +;W[hg]WL[15]OW[2] +;B[if]BL[15]OB[5] +;W[fg]WL[15]OW[2] +;B[gh]BL[15]OB[5] +;W[ef]WL[15]OW[2] +;B[ml]BL[15]OB[5] +;W[fh]WL[15]OW[1] +;B[fi]BL[15]OB[5] +;W[ei]WL[15]OW[1]C[Ootakamoku [1d\]: e11 loosk aji keshi +] +;B[gi]BL[15]OB[5] +;W[dh]WL[15]OW[1] +;B[cj]BL[15]OB[5] +;W[oi]WL[15]OW[1] +;B[do]BL[15]OB[5] +;W[dn]WL[15]OW[1] +;B[bo]BL[15]OB[5] +;W[bn]WL[15]OW[1] +;B[bs]BL[15]OB[5] +;W[ar]WL[15]OW[1] +;B[fr]BL[15]OB[5] +;W[ds]WL[15]OW[1] +;B[cs]BL[15]OB[5] +;W[ao]WL[15]OW[1] +;B[ap]BL[15]OB[5] +;W[an]WL[15]OW[1] +;B[gq]BL[15]OB[5] +;W[mi]WL[15]OW[1] +;B[sa]BL[15]OB[5] +;W[se]WL[15]OW[1] +;B[sb]BL[15]OB[5] +;W[pa]WL[15]OW[1] +;B[qm]BL[15]OB[5] +;W[nh]WL[15]OW[1] +;B[mg]BL[15]OB[5] +;W[ki]WL[15]OW[1] +;B[li]BL[15]OB[5] +;W[lj]WL[15]OW[1] +;B[lh]BL[15]OB[5] +;W[mj]WL[15]OW[1] +;B[kl]BL[15]OB[5] +;W[jj]WL[15]OW[1] +;B[ji]BL[15]OB[5] +;W[kj]WL[15]OW[1] +;B[ik]BL[15]OB[5] +;W[ii]WL[15]OW[1] +;B[ij]BL[15]OB[5] +;W[jh]WL[15]OW[1] +;B[jg]BL[15]OB[5] +;W[qq]WL[15]OW[1] +;B[pq]BL[15]OB[5] +;W[qp]WL[15]OW[1] +;B[qo]BL[15]OB[5] +;W[ro]WL[15]OW[1] +;B[rn]BL[15]OB[5] +;W[po]WL[15]OW[1] +;B[qn]BL[15]OB[5] +;W[pr]WL[15]OW[1] +;B[or]BL[15]OB[5] +;W[qr]WL[15]OW[1] +;B[nq]BL[15]OB[5] +;W[rp]WL[15]OW[1] +;B[eg]BL[15]OB[5] +;W[eh]WL[15]OW[1] +;B[ol]BL[15]OB[5] +;W[qk]WL[15]OW[1] +;B[dg]BL[15]OB[5] +;W[ch]WL[15]OW[1] +;B[qg]BL[15]OB[5] +;W[rg]WL[15]OW[1] +;B[ed]BL[15]OB[5] +;W[fe]WL[15]OW[1] +;B[cb]BL[15]OB[5] +;W[bb]WL[15]OW[1] +;B[em]BL[15]OB[5] +;W[en]WL[15]OW[1] +;B[bh]BL[15]OB[5] +;W[nb]WL[15]OW[1] +;B[eb]BL[15]OB[5] +;W[ca]WL[15]OW[1] +;B[qh]BL[15]OB[5] +;W[qi]WL[15]OW[1] +;B[mb]BL[15]OB[5] +;W[ma]WL[15]OW[1] +;B[rh]BL[15]OB[5] +;W[ri]WL[15]OW[1] +;B[sh]BL[15]OB[5] +;W[si]WL[15]OW[1] +;B[la]BL[15]OB[5] +;W[na]WL[15]OW[1] +;B[ea]BL[15]OB[5] +;W[da]WL[15]OW[1] +;B[ib]BL[15]OB[5] +;W[lb]WL[15]OW[1] +;B[lc]BL[15]OB[5] +;W[mc]WL[15]OW[1] +;B[ga]BL[15]OB[5] +;W[ec]WL[15]OW[1] +;B[fc]BL[15]OB[5] +;W[fd]WL[15]OW[1] +;B[fa]BL[15]OB[5] +;W[ec]WL[15]OW[1] +;B[md]BL[15]OB[5] +;W[ld]WL[15]OW[1]C[Hendrik [10k\]: why not b f14 just now? +] +;B[kb]BL[15]OB[5] +;W[kc]WL[15]OW[1] +;B[le]BL[15]OB[5] +;W[ka]WL[15]OW[1] +;B[kd]BL[15]OB[5]C[goeoekun [26k?\]: Ootakamoku +] +;W[ng]WL[15]OW[1]C[goeoekun [26k?\]: hi +] +;B[mf]BL[15]OB[5]C[goeoekun [26k?\]: bye +] +;W[nf]WL[15]OW[1] +;B[lc]BL[15]OB[5] +;W[ne]WL[15]OW[1] +;B[rr]BL[15]OB[5] +;W[rs]WL[15]OW[1] +;B[sr]BL[15]OB[5] +;W[sq]WL[15]OW[1] +;B[so]BL[15]OB[5] +;W[ps]WL[15]OW[1] +;B[ql]BL[15]OB[5] +;W[rk]WL[15]OW[1] +;B[bi]BL[15]OB[5] +;W[jk]WL[15]OW[1] +;B[jl]BL[15]OB[5] +;W[hj]WL[15]OW[1] +;B[hk]BL[15]OB[5] +;W[gj]WL[15]OW[1] +;B[fj]BL[15]OB[5] +;W[hi]WL[15]OW[1] +;B[gk]BL[15]OB[5] +;W[rl]WL[15]OW[1] +;B[oo]BL[15]OB[5] +;W[ad]WL[15]OW[1] +;B[be]BL[15]OB[5] +;W[pk]WL[15]OW[1] +;B[hr]BL[15]OB[5] +;W[ok]WL[15]OW[1] +;B[nk]BL[15]OB[5] +;W[nj]WL[15]OW[1] +;B[hh]BL[15]OB[5] +;W[pl]WL[15]OW[1] +;B[pm]BL[15]OB[5] +;W[bk]WL[15]OW[1] +;B[ih]BL[15]OB[5] +;W[ji]WL[15]OW[1] +;B[ak]BL[15]OB[5] +;W[aj]WL[15]OW[1] +;B[ck]BL[15]OB[5] +;W[fb]WL[15]OW[1] +;B[ha]BL[15]OB[5] +;W[mk]WL[15]OW[1] +;B[nl]BL[15]OB[5] +;W[os]WL[15]OW[1] +;B[mr]BL[15]OB[5] +;W[ns]WL[15]OW[1] +;B[hp]BL[15]OB[5] +;W[ms]WL[15]OW[1] +;B[ll]BL[15]OB[5] +;W[lr]WL[15]OW[1] +;B[lq]BL[15]OB[5] +;W[nr]WL[15]OW[1] +;B[kr]BL[15]OB[5] +;W[mq]WL[15]OW[1] +;B[np]BL[15]OB[5] +;W[mp]WL[15]OW[1] +;B[lp]BL[15]OB[5] +;W[mo]WL[15]OW[1] +;B[no]BL[15]OB[5] +;W[lo]WL[15]OW[1] +;B[ko]BL[15]OB[5] +;W[ln]WL[15]OW[1] +;B[mm]BL[15]OB[5] +;W[kn]WL[15]OW[1] +;B[jo]BL[15]OB[5] +;W[jn]WL[15]OW[1] +;B[jr]BL[15]OB[5] +;W[in]WL[15]OW[1] +;B[im]BL[15]OB[5] +;W[oq]WL[15]OW[1] +;B[op]BL[15]OB[5] +;W[bl]WL[15]OW[1] +;B[bj]BL[15]OB[5] +;W[al]WL[15]OW[1] +;B[kp]BL[15]OB[5] +;W[io]WL[15]OW[1] +;B[ir]BL[15]OB[5] +;W[mb]WL[15]OW[1] +;B[ld]BL[15]OB[5] +;W[ja]WL[15]OW[1] +;B[ig]BL[15]OB[5] +;W[ip]WL[15]OW[1] +;B[ks]BL[15]OB[5] +;W[jb]WL[15]OW[1] +;B[ic]BL[15]OB[5] +;W[sm]WL[15]OW[1] +;B[sl]BL[15]OB[5] +;W[rm]WL[15]OW[1] +;B[sn]BL[15]OB[5] +;W[nn]WL[15]OW[1] +;B[on]BL[15]OB[5] +;W[mn]WL[15]OW[1] +;B[jq]BL[15]OB[5] +;W[nm]WL[15]OW[1] +;B[om]BL[15]OB[5] +;W[hn]WL[15]OW[1] +;B[je]BL[15]OB[5] +;W[hm]WL[15]OW[1] +;B[hl]BL[15]OB[5] +;W[gm]WL[15]OW[1] +;B[is]BL[15]OB[5] +;W[aq]WL[15]OW[1] +;B[fq]BL[15]OB[5] +;W[bq]WL[15]OW[1] +;B[as]BL[15]OB[5] +;W[bq]WL[15]OW[1] +;B[ac]BL[15]OB[5] +;W[ab]WL[15]OW[1] +;B[bm]BL[15]OB[5] +;W[cm]WL[15]OW[1] +;B[es]BL[15]OB[5] +;W[aq]WL[15]OW[1] +;B[lf]BL[15]OB[5] +;W[gl]WL[15]OW[1] +;B[el]BL[15]OB[5] +;W[fl]WL[15]OW[1] +;B[fk]BL[15]OB[5] +;W[ae]WL[15]OW[1] +;B[jf]BL[15]OB[5] +;W[ar]WL[15]OW[1] +;B[br]BL[15]OB[5] +;W[aq]WL[15]OW[1] +;B[il]BL[15]OB[5] +;W[kc]WL[15]OW[1] +;B[af]BL[15]OB[5] +;W[kb]WL[15]OW[1] +;B[ai]BL[15]OB[5] +;W[bq]WL[15]OW[1] +;B[ak]BL[15]OB[5] +;W[am]WL[15]OW[1] +;B[er]BL[15]OB[5] +;W[ar]WL[15]OW[1] +;B[ac]BL[15]OB[5] +;W[ad]WL[15]OW[1] +;B[ae]BL[15]OB[5] +;W[ac]WL[15]OW[1] +;B[dd]BL[15]OB[5] +;W[rq]WL[15]OW[1]C[jlg [-\]: thanks for the game +]) diff --git a/jni/pachi/t-regress/games/2011-07-28-pachi2-Novicer.sgf b/jni/pachi/t-regress/games/2011-07-28-pachi2-Novicer.sgf new file mode 100644 index 0000000..ca1454e --- /dev/null +++ b/jni/pachi/t-regress/games/2011-07-28-pachi2-Novicer.sgf @@ -0,0 +1,325 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[pachi2]PB[Novicer]BR[2d]DT[2011-07-28]GC[128 +C2:alive{falseeye} False eye evaluation problem in bottom left. +252 +S11:bad,+B19:alive Pachi makes many stupid moves because it thinks the top left corner is unsettled although it is clearly alive. This shape (4 in a row) is so simple that it should get it right. +W 256 T12 bad +W 264 E19 doesn't make sense, but still 69% +W 266 H19 doesn't make sense, but still 61% +W 268 T9 should be T8 +W 292 A17 50%, W 294 R6 37% shows that top left was not evaluated correctly.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [-\]: GTP Engine for pachi2 (white): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Resign] +;B[pd]BL[15]OB[5] +;W[dp]WL[15]OW[5] +;B[pq]BL[15]OB[5] +;W[dd]WL[15]OW[5] +;B[fq]BL[15]OB[5] +;W[cn]WL[15]OW[5] +;B[fc]BL[15]OB[5] +;W[hc]WL[15]OW[5] +;B[fe]BL[15]OB[5] +;W[df]WL[15]OW[5] +;B[id]BL[15]OB[5] +;W[ic]WL[15]OW[5] +;B[jd]BL[15]OB[5] +;W[hd]WL[15]OW[5] +;B[he]BL[15]OB[5] +;W[jc]WL[15]OW[5] +;B[kd]BL[15]OB[5] +;W[lc]WL[15]OW[5] +;B[me]BL[15]OB[5] +;W[ge]WL[15]OW[5] +;B[gf]BL[15]OB[5] +;W[gd]WL[15]OW[5] +;B[hf]BL[15]OB[5] +;W[fd]WL[15]OW[5] +;B[jp]BL[15]OB[5] +;W[qj]WL[15]OW[5] +;B[qm]BL[15]OB[5] +;W[pl]WL[15]OW[5] +;B[pm]BL[15]OB[5] +;W[ol]WL[15]OW[5] +;B[om]BL[15]OB[5] +;W[nl]WL[15]OW[5] +;B[mm]BL[15]OB[5] +;W[nm]WL[15]OW[5] +;B[nn]BL[15]OB[5] +;W[mn]WL[15]OW[5] +;B[no]BL[15]OB[5] +;W[ln]WL[15]OW[5] +;B[ll]BL[15]OB[5] +;W[kl]WL[15]OW[5] +;B[kk]BL[15]OB[5] +;W[lm]WL[15]OW[5] +;B[ml]BL[15]OB[5] +;W[jk]WL[15]OW[5] +;B[kj]BL[15]OB[5] +;W[jm]WL[15]OW[5] +;B[jj]BL[15]OB[5] +;W[mj]WL[15]OW[5] +;B[ik]BL[15]OB[5] +;W[lk]WL[15]OW[5] +;B[jl]BL[15]OB[5] +;W[mk]WL[15]OW[5] +;B[cc]BL[15]OB[5] +;W[mp]WL[15]OW[5] +;B[fo]BL[15]OB[5] +;W[ck]WL[15]OW[5] +;B[cq]BL[15]OB[5] +;W[dq]WL[15]OW[5] +;B[dr]BL[15]OB[5] +;W[er]WL[15]OW[5] +;B[cr]BL[15]OB[5] +;W[eq]WL[15]OW[5] +;B[fr]BL[15]OB[5] +;W[cp]WL[15]OW[5] +;B[es]BL[15]OB[5] +;W[ds]WL[15]OW[5] +;B[fs]BL[15]OB[5] +;W[dc]WL[15]OW[5] +;B[bd]BL[15]OB[5] +;W[np]WL[15]OW[5] +;B[op]BL[15]OB[5] +;W[oq]WL[15]OW[5] +;B[or]BL[15]OB[5] +;W[nq]WL[15]OW[5] +;B[pp]BL[15]OB[5] +;W[cb]WL[15]OW[5] +;B[bb]BL[15]OB[5] +;W[cd]WL[15]OW[5] +;B[bc]BL[15]OB[5] +;W[db]WL[15]OW[5] +;B[bf]BL[15]OB[5] +;W[be]WL[15]OW[5] +;B[ce]BL[15]OB[5] +;W[cf]WL[15]OW[5] +;B[ae]BL[15]OB[5] +;W[bg]WL[15]OW[5] +;B[ba]BL[15]OB[5] +;W[fh]WL[15]OW[5] +;B[kc]BL[15]OB[5] +;W[kb]WL[15]OW[5] +;B[mb]BL[15]OB[5] +;W[lb]WL[15]OW[5] +;B[nc]BL[15]OB[5] +;W[hm]WL[15]OW[5] +;B[fk]BL[15]OB[5] +;W[hp]WL[15]OW[5] +;B[hq]BL[15]OB[5] +;W[nr]WL[15]OW[5] +;B[gh]BL[15]OB[5] +;W[iq]WL[15]OW[5] +;B[ip]BL[15]OB[5] +;W[hr]WL[15]OW[5] +;B[gq]BL[15]OB[5] +;W[jq]WL[15]OW[5] +;B[kq]BL[15]OB[5] +;W[kr]WL[15]OW[5] +;B[kp]BL[15]OB[5] +;W[lr]WL[15]OW[5] +;B[ho]BL[15]OB[5] +;W[en]WL[15]OW[5] +;B[bp]BL[15]OB[5] +;W[bo]WL[15]OW[5] +;B[aq]BL[15]OB[5] +;W[qg]WL[15]OW[5] +;B[qe]BL[15]OB[5] +;W[ql]WL[15]OW[5] +;B[ph]BL[15]OB[5] +;W[qh]WL[15]OW[5] +;B[pg]BL[15]OB[5] +;W[de]WL[15]OW[5] +;B[be]BL[15]OB[5] +;W[gi]WL[15]OW[5] +;B[hi]BL[15]OB[5] +;W[gg]WL[15]OW[5] +;B[hh]BL[15]OB[5] +;W[br]WL[15]OW[5] +;B[bs]BL[15]OB[5] +;W[ff]WL[15]OW[5] +;B[fi]BL[15]OB[5] +;W[ei]WL[15]OW[5] +;B[fj]BL[15]OB[5] +;W[mh]WL[15]OW[5] +;B[pi]BL[15]OB[5] +;W[qf]WL[15]OW[5] +;B[pf]BL[15]OB[5] +;W[gn]WL[15]OW[5] +;B[go]BL[15]OB[5] +;W[qi]WL[15]OW[5] +;B[rm]BL[15]OB[5] +;W[ld]WL[15]OW[5] +;B[le]BL[15]OB[5] +;W[ke]WL[15]OW[5] +;B[ie]BL[15]OB[5] +;W[hg]WL[15]OW[5] +;B[ig]BL[15]OB[5] +;W[fg]WL[15]OW[5] +;B[lg]BL[15]OB[5] +;W[jf]WL[15]OW[5] +;B[if]BL[15]OB[5] +;W[fn]WL[15]OW[5] +;B[el]BL[15]OB[4] +;W[gl]WL[15]OW[5] +;B[dj]BL[15]OB[4] +;W[ih]WL[15]OW[5] +;B[jg]BL[15]OB[4] +;W[gj]WL[15]OW[5] +;B[gk]BL[15]OB[4] +;W[hj]WL[15]OW[5] +;B[hk]BL[15]OB[4] +;W[ii]WL[15]OW[5] +;B[kh]BL[15]OB[4] +;W[cj]WL[15]OW[5] +;B[di]BL[15]OB[4] +;W[ki]WL[15]OW[5] +;B[ji]BL[15]OB[4] +;W[dk]WL[15]OW[5] +;B[ej]BL[15]OB[4] +;W[pr]WL[15]OW[5] +;B[qr]BL[15]OB[4] +;W[ps]WL[15]OW[5] +;B[rq]BL[15]OB[4] +;W[ch]WL[15]OW[5] +;B[li]BL[15]OB[4] +;W[mi]WL[15]OW[5] +;B[rf]BL[15]OB[4] +;W[rg]WL[15]OW[5] +;B[re]BL[15]OB[4] +;W[fl]WL[15]OW[5] +;B[ek]BL[15]OB[4] +;W[dl]WL[15]OW[5] +;B[sj]BL[15]OB[4] +;W[io]WL[15]OW[5] +;B[ar]BL[15]OB[4] +;W[mc]WL[15]OW[5] +;B[nb]BL[15]OB[4] +;W[oc]WL[15]OW[5] +;B[nd]BL[15]OB[4] +;W[rk]WL[15]OW[5] +;B[sk]BL[15]OB[4] +;W[rl]WL[15]OW[5] +;B[sl]BL[15]OB[4] +;W[jo]WL[15]OW[5] +;B[ca]BL[15]OB[4] +;W[da]WL[15]OW[5] +;B[ag]BL[15]OB[4] +;W[bh]WL[15]OW[5] +;B[ah]BL[15]OB[4] +;W[ai]WL[15]OW[5] +;B[af]BL[15]OB[4] +;W[ci]WL[15]OW[5] +;B[mg]BL[15]OB[4] +;W[eo]WL[15]OW[5] +;B[nh]BL[15]OB[4] +;W[pj]WL[15]OW[5] +;B[ni]BL[15]OB[4] +;W[ko]WL[15]OW[5] +;B[em]BL[15]OB[4] +;W[eh]WL[15]OW[5] +;B[dm]BL[15]OB[4] +;W[lq]WL[15]OW[5] +;B[gp]BL[15]OB[4] +;W[cm]WL[15]OW[5] +;B[qs]BL[15]OB[4] +;W[os]WL[15]OW[5] +;B[oj]BL[15]OB[4] +;W[sf]WL[15]OW[5] +;B[se]BL[15]OB[4] +;W[lp]WL[15]OW[5] +;B[hp]BL[15]OB[4] +;W[ma]WL[15]OW[5] +;B[na]BL[15]OB[4] +;W[on]WL[15]OW[5] +;B[oo]BL[15]OB[4] +;W[rj]WL[15]OW[5] +;B[si]BL[15]OB[4] +;W[il]WL[15]OW[5] +;B[jk]BL[15]OB[4] +;W[sg]WL[15]OW[5] +;B[dh]BL[15]OB[4] +;W[hn]WL[15]OW[5] +;B[ir]BL[15]OB[4] +;W[jr]WL[15]OW[5] +;B[dg]BL[15]OB[4] +;W[ok]WL[15]OW[5] +;B[hl]BL[15]OB[4] +;W[nj]WL[15]OW[5] +;B[fm]BL[15]OB[4] +;W[oi]WL[15]OW[5] +;B[gm]BL[15]OB[4] +;W[oh]WL[15]OW[5] +;B[ng]BL[15]OB[4] +;W[sm]WL[15]OW[5] +;B[sn]BL[15]OB[4] +;W[og]WL[15]OW[5] +;B[of]BL[15]OB[4] +;W[oj]WL[15]OW[5] +;B[dn]BL[15]OB[4] +;W[im]WL[15]OW[5] +;B[do]BL[15]OB[4] +;W[co]WL[15]OW[5] +;B[la]BL[15]OB[3] +;W[ri]WL[15]OW[5] +;B[ja]BL[15]OB[2] +;W[jb]WL[15]OW[5] +;B[ka]BL[15]OB[2] +;W[sh]WL[15]OW[5] +;B[ia]BL[15]OB[1] +;W[sm]WL[15]OW[5] +;B[gb]BL[15]OB[1] +;W[pe]WL[15]OW[5] +;B[oe]BL[15]OB[1] +;W[fb]WL[15]OW[5] +;B[fa]BL[15]OB[1] +;W[ea]WL[15]OW[5] +;B[eb]BL[15]OB[1] +;W[ha]WL[15]OW[5] +;B[hb]BL[15]OB[1] +;W[sk]WL[15]OW[5] +;B[rn]BL[15]OB[1] +;W[ib]WL[15]OW[5] +;B[ga]BL[15]OB[1] +;W[rc]WL[15]OW[5] +;B[ma]BL[15]OB[1] +;W[qb]WL[15]OW[5] +;B[pb]BL[15]OB[1] +;W[pc]WL[15]OW[5] +;B[pa]BL[15]OB[1] +;W[oa]WL[15]OW[5] +;B[ob]BL[15]OB[1] +;W[qp]WL[15]OW[5] +;B[qq]BL[15]OB[1] +;W[qo]WL[15]OW[5] +;B[po]BL[15]OB[1] +;W[qa]WL[15]OW[5] +;B[rd]BL[15]OB[1] +;W[sc]WL[15]OW[5] +;B[cs]BL[15]OB[1] +;W[kg]WL[15]OW[5] +;B[kf]BL[15]OB[1] +;W[lh]WL[15]OW[5] +;B[lj]BL[15]OB[1] +;W[ac]WL[15]OW[5] +;B[ab]BL[15]OB[1] +;W[qn]WL[15]OW[5] +;B[ao]BL[15]OB[1] +;W[pn]WL[15]OW[5] +;B[rp]BL[15]OB[1] +;W[so]WL[15]OW[5] +;B[ro]BL[15]OB[1] +;W[in]WL[15]OW[5] +;B[an]BL[15]OB[1] +;W[jh]WL[15]OW[5] +;B[al]BL[15]OB[1] +;W[bk]WL[15]OW[5] +;B[aj]BL[15]OB[1] +;W[bi]WL[15]OW[5] +;B[ak]BL[15]OB[1] +;W[ec]WL[15]OW[5] +;B[fb]BL[15]OB[1] +;W[gc]WL[15]OW[5] +;B[ha]BL[15]OB[1] +;W[qc]WL[15]OW[5] +;B[ij]BL[15]OB[1] +;W[hh]WL[15]OW[5] +;B[hs]BL[15]OB[1]) diff --git a/jni/pachi/t-regress/games/2011-07-28-pachi2-pkunzip-2.sgf b/jni/pachi/t-regress/games/2011-07-28-pachi2-pkunzip-2.sgf new file mode 100644 index 0000000..a738cac --- /dev/null +++ b/jni/pachi/t-regress/games/2011-07-28-pachi2-pkunzip-2.sgf @@ -0,0 +1,303 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[pachi2]PB[pkunzip]BR[3d]DT[2011-07-28]GC[132 /C9:bad!{ladder} the group can be caught +134 +B8:bad!{ladder} straightforward ladder +Pachi thought it was winning 72% at W 198 M3 but lost the game. (Seems like just a purely bad endgame play.)]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [-\]: GTP Engine for pachi2 (white): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Resign] +;B[pd]BL[15]OB[5] +;W[dp]WL[15]OW[5] +;B[pp]BL[15]OB[5] +;W[dc]WL[15]OW[5] +;B[de]BL[15]OB[5] +;W[ce]WL[15]OW[5] +;B[cf]BL[15]OB[5] +;W[cd]WL[15]OW[5] +;B[df]BL[15]OB[5] +;W[fc]WL[15]OW[5] +;B[cj]BL[15]OB[5] +;W[cl]WL[15]OW[5] +;B[pj]BL[15]OB[5] +;W[jq]WL[15]OW[5] +;B[ej]BL[15]OB[5] +;W[el]WL[15]OW[5] +;B[cq]BL[15]OB[5] +;W[dq]WL[15]OW[5] +;B[cp]BL[15]OB[5] +;W[do]WL[15]OW[5] +;B[bn]BL[15]OB[5] +;W[cr]WL[15]OW[5] +;B[br]BL[15]OB[5] +;W[mq]WL[15]OW[5] +;B[dr]BL[15]OB[5] +;W[er]WL[15]OW[5] +;B[cs]BL[15]OB[5] +;W[mc]WL[15]OW[5] +;B[oq]BL[15]OB[5] +;W[oe]WL[15]OW[5] +;B[qf]BL[15]OB[5] +;W[og]WL[15]OW[5] +;B[oc]BL[15]OB[5] +;W[ik]WL[15]OW[5] +;B[ph]BL[15]OB[5] +;W[lf]WL[15]OW[5] +;B[kc]BL[15]OB[5] +;W[mn]WL[15]OW[5] +;B[mb]BL[15]OB[5] +;W[lb]WL[15]OW[5] +;B[lc]BL[15]OB[5] +;W[nb]WL[15]OW[5] +;B[md]BL[15]OB[5] +;W[nc]WL[15]OW[5] +;B[le]BL[15]OB[5] +;W[nd]WL[15]OW[5] +;B[kf]BL[15]OB[5] +;W[me]WL[15]OW[5] +;B[ld]BL[15]OB[5] +;W[kb]WL[15]OW[5] +;B[lg]BL[15]OB[5] +;W[mf]WL[15]OW[5] +;B[kg]BL[15]OB[5] +;W[fk]WL[15]OW[5] +;B[jc]BL[15]OB[5] +;W[pn]WL[15]OW[5] +;B[ob]BL[15]OB[5] +;W[od]WL[15]OW[5] +;B[qc]BL[15]OB[5] +;W[pe]WL[15]OW[5] +;B[qe]BL[15]OB[5] +;W[jb]WL[15]OW[5] +;B[ll]BL[15]OB[5] +;W[qo]WL[15]OW[5] +;B[qp]BL[15]OB[5] +;W[ic]WL[15]OW[5] +;B[id]BL[15]OB[5] +;W[hd]WL[15]OW[5] +;B[ie]BL[15]OB[5] +;W[pl]WL[15]OW[5] +;B[ln]BL[15]OB[5] +;W[lm]WL[15]OW[5] +;B[km]BL[15]OB[5] +;W[mm]WL[15]OW[5] +;B[kk]BL[15]OB[5] +;W[in]WL[15]OW[5] +;B[ko]BL[15]OB[5] +;W[mp]WL[15]OW[5] +;B[jp]BL[15]OB[5] +;W[iq]WL[15]OW[5] +;B[ip]BL[15]OB[5] +;W[kp]WL[15]OW[5] +;B[lp]BL[15]OB[5] +;W[kq]WL[15]OW[5] +;B[jo]BL[15]OB[5] +;W[jl]WL[15]OW[5] +;B[kl]BL[15]OB[5] +;W[jn]WL[15]OW[5] +;B[kn]BL[15]OB[5] +;W[hp]WL[15]OW[5] +;B[hc]BL[15]OB[5] +;W[co]WL[15]OW[5] +;B[bo]BL[15]OB[5] +;W[ib]WL[15]OW[5] +;B[cm]BL[15]OB[5] +;W[jj]WL[15]OW[5] +;B[kj]BL[15]OB[5] +;W[ge]WL[15]OW[5] +;B[dm]BL[15]OB[5] +;W[em]WL[15]OW[5] +;B[dl]BL[15]OB[5] +;W[qr]WL[15]OW[5] +;B[pr]BL[15]OB[5] +;W[ki]WL[15]OW[5] +;B[li]BL[15]OB[5] +;W[ji]WL[15]OW[5] +;B[mj]BL[15]OB[5] +;W[lh]WL[15]OW[5] +;B[mh]BL[15]OB[5] +;W[qd]WL[15]OW[5] +;B[rd]BL[15]OB[5] +;W[rp]WL[15]OW[5] +;B[rq]BL[15]OB[5] +;W[rn]WL[15]OW[5] +;B[sp]BL[15]OB[5] +;W[ro]WL[15]OW[5] +;B[nr]BL[15]OB[5] +;W[bf]WL[15]OW[5] +;B[bg]BL[15]OB[5] +;W[ke]WL[15]OW[5] +;B[je]BL[15]OB[5] +;W[kd]WL[15]OW[5] +;B[jd]BL[15]OB[5] +;W[kh]WL[15]OW[5] +;B[mg]BL[15]OB[5] +;W[dk]WL[15]OW[5] +;B[ek]BL[15]OB[5] +;W[dj]WL[15]OW[5] +;B[di]BL[15]OB[5] +;W[ni]WL[15]OW[5] +;B[mi]BL[15]OB[5] +;W[ck]WL[15]OW[5] +;B[bk]BL[15]OB[5] +;W[bl]WL[15]OW[5] +;B[bm]BL[15]OB[5] +;W[al]WL[15]OW[5] +;B[ak]BL[15]OB[5] +;W[pg]WL[15]OW[5] +;B[qg]BL[15]OB[5] +;W[qq]WL[15]OW[5] +;B[rr]BL[15]OB[5] +;W[fj]WL[15]OW[5] +;B[am]BL[15]OB[5] +;W[qj]WL[15]OW[5] +;B[qi]BL[15]OB[5] +;W[qk]WL[15]OW[5] +;B[be]BL[15]OB[5] +;W[bd]WL[15]OW[5] +;B[af]BL[15]OB[5] +;W[ri]WL[15]OW[5] +;B[rh]BL[15]OB[5] +;W[oi]WL[15]OW[5] +;B[pi]BL[15]OB[5] +;W[ig]WL[15]OW[5] +;B[he]BL[15]OB[5] +;W[gd]WL[15]OW[5] +;B[oh]BL[15]OB[5] +;W[fq]WL[15]OW[5] +;B[ad]BL[15]OB[5] +;W[ac]WL[15]OW[5] +;B[bc]BL[15]OB[5] +;W[ae]WL[15]OW[5] +;B[ho]BL[15]OB[5] +;W[bf]WL[15]OW[5] +;B[gp]BL[15]OB[5] +;W[hq]WL[15]OW[5] +;B[io]BL[15]OB[5] +;W[ag]WL[15]OW[5] +;B[bh]BL[15]OB[5] +;W[go]WL[15]OW[5] +;B[gn]BL[15]OB[5] +;W[fo]WL[15]OW[5] +;B[hn]BL[15]OB[5] +;W[gg]WL[15]OW[5] +;B[fi]BL[15]OB[5] +;W[gi]WL[15]OW[5] +;B[fh]BL[15]OB[5] +;W[hl]WL[15]OW[5] +;B[rj]BL[15]OB[5] +;W[rk]WL[15]OW[5] +;B[si]BL[15]OB[5] +;W[pb]WL[15]OW[5] +;B[qb]BL[15]OB[5] +;W[oa]WL[15]OW[5] +;B[pc]BL[15]OB[5] +;W[nk]WL[15]OW[5] +;B[nj]BL[15]OB[5] +;W[ok]WL[15]OW[5] +;B[oj]BL[15]OB[5] +;W[mr]WL[15]OW[5] +;B[fg]BL[15]OB[5] +;W[gh]WL[15]OW[5] +;B[fn]BL[15]OB[5] +;W[en]WL[15]OW[5] +;B[ms]BL[15]OB[5] +;W[ls]WL[15]OW[5] +;B[ns]BL[15]OB[5] +;W[lq]WL[15]OW[5] +;B[oo]BL[15]OB[5] +;W[pa]WL[15]OW[5] +;B[qa]BL[15]OB[5] +;W[na]WL[15]OW[5] +;B[jm]BL[15]OB[5] +;W[im]WL[15]OW[5] +;B[hm]BL[15]OB[5] +;W[il]WL[15]OW[5] +;B[gl]BL[15]OB[5] +;W[gk]WL[15]OW[5] +;B[on]BL[15]OB[5] +;W[om]WL[15]OW[5] +;B[ed]BL[15]OB[5] +;W[ec]WL[15]OW[5] +;B[nf]BL[15]OB[4] +;W[ne]WL[15]OW[5] +;B[ng]BL[15]OB[4] +;W[ah]WL[15]OW[5] +;B[ai]BL[15]OB[4] +;W[qs]WL[15]OW[5] +;B[ps]BL[15]OB[4] +;W[lo]WL[15]OW[5] +;B[jg]BL[15]OB[4] +;W[hf]WL[15]OW[5] +;B[jh]BL[15]OB[4] +;W[ih]WL[15]OW[5] +;B[jk]BL[15]OB[4] +;W[hj]WL[15]OW[5] +;B[of]BL[15]OB[4] +;W[sq]WL[15]OW[5] +;B[sr]BL[15]OB[4] +;W[fm]WL[15]OW[5] +;B[pf]BL[15]OB[4] +;W[ma]WL[15]OW[5] +;B[gm]BL[15]OB[4] +;W[af]WL[15]OW[5] +;B[mk]BL[15]OB[4] +;W[bi]WL[15]OW[5] +;B[aj]BL[15]OB[4] +;W[dd]WL[15]OW[5] +;B[ee]BL[15]OB[4] +;W[ff]WL[15]OW[5] +;B[ef]BL[15]OB[4] +;W[ei]WL[15]OW[5] +;B[eh]BL[15]OB[4] +;W[ch]WL[15]OW[5] +;B[cg]BL[15]OB[4] +;W[sk]WL[15]OW[5] +;B[ml]BL[15]OB[4] +;W[sj]WL[15]OW[5] +;B[nl]BL[15]OB[4] +;W[ri]WL[15]OW[5] +;B[sh]BL[15]OB[4] +;W[if]WL[15]OW[5] +;B[ol]BL[15]OB[4] +;W[pk]WL[15]OW[5] +;B[nm]BL[15]OB[4] +;W[es]WL[15]OW[5] +;B[pm]BL[15]OB[4] +;W[qm]WL[15]OW[5] +;B[po]BL[15]OB[4] +;W[om]WL[15]OW[5] +;B[nn]BL[15]OB[4] +;W[mo]WL[15]OW[5] +;B[pm]BL[15]OB[4] +;W[jf]WL[15]OW[5] +;B[ke]BL[15]OB[4] +;W[om]WL[15]OW[5] +;B[ii]BL[15]OB[4] +;W[ij]WL[15]OW[5] +;B[pm]BL[15]OB[4] +;W[ds]WL[15]OW[5] +;B[cr]BL[15]OB[4] +;W[om]WL[15]OW[5] +;B[la]BL[15]OB[4] +;W[ka]WL[15]OW[5] +;B[pm]BL[15]OB[4] +;W[rs]WL[15]OW[5] +;B[pq]BL[15]OB[4] +;W[om]WL[15]OW[5] +;B[fp]BL[15]OB[4] +;W[eo]WL[15]OW[5] +;B[pm]BL[15]OB[4] +;W[so]WL[15]OW[5] +;B[ss]BL[15]OB[4] +;W[om]WL[15]OW[5] +;B[lr]BL[15]OB[4] +;W[kr]WL[15]OW[5] +;B[pm]BL[15]OB[4] +;W[qn]WL[15]OW[5] +;B[fe]BL[15]OB[4] +;W[om]WL[15]OW[5] +;B[gf]BL[15]OB[4] +;W[fd]WL[15]OW[5] +;B[np]BL[15]OB[4] +;W[ff]WL[15]OW[5] +;B[sq]BL[15]OB[4] +;W[gf]WL[15]OW[5] +;B[pm]BL[15]OB[4]) diff --git a/jni/pachi/t-regress/games/2011-08-08-Dallas-pachi2-2.sgf b/jni/pachi/t-regress/games/2011-08-08-Dallas-pachi2-2.sgf new file mode 100644 index 0000000..20174c3 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-08-08-Dallas-pachi2-2.sgf @@ -0,0 +1,193 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[Dallas]PB[pachi2]WR[3d]DT[2011-08-08]GC[115 +K17:bad!{ladder} pachi extends a losing ladder]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [-\]: GTP Engine for pachi2 (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Time] +;B[pp]BL[15]OB[5] +;W[dd]WL[15]OW[5] +;B[cp]BL[15]OB[5] +;W[fp]WL[15]OW[5] +;B[eq]BL[15]OB[5] +;W[pd]WL[15]OW[5] +;B[ep]BL[15]OB[5] +;W[fo]WL[15]OW[5] +;B[gr]BL[15]OB[5] +;W[qn]WL[15]OW[5] +;B[pl]BL[15]OB[5] +;W[on]WL[15]OW[5] +;B[mn]BL[15]OB[5] +;W[no]WL[15]OW[5] +;B[nm]BL[15]OB[5] +;W[nq]WL[15]OW[5] +;B[lp]BL[15]OB[5] +;W[om]WL[15]OW[5] +;B[ol]BL[15]OB[5] +;W[mo]WL[15]OW[5] +;B[lo]BL[15]OB[5] +;W[nl]WL[15]OW[5] +;B[nk]BL[15]OB[5] +;W[ml]WL[15]OW[5] +;B[ln]BL[15]OB[5] +;W[mk]WL[15]OW[5] +;B[nj]BL[15]OB[5] +;W[mj]WL[15]OW[5] +;B[ni]BL[15]OB[5] +;W[mi]WL[15]OW[5] +;B[nh]BL[15]OB[5] +;W[pj]WL[15]OW[5] +;B[mh]BL[15]OB[5] +;W[ph]WL[15]OW[5] +;B[rl]BL[15]OB[5] +;W[qm]WL[15]OW[5] +;B[ql]BL[15]OB[5] +;W[qj]WL[15]OW[5] +;B[li]BL[15]OB[5] +;W[kk]WL[15]OW[5] +;B[jk]BL[15]OB[5] +;W[kj]WL[15]OW[5] +;B[ji]BL[15]OB[5] +;W[ki]WL[15]OW[4] +;B[kh]BL[15]OB[5] +;W[jj]WL[15]OW[4] +;B[pm]BL[15]OB[5] +;W[pn]WL[15]OW[4] +;B[ij]BL[15]OB[5] +;W[ik]WL[15]OW[4] +;B[ii]BL[15]OB[5] +;W[jl]WL[15]OW[4] +;B[ok]BL[15]OB[5] +;W[jh]WL[15]OW[4] +;B[im]BL[15]OB[5] +;W[hk]WL[15]OW[4] +;B[gj]BL[15]OB[5] +;W[fl]WL[15]OW[3] +;B[jg]BL[15]OB[5] +;W[ih]WL[15]OW[3] +;B[gh]BL[15]OB[5] +;W[ig]WL[15]OW[3] +;B[jf]BL[15]OB[5] +;W[if]WL[15]OW[3] +;B[ie]BL[15]OB[5] +;W[he]WL[15]OW[3] +;B[id]BL[15]OB[5] +;W[hd]WL[15]OW[3] +;B[hf]BL[15]OB[5] +;W[kg]WL[15]OW[3] +;B[lh]BL[15]OB[5] +;W[je]WL[15]OW[3] +;B[gk]BL[15]OB[5] +;W[gl]WL[15]OW[3] +;B[kf]BL[15]OB[5] +;W[ic]WL[15]OW[3] +;B[hl]BL[15]OB[5] +;W[jn]WL[15]OW[3] +;B[mp]BL[15]OB[5] +;W[np]WL[15]OW[3] +;B[in]BL[15]OB[5] +;W[io]WL[15]OW[3] +;B[ho]BL[15]OB[5] +;W[hm]WL[15]OW[3] +;B[jo]BL[15]OB[5] +;W[il]WL[15]OW[3] +;B[ip]BL[15]OB[5] +;W[ej]WL[15]OW[3] +;B[qq]BL[15]OB[5] +;W[rp]WL[15]OW[3] +;B[ro]BL[15]OB[5] +;W[qo]WL[15]OW[3] +;B[qp]BL[15]OB[5] +;W[rq]WL[15]OW[3] +;B[rr]BL[15]OB[5] +;W[rn]WL[15]OW[3] +;B[sq]BL[15]OB[5] +;W[so]WL[15]OW[3] +;B[or]BL[15]OB[5] +;W[nr]WL[15]OW[3] +;B[ek]BL[15]OB[5] +;W[fk]WL[15]OW[3] +;B[fj]BL[15]OB[5] +;W[dk]WL[15]OW[3] +;B[ei]BL[15]OB[5] +;W[el]WL[15]OW[3] +;B[jd]BL[15]OB[5] +;W[ke]WL[15]OW[3] +;B[lf]BL[15]OB[5] +;W[kd]WL[15]OW[3] +;B[pr]BL[15]OB[5] +;W[qs]WL[15]OW[3] +;B[oq]BL[15]OB[5] +;W[op]WL[15]OW[3] +;B[jc]BL[15]OB[5] +;W[jb]WL[15]OW[3] +;B[kc]BL[15]OB[5] +;W[lc]WL[15]OW[3] +;B[kb]BL[15]OB[5] +;W[lb]WL[15]OW[3] +;B[ka]BL[15]OB[5] +;W[la]WL[15]OW[3] +;B[hh]BL[15]OB[5] +;W[ja]WL[15]OW[3] +;B[nn]BL[15]OB[5] +;W[lr]WL[15]OW[3] +;B[kr]BL[15]OB[5] +;W[sr]WL[15]OW[2] +;B[qr]BL[15]OB[5] +;W[rs]WL[15]OW[2] +;B[ss]BL[15]OB[5] +;W[kq]WL[15]OW[2] +;B[jr]BL[15]OB[5] +;W[sr]WL[15]OW[2] +;B[ns]BL[15]OB[5] +;W[ms]WL[15]OW[2] +;B[ss]BL[15]OB[5] +;W[jq]WL[15]OW[2] +;B[ir]BL[15]OB[5] +;W[sr]WL[15]OW[2] +;B[sm]BL[15]OB[5] +;W[sn]WL[15]OW[2] +;B[ss]BL[15]OB[5] +;W[iq]WL[15]OW[2] +;B[hq]BL[15]OB[5] +;W[sr]WL[15]OW[2] +;B[ls]BL[15]OB[5] +;W[os]WL[15]OW[1] +;B[ss]BL[15]OB[5] +;W[mq]WL[15]OW[1] +;B[lq]BL[15]OB[5] +;W[ks]WL[15]OW[1] +;B[ps]BL[15]OB[5] +;W[ns]WL[15]OW[1] +;B[hg]BL[15]OB[5] +;W[ie]WL[15]OW[1] +;B[pf]BL[15]OB[5] +;W[qf]WL[15]OW[1] +;B[qg]BL[15]OB[5] +;W[pg]WL[15]OW[1] +;B[qe]BL[15]OB[5] +;W[of]WL[15]OW[1] +;B[pe]BL[15]OB[5] +;W[ng]WL[15]OW[1] +;B[og]BL[15]OB[5] +;W[oh]WL[15]OW[1] +;B[oe]BL[15]OB[5] +;W[ne]WL[15]OW[1] +;B[rj]BL[15]OB[5] +;W[ri]WL[15]OW[1] +;B[mf]BL[15]OB[5] +;W[rk]WL[15]OW[1] +;B[nf]BL[15]OB[5] +;W[og]WL[15]OW[1] +;B[md]BL[15]OB[5] +;W[nd]WL[15]OW[1] +;B[nc]BL[15]OB[5] +;W[od]WL[15]OW[1] +;B[qd]BL[15]OB[5] +;W[mc]WL[15]OW[1] +;B[qh]BL[15]OB[5] +;W[rf]WL[15]OW[1] +;B[rg]BL[15]OB[5] +;W[sj]WL[15]OW[1] +;B[re]BL[15]OB[5] +;W[me]WL[15]OW[1] +;B[qi]BL[15]OB[5] +;W[pi]WL[15]OW[1] +;B[sh]BL[15]OB[5]) diff --git a/jni/pachi/t-regress/games/2011-08-09-pachi2-BlueSpark.sgf b/jni/pachi/t-regress/games/2011-08-09-pachi2-BlueSpark.sgf new file mode 100644 index 0000000..7158a16 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-08-09-pachi2-BlueSpark.sgf @@ -0,0 +1,158 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[pachi2]PB[BlueSpark]BR[5k]DT[2011-08-09]GC[144 +Q17:bad!{ladder} pachi extends a losing ladder]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [-\]: GTP Engine for pachi2 (white): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Time] +;B[dp]BL[15]OB[5] +;W[pd]WL[15]OW[5] +;B[dd]BL[15]OB[5] +;W[pq]WL[15]OW[5] +;B[po]BL[15]OB[5] +;W[qo]WL[15]OW[5] +;B[pn]BL[15]OB[5] +;W[pp]WL[15]OW[5] +;B[qn]BL[15]OB[5] +;W[ro]WL[15]OW[5] +;B[rn]BL[15]OB[5] +;W[no]WL[15]OW[5] +;B[qf]BL[15]OB[5] +;W[nm]WL[15]OW[5] +;B[kq]BL[15]OB[5] +;W[pi]WL[15]OW[5] +;B[qj]BL[15]OB[5] +;W[qi]WL[15]OW[5] +;B[ok]BL[15]OB[5] +;W[pj]WL[15]OW[5] +;B[qk]BL[15]OB[4] +;W[pk]WL[15]OW[5] +;B[pl]BL[15]OB[4] +;W[ol]WL[15]OW[5] +;B[pm]BL[15]OB[3] +;W[ri]WL[15]OW[5] +;B[rj]BL[15]OB[3] +;W[nk]WL[15]OW[5] +;B[of]BL[15]OB[3] +;W[sj]WL[15]OW[5] +;B[sk]BL[15]OB[3] +;W[iq]WL[15]OW[5] +;B[nq]BL[15]OB[3] +;W[kp]WL[15]OW[5] +;B[lp]BL[15]OB[3] +;W[mp]WL[15]OW[5] +;B[lo]BL[15]OB[3] +;W[mq]WL[15]OW[5] +;B[jp]BL[15]OB[3] +;W[ip]WL[15]OW[5] +;B[io]BL[15]OB[3] +;W[ho]WL[15]OW[5] +;B[in]BL[15]OB[3] +;W[hn]WL[15]OW[5] +;B[gp]BL[15]OB[3] +;W[im]WL[15]OW[5] +;B[jn]BL[15]OB[3] +;W[hp]WL[15]OW[5] +;B[kl]BL[15]OB[3] +;W[jm]WL[15]OW[5] +;B[kn]BL[15]OB[2] +;W[km]WL[15]OW[5] +;B[lm]BL[15]OB[2] +;W[gm]WL[15]OW[5] +;B[ml]BL[15]OB[2] +;W[mk]WL[15]OW[5] +;B[lk]BL[15]OB[2] +;W[nl]WL[15]OW[5] +;B[nd]BL[15]OB[1] +;W[lj]WL[15]OW[5] +;B[kj]BL[15]OB[1] +;W[ki]WL[15]OW[5] +;B[li]BL[15]OB[1] +;W[mj]WL[15]OW[5] +;B[ji]BL[15]OB[1] +;W[kh]WL[15]OW[5] +;B[jj]BL[15]OB[1] +;W[cn]WL[15]OW[5] +;B[cl]BL[15]OB[1] +;W[dm]WL[15]OW[5] +;B[dl]BL[15]OB[1] +;W[el]WL[15]OW[5] +;B[di]BL[15]OB[1] +;W[cq]WL[15]OW[5] +;B[cp]BL[15]OB[1] +;W[dq]WL[15]OW[5] +;B[bq]BL[15]OB[1] +;W[br]WL[15]OW[5] +;B[bo]BL[15]OB[1] +;W[bn]WL[15]OW[5] +;B[eq]BL[15]OB[1] +;W[er]WL[15]OW[5] +;B[fr]BL[15]OB[1] +;W[aq]WL[15]OW[5] +;B[bp]BL[15]OB[1] +;W[dr]WL[15]OW[5] +;B[fq]BL[15]OB[1] +;W[eo]WL[15]OW[5] +;B[cs]BL[15]OB[1] +;W[bs]WL[15]OW[5] +;B[ar]BL[15]OB[1] +;W[fs]WL[15]OW[5] +;B[gs]BL[15]OB[1] +;W[ep]WL[15]OW[5] +;B[es]BL[15]OB[1] +;W[cr]WL[15]OW[5] +;B[ap]BL[15]OB[1] +;W[lc]WL[15]OW[5] +;B[oc]BL[15]OB[1] +;W[ne]WL[15]OW[5] +;B[od]BL[15]OB[1] +;W[oe]WL[15]OW[5] +;B[pe]BL[15]OB[1] +;W[md]WL[15]OW[5] +;B[qd]BL[15]OB[1] +;W[nf]WL[15]OW[5] +;B[gi]BL[15]OB[1] +;W[bl]WL[15]OW[5] +;B[ck]BL[15]OB[1] +;W[cj]WL[15]OW[5] +;B[bj]BL[15]OB[1] +;W[ci]WL[15]OW[5] +;B[dj]BL[15]OB[1] +;W[bk]WL[15]OW[5] +;B[ch]BL[15]OB[1] +;W[bi]WL[15]OW[5] +;B[bh]BL[15]OB[1] +;W[aj]WL[15]OW[5] +;B[ic]BL[15]OB[1] +;W[dh]WL[15]OW[5] +;B[dg]BL[15]OB[1] +;W[eh]WL[15]OW[5] +;B[ei]BL[15]OB[1] +;W[ig]WL[15]OW[5] +;B[gg]BL[15]OB[1] +;W[fh]WL[15]OW[5] +;B[eg]BL[15]OB[1] +;W[cg]WL[15]OW[5] +;B[bg]BL[15]OB[1] +;W[cf]WL[15]OW[5] +;B[fg]BL[15]OB[1] +;W[bf]WL[15]OW[5] +;B[gh]BL[15]OB[1] +;W[ah]WL[15]OW[5] +;B[fi]BL[15]OB[1] +;W[ag]WL[15]OW[5] +;B[dh]BL[15]OB[1] +;W[ql]WL[15]OW[5] +;B[si]BL[15]OB[1] +;W[rl]WL[15]OW[5] +;B[rm]BL[15]OB[1] +;W[rg]WL[15]OW[5] +;B[rf]BL[15]OB[1] +;W[pc]WL[15]OW[5] +;B[pb]BL[15]OB[1] +;W[qc]WL[15]OW[5] +;B[rc]BL[15]OB[1] +;W[qb]WL[15]OW[5] +;B[rb]BL[15]OB[1] +;W[qa]WL[15]OW[5] +;B[pa]BL[15]OB[1] +;W[rk]WL[15]OW[5]C[BlueSpark [5k\]: рь +BlueSpark [5k\]: hm +]) diff --git a/jni/pachi/t-regress/games/2011-08-09-somrak-pachi2-2.sgf b/jni/pachi/t-regress/games/2011-08-09-somrak-pachi2-2.sgf new file mode 100644 index 0000000..3df5b98 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-08-09-somrak-pachi2-2.sgf @@ -0,0 +1,193 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[somrak]PB[pachi2]WR[2d]DT[2011-08-09]GC[101 +Q14:bad!{ladder} pachi extends a losing ladder]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [-\]: GTP Engine for pachi2 (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+1.50] +;B[pp]BL[15]OB[5] +;W[qc]WL[15]OW[5] +;B[cp]BL[15]OB[5] +;W[cc]WL[15]OW[5] +;B[iq]BL[15]OB[5] +;W[jd]WL[15]OW[5] +;B[cj]BL[15]OB[5] +;W[qn]WL[15]OW[5] +;B[pl]BL[15]OB[5] +;W[on]WL[15]OW[5] +;B[nl]BL[15]OB[5] +;W[np]WL[15]OW[5] +;B[lp]BL[15]OB[5] +;W[op]WL[15]OW[4] +;B[pq]BL[15]OB[5] +;W[rp]WL[15]OW[4] +;B[po]BL[15]OB[5] +;W[pn]WL[15]OW[4] +;B[oo]BL[15]OB[5] +;W[no]WL[15]OW[4] +;B[nn]BL[15]OB[5] +;W[nm]WL[15]OW[4] +;B[mn]BL[15]OB[5] +;W[ol]WL[15]OW[4] +;B[ok]BL[15]OB[5] +;W[om]WL[15]OW[4] +;B[mm]BL[15]OB[5] +;W[nk]WL[15]OW[4] +;B[ml]BL[15]OB[5] +;W[oj]WL[15]OW[4] +;B[pk]BL[15]OB[5] +;W[ni]WL[15]OW[4] +;B[mk]BL[15]OB[5] +;W[nj]WL[15]OW[4] +;B[pj]BL[15]OB[5] +;W[pg]WL[15]OW[3] +;B[rq]BL[15]OB[5] +;W[ro]WL[15]OW[3] +;B[rm]BL[15]OB[5] +;W[qq]WL[15]OW[3] +;B[qr]BL[15]OB[5] +;W[qp]WL[15]OW[3] +;B[rr]BL[15]OB[5] +;W[or]WL[15]OW[3] +;B[pr]BL[15]OB[5] +;W[oq]WL[15]OW[3] +;B[rn]BL[15]OB[5] +;W[qo]WL[15]OW[3] +;B[li]BL[15]OB[5] +;W[oh]WL[15]OW[3] +;B[qm]BL[15]OB[5] +;W[eq]WL[15]OW[3] +;B[cm]BL[15]OB[5] +;W[gq]WL[15]OW[3] +;B[hp]BL[15]OB[5] +;W[gp]WL[15]OW[3] +;B[go]BL[15]OB[5] +;W[fo]WL[15]OW[3] +;B[gn]BL[15]OB[5] +;W[ir]WL[15]OW[3] +;B[jr]BL[15]OB[5] +;W[hr]WL[15]OW[3] +;B[cg]BL[15]OB[5] +;W[de]WL[15]OW[3] +;B[lg]BL[15]OB[5] +;W[le]WL[15]OW[3] +;B[kq]BL[15]OB[5] +;W[eg]WL[15]OW[3] +;B[dr]BL[15]OB[5] +;W[dq]WL[15]OW[3] +;B[cq]BL[15]OB[5] +;W[er]WL[15]OW[3] +;B[hi]BL[15]OB[5] +;W[hg]WL[15]OW[3] +;B[ei]BL[15]OB[5] +;W[mf]WL[15]OW[3] +;B[qh]BL[15]OB[5] +;W[qg]WL[15]OW[3] +;B[ce]BL[15]OB[5] +;W[cd]WL[15]OW[3] +;B[fn]BL[15]OB[5] +;W[kh]WL[15]OW[3] +;B[eo]BL[15]OB[5] +;W[cr]WL[15]OW[3] +;B[br]BL[15]OB[5] +;W[ds]WL[15]OW[3] +;B[ki]BL[15]OB[5] +;W[lh]WL[15]OW[3] +;B[jh]BL[15]OB[5] +;W[jg]WL[15]OW[3] +;B[mh]BL[15]OB[5] +;W[kg]WL[15]OW[3] +;B[mi]BL[15]OB[5] +;W[mg]WL[15]OW[3] +;B[rg]BL[15]OB[5] +;W[rf]WL[15]OW[3] +;B[rh]BL[15]OB[5] +;W[ii]WL[15]OW[3] +;B[qf]BL[15]OB[5] +;W[qe]WL[15]OW[3] +;B[pf]BL[15]OB[5] +;W[of]WL[15]OW[3] +;B[pe]BL[15]OB[5] +;W[pd]WL[15]OW[3] +;B[oe]BL[15]OB[5] +;W[ne]WL[15]OW[3] +;B[ij]BL[15]OB[5] +;W[hh]WL[15]OW[3] +;B[gi]BL[15]OB[5] +;W[cf]WL[15]OW[3] +;B[df]BL[15]OB[5] +;W[be]WL[15]OW[3] +;B[dg]BL[15]OB[5] +;W[ef]WL[15]OW[3] +;B[ih]BL[15]OB[5] +;W[ig]WL[15]OW[3] +;B[od]BL[15]OB[5] +;W[oc]WL[15]OW[3] +;B[ji]BL[15]OB[5] +;W[bg]WL[15]OW[3] +;B[hq]BL[15]OB[5] +;W[bs]WL[15]OW[3] +;B[fp]BL[15]OB[5] +;W[gr]WL[15]OW[3] +;B[fq]BL[15]OB[5] +;W[fr]WL[15]OW[3] +;B[nd]BL[15]OB[5] +;W[md]WL[15]OW[3] +;B[nc]BL[15]OB[5] +;W[nb]WL[15]OW[3] +;B[mc]BL[15]OB[5] +;W[lc]WL[15]OW[3] +;B[mb]BL[15]OB[5] +;W[lb]WL[15]OW[3] +;B[bi]BL[15]OB[5] +;W[bh]WL[15]OW[3] +;B[lr]BL[15]OB[5] +;W[ci]WL[15]OW[2] +;B[di]BL[15]OB[5] +;W[dh]WL[15]OW[2] +;B[ch]BL[15]OB[5] +;W[ci]WL[15]OW[2] +;B[ma]BL[15]OB[5] +;W[na]WL[15]OW[2] +;B[sf]BL[15]OB[5] +;W[re]WL[15]OW[2] +;B[ph]BL[15]OB[5] +;W[la]WL[15]OW[2] +;B[bq]BL[15]OB[5] +;W[ch]WL[15]OW[2] +;B[bk]BL[15]OB[5] +;W[dp]WL[15]OW[2] +;B[do]BL[15]OB[5] +;W[ai]WL[15]OW[2] +;B[pm]BL[15]OB[5] +;W[bj]WL[15]OW[2] +;B[bl]BL[15]OB[5] +;W[fh]WL[15]OW[2] +;B[fi]BL[15]OB[5] +;W[nh]WL[15]OW[2] +;B[is]BL[15]OB[5] +;W[se]WL[15]OW[2] +;B[gh]BL[15]OB[5] +;W[gg]WL[15]OW[2] +;B[sg]BL[15]OB[5] +;W[eh]WL[15]OW[2] +;B[gs]BL[15]OB[5] +;W[hs]WL[15]OW[2] +;B[js]BL[15]OB[5] +;W[mj]WL[15]OW[2] +;B[lj]BL[15]OB[5] +;W[pi]WL[15]OW[2] +;B[qi]BL[15]OB[5] +;W[ar]WL[15]OW[2] +;B[aq]BL[15]OB[5] +;W[ak]WL[15]OW[2] +;B[al]BL[15]OB[5] +;W[aj]WL[15]OW[2] +;B[co]BL[15]OB[5] +;W[ep]WL[15]OW[2] +;B[fo]BL[15]OB[5] +;W[cs]WL[15]OW[2] +;B[ck]BL[15]OB[5] +;W[oi]WL[15]OW[2] +;B[rj]BL[15]OB[5] +;W[as]WL[15]OW[2] +;B[sp]BL[15]OB[5] +;W[]WL[15]OW[2] +;B[]BL[15]OB[5]TW[aa][ba][ca][da][ea][fa][ga][ha][ia][ja][ka][ma][oa][pa][qa][ra][sa][ab][bb][cb][db][eb][fb][gb][hb][ib][jb][kb][mb][ob][pb][qb][rb][sb][ac][bc][dc][ec][fc][gc][hc][ic][jc][kc][mc][nc][pc][rc][sc][ad][bd][dd][ed][fd][gd][hd][id][kd][ld][nd][od][qd][rd][sd][ae][ce][ee][fe][ge][he][ie][je][ke][me][oe][pe][af][bf][df][ff][gf][hf][if][jf][kf][lf][nf][pf][qf][ag][cg][dg][fg][lg][ng][og][ah][bi][dr][es][fs][gs]TB[sh][ii][ri][si][dj][ej][fj][gj][hj][jj][kj][qj][sj][dk][ek][fk][gk][hk][ik][jk][kk][lk][qk][rk][sk][cl][dl][el][fl][gl][hl][il][jl][kl][ll][ol][ql][rl][sl][am][bm][dm][em][fm][gm][hm][im][jm][km][lm][nm][om][sm][an][bn][cn][dn][en][hn][in][jn][kn][ln][on][pn][qn][sn][ao][bo][ho][io][jo][ko][lo][mo][no][qo][ro][so][ap][bp][ip][jp][kp][mp][np][op][qp][rp][jq][lq][mq][nq][oq][qq][sq][kr][mr][nr][or][sr][ks][ls][ms][ns][os][ps][qs][rs][ss]) diff --git a/jni/pachi/t-regress/games/2011-08-12-xiaosugi-pachi2.sgf b/jni/pachi/t-regress/games/2011-08-12-xiaosugi-pachi2.sgf new file mode 100644 index 0000000..0965e26 --- /dev/null +++ b/jni/pachi/t-regress/games/2011-08-12-xiaosugi-pachi2.sgf @@ -0,0 +1,320 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[xiaosugi]PB[pachi2]BR[3d]DT[2011-08-12]GC[191 +S3:alive{falseeye} Pachi hallucinated again on bottom right corner.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [3d\]: GTP Engine for pachi2 (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[W+Resign] +;B[pp]BL[15]OB[5] +;W[dp]WL[15]OW[5] +;B[pc]BL[15]OB[5] +;W[dc]WL[15]OW[5] +;B[de]BL[15]OB[5] +;W[fd]WL[15]OW[5] +;B[cc]BL[15]OB[5] +;W[cb]WL[15]OW[5] +;B[cd]BL[15]OB[5] +;W[eb]WL[15]OW[5] +;B[ci]BL[15]OB[5] +;W[pe]WL[15]OW[5] +;B[ph]BL[15]OB[5] +;W[md]WL[15]OW[5] +;B[gp]BL[15]OB[5] +;W[cn]WL[15]OW[5] +;B[cl]BL[15]OB[5] +;W[qc]WL[15]OW[5] +;B[kq]BL[15]OB[5] +;W[qn]WL[15]OW[5] +;B[ql]BL[15]OB[5] +;W[qq]WL[15]OW[5] +;B[qp]BL[15]OB[5] +;W[pq]WL[15]OW[5] +;B[op]BL[15]OB[5] +;W[rp]WL[15]OW[5] +;B[ro]BL[15]OB[5] +;W[rq]WL[15]OW[5] +;B[qo]BL[15]OB[5] +;W[nq]WL[15]OW[5] +;B[np]BL[15]OB[5] +;W[mq]WL[15]OW[5] +;B[mp]BL[15]OB[5] +;W[nh]WL[15]OW[5] +;B[jj]BL[15]OB[5] +;W[jh]WL[15]OW[5] +;B[qd]BL[15]OB[5] +;W[pd]WL[15]OW[5] +;B[rc]BL[15]OB[5] +;W[qb]WL[15]OW[5] +;B[qe]BL[15]OB[5] +;W[rb]WL[15]OW[4] +;B[pf]BL[15]OB[5] +;W[of]WL[15]OW[4] +;B[pg]BL[15]OB[5] +;W[rd]WL[15]OW[4] +;B[ng]BL[15]OB[5] +;W[mg]WL[15]OW[4] +;B[nf]BL[15]OB[5] +;W[od]WL[15]OW[4] +;B[mh]BL[15]OB[5] +;W[lh]WL[15]OW[4] +;B[mi]BL[15]OB[5] +;W[li]WL[15]OW[4] +;B[mj]BL[15]OB[5] +;W[lj]WL[15]OW[4] +;B[lg]BL[15]OB[5] +;W[mf]WL[15]OW[4] +;B[me]BL[15]OB[5] +;W[lf]WL[15]OW[4] +;B[le]BL[15]OB[5] +;W[kf]WL[15]OW[4] +;B[nd]BL[15]OB[5] +;W[nc]WL[15]OW[4] +;B[ne]BL[15]OB[5] +;W[re]WL[15]OW[4] +;B[mk]BL[15]OB[5] +;W[lk]WL[15]OW[4] +;B[qf]BL[15]OB[5] +;W[oc]WL[15]OW[4] +;B[ml]BL[15]OB[5] +;W[eq]WL[15]OW[4] +;B[ke]BL[15]OB[5] +;W[jf]WL[15]OW[4] +;B[gm]BL[15]OB[5] +;W[ll]WL[15]OW[4] +;B[lm]BL[15]OB[5] +;W[km]WL[15]OW[4] +;B[kn]BL[15]OB[5] +;W[ln]WL[15]OW[4] +;B[mm]BL[15]OB[5] +;W[jn]WL[15]OW[4] +;B[ko]BL[15]OB[5] +;W[jl]WL[15]OW[4] +;B[gj]BL[15]OB[5] +;W[gr]WL[15]OW[4] +;B[ff]BL[15]OB[5] +;W[rf]WL[15]OW[4] +;B[dm]BL[15]OB[5] +;W[gh]WL[15]OW[4] +;B[fh]BL[15]OB[5] +;W[rh]WL[15]OW[4] +;B[im]BL[15]OB[5] +;W[jm]WL[15]OW[4] +;B[je]BL[15]OB[5] +;W[qi]WL[15]OW[4] +;B[hc]BL[15]OB[5] +;W[gc]WL[15]OW[4] +;B[gq]BL[15]OB[5] +;W[hr]WL[15]OW[4] +;B[gb]BL[15]OB[5] +;W[fb]WL[15]OW[4] +;B[gd]BL[15]OB[5] +;W[fc]WL[15]OW[4] +;B[if]BL[15]OB[5] +;W[ig]WL[15]OW[4] +;B[kg]BL[15]OB[5] +;W[jg]WL[15]OW[4] +;B[cq]BL[15]OB[5] +;W[cp]WL[15]OW[4] +;B[er]BL[15]OB[5] +;W[dr]WL[15]OW[4] +;B[dq]BL[15]OB[5] +;W[cr]WL[15]OW[4] +;B[fq]BL[15]OB[5] +;W[bq]WL[15]OW[4] +;B[ib]BL[15]OB[5] +;W[fr]WL[15]OW[4] +;B[bb]BL[15]OB[5] +;W[ba]WL[15]OW[4] +;B[hg]BL[15]OB[5] +;W[hh]WL[15]OW[4] +;B[rg]BL[15]OB[5] +;W[sg]WL[15]OW[4] +;B[sh]BL[15]OB[5] +;W[si]WL[15]OW[4] +;B[gg]BL[15]OB[5] +;W[jk]WL[15]OW[4] +;B[bc]BL[15]OB[5] +;W[ed]WL[15]OW[4] +;B[fa]BL[15]OB[5] +;W[ea]WL[15]OW[4] +;B[ji]BL[15]OB[5] +;W[kc]WL[15]OW[4] +;B[lc]BL[15]OB[5] +;W[lb]WL[15]OW[4] +;B[lq]BL[15]OB[5] +;W[bm]WL[15]OW[4] +;B[ii]BL[15]OB[5] +;W[ih]WL[15]OW[4] +;B[hk]BL[15]OB[5] +;W[bk]WL[15]OW[4] +;B[bl]BL[15]OB[5] +;W[al]WL[15]OW[4] +;B[ck]BL[15]OB[5] +;W[bj]WL[15]OW[4] +;B[ak]BL[15]OB[5] +;W[aj]WL[15]OW[4] +;B[bi]BL[15]OB[5] +;W[qk]WL[15]OW[4] +;B[rk]BL[15]OB[5] +;W[pk]WL[15]OW[4] +;B[rj]BL[15]OB[5] +;W[pi]WL[15]OW[4] +;B[kb]BL[15]OB[5] +;W[ld]WL[15]OW[4] +;B[jc]BL[15]OB[5] +;W[mc]WL[15]OW[4] +;B[cm]BL[15]OB[5] +;W[bn]WL[15]OW[4] +;B[ep]BL[15]OB[5] +;W[es]WL[15]OW[4] +;B[pl]BL[15]OB[5] +;W[eo]WL[15]OW[4] +;B[mr]BL[15]OB[5] +;W[nr]WL[15]OW[4] +;B[oq]BL[15]OB[5] +;W[or]WL[15]OW[4] +;B[lr]BL[15]OB[5] +;W[jp]WL[15]OW[4] +;B[jo]BL[15]OB[5] +;W[io]WL[15]OW[4] +;B[ip]BL[15]OB[5] +;W[go]WL[15]OW[4] +;B[in]BL[15]OB[5] +;W[hp]WL[15]OW[4] +;B[ns]BL[15]OB[5] +;W[pr]WL[15]OW[4] +;B[ho]BL[15]OB[5] +;W[hq]WL[15]OW[4] +;B[rr]BL[15]OB[5] +;W[sr]WL[15]OW[4] +;B[rs]BL[15]OB[5] +;W[qs]WL[15]OW[4] +;B[os]BL[15]OB[5] +;W[sp]WL[15]OW[4] +;B[qj]BL[15]OB[5] +;W[pj]WL[15]OW[4] +;B[oi]BL[15]OB[5] +;W[oj]WL[15]OW[4] +;B[sf]BL[15]OB[5] +;W[qg]WL[15]OW[4] +;B[fo]BL[15]OB[5] +;W[fp]WL[15]OW[4] +;B[ni]BL[15]OB[5] +;W[ol]WL[15]OW[4] +;B[om]BL[15]OB[5] +;W[qh]WL[15]OW[4] +;B[rl]BL[15]OB[5] +;W[ai]WL[15]OW[4] +;B[cj]BL[15]OB[5] +;W[am]WL[15]OW[4] +;B[ah]BL[15]OB[5] +;W[ak]WL[15]OW[4] +;B[iq]BL[15]OB[5] +;W[bh]WL[15]OW[4] +;B[ch]BL[15]OB[5] +;W[bg]WL[15]OW[4] +;B[cg]BL[15]OB[5] +;W[bf]WL[15]OW[4] +;B[cf]BL[15]OB[5] +;W[be]WL[15]OW[4] +;B[ca]BL[15]OB[5] +;W[da]WL[15]OW[4] +;B[ok]BL[15]OB[5] +;W[nk]WL[15]OW[4] +;B[ce]BL[15]OB[5] +;W[ge]WL[15]OW[4] +;B[hd]BL[15]OB[5] +;W[fe]WL[15]OW[4] +;B[fn]BL[15]OB[5] +;W[ga]WL[15]OW[4] +;B[hb]BL[15]OB[5] +;W[ie]WL[15]OW[4] +;B[hf]BL[15]OB[5] +;W[he]WL[15]OW[4] +;B[aa]BL[15]OB[5] +;W[ab]WL[15]OW[4] +;B[id]BL[15]OB[5] +;W[ka]WL[15]OW[4] +;B[ja]BL[15]OB[5] +;W[jb]WL[15]OW[4] +;B[ir]BL[15]OB[5] +;W[gf]WL[15]OW[4] +;B[fg]BL[15]OB[5] +;W[ef]WL[15]OW[4] +;B[kb]BL[15]OB[5] +;W[fi]WL[15]OW[4] +;B[eh]BL[15]OB[5] +;W[ei]WL[15]OW[4] +;B[di]BL[15]OB[5] +;W[gi]WL[15]OW[4] +;B[la]BL[15]OB[5] +;W[ma]WL[15]OW[4] +;B[fj]BL[15]OB[5] +;W[ej]WL[15]OW[4] +;B[ek]BL[15]OB[5] +;W[eg]WL[15]OW[4] +;B[dh]BL[15]OB[5] +;W[ee]WL[15]OW[4] +;B[kd]BL[15]OB[5] +;W[lc]WL[15]OW[4] +;B[na]BL[15]OB[5] +;W[ka]WL[15]OW[4] +;B[oe]BL[15]OB[5] +;W[jb]WL[15]OW[4] +;B[ik]BL[15]OB[5] +;W[ia]WL[15]OW[4] +;B[ha]BL[15]OB[5] +;W[hn]WL[15]OW[4] +;B[ja]BL[15]OB[5] +;W[kb]WL[15]OW[4] +;B[gn]BL[15]OB[5] +;W[io]WL[15]OW[4] +;B[co]BL[15]OB[5] +;W[ho]WL[15]OW[3] +;B[dn]BL[15]OB[5] +;W[bo]WL[15]OW[3] +;B[do]BL[15]OB[5] +;W[so]WL[15]OW[3] +;B[hl]BL[15]OB[5] +;W[rn]WL[15]OW[3] +;B[nl]BL[15]OB[5] +;W[pn]WL[15]OW[3] +;B[ok]BL[15]OB[5] +;W[nj]WL[15]OW[3] +;B[ol]BL[15]OB[5] +;W[ri]WL[15]OW[3] +;B[is]BL[15]OB[5] +;W[on]WL[15]OW[3] +;B[lo]BL[15]OB[5] +;W[nn]WL[15]OW[3] +;B[ki]BL[15]OB[5] +;W[kh]WL[15]OW[3] +;B[hi]BL[15]OB[5] +;W[mn]WL[15]OW[3] +;B[kk]BL[15]OB[5] +;W[kj]WL[15]OW[3] +;B[en]BL[15]OB[5] +;W[ad]WL[15]OW[3] +;B[ac]BL[15]OB[5] +;W[bd]WL[15]OW[3] +;B[hs]BL[15]OB[5] +;W[gs]WL[15]OW[3] +;B[jq]BL[15]OB[5] +;W[dj]WL[15]OW[3] +;B[sj]BL[15]OB[5] +;W[dk]WL[15]OW[3] +;B[el]BL[15]OB[5] +;W[sm]WL[15]OW[3] +;B[aa]BL[15]OB[5] +;W[ca]WL[15]OW[3] +;B[ab]BL[15]OB[5] +;W[sl]WL[15]OW[3] +;B[ls]BL[15]OB[5] +;W[mo]WL[15]OW[3] +;B[lp]BL[15]OB[5] +;W[ep]WL[15]OW[3] +;B[fl]BL[15]OB[5] +;W[la]WL[15]OW[3] +;B[bs]BL[15]OB[5] +;W[fa]WL[15]OW[3] +;B[df]BL[15]OB[5] +;W[kl]WL[15]OW[3] +;B[sc]BL[15]OB[5] +;W[qr]WL[15]OW[3]) diff --git a/jni/pachi/t-regress/games/2011-08-24-StoneGrid-pachi2.sgf b/jni/pachi/t-regress/games/2011-08-24-StoneGrid-pachi2.sgf new file mode 100644 index 0000000..cf97b5c --- /dev/null +++ b/jni/pachi/t-regress/games/2011-08-24-StoneGrid-pachi2.sgf @@ -0,0 +1,391 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[14400] +PW[StoneGrid]PB[pachi2]BR[3d]DT[2011-08-24]GC[51 +E14:bad!{ladder} pachi plays a broken ladder, which almost cost the game]PC[The KGS Go Server at http://www.gokgs.com/]C[StoneGrid [-\]: GTP Engine for StoneGrid (white): stonegrid version 0.3.4 48 +pachi2 [3d\]: GTP Engine for pachi2 (black): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +]RE[B+Resign] +;B[pp]BL[14399.379]C[maproomad [-\]: pachi2: In 2367711 playouts at 64 machines, white M10 can win with 96.89% probability. +maproomad [-\]: I guess that is left over from the previous round +maproomad [-\]: pachi2: In 10750 playouts at 64 machines, black Q4 can win with 100.00% probability. +maproomad [-\]: maybe not +] +;W[mg]WL[14246.492]C[BinX [-\]: black q4 can win with a 100% probability ? kind of a divine first move ? +] +;B[cj]BL[14189.181]C[BinX [-\]: oh :) +BinX [-\]: fun game. +] +;W[no]WL[14092.666] +;B[pm]BL[14083.262] +;W[op]WL[13940.553] +;B[pq]BL[13977.422]C[maproomad [-\]: pachi2: In 125164015 playouts at 64 machines, black Q3 can win with 48.70% probability. +] +;W[cq]WL[13788.393] +;B[co]BL[13775.243]C[uurtamo [-\]: but pachi2 could have checked each of the 6-ply inbetween then. :) +] +;W[dn]WL[13637.551]C[goIngo [-\]: is this avantgarde go? +uurtamo [-\]: it's curious. +BinX [-\]: oh, pachi lose confidence with q3 already :o +] +;B[cn]BL[13669.876]C[uurtamo [-\]: yes, to go from 100% to 48.7% in a sequence of moves all of which could be explored, means something isn't right. :) +blob [5k\]: sure, n13, to hell with joseki +uurtamo [-\]: i don't understand n13 well enough to say whether it's joseki or not. +] +;W[do]WL[13486.901]C[blob [5k\]: n13 of course, is not joseki +blob [5k\]: not yet anyway....... +uurtamo [-\]: joseki isn't about what is in a book, it's about what makes the board equal. +blob [5k\]: uh, no +blob [5k\]: it's what has the historical concensus of being a solid move +BinX [-\]: ? +] +;B[dm]BL[13564.821]C[uurtamo [-\]: it's what has the historical concensus of giving a fair outcome for both players. +blob [5k\]: which n13 hasn't +uurtamo [-\]: fair enough. +uurtamo [-\]: however, it might give a fair outcome for both players. all i'm saying. +blob [5k\]: "might".... which is why i said "not yet anyway" +] +;W[cm]WL[13336.94] +;B[em]BL[13457.786]C[uurtamo [-\]: you're fixated on the "historical concensus" and i'm fixated on the "fair outcome". no biggie. +blob [5k\]: sure, but you just misunderstand the meaning of joseki apparently +uurtamo [-\]: could very well be the case. +] +;W[cp]WL[13186.961]C[blob [5k\]: the jo in joseki means "established" (among other things) +blob [5k\]: i.o.w., an established move +] +;B[bm]BL[13363.95]C[blob [5k\]: ....why do these bots get so much time? +uurtamo [-\]: because it's the "slow play tournament" +blob [5k\]: it sort of rewards brute force approaches to machine go, which i think isn't really desirable +] +;W[cc]WL[13039.78]C[uurtamo [-\]: isn't desirable in what sense? +blob [5k\]: is there a fast tournament? +uurtamo [-\]: have you ever tried to write a machine go program? +blob [5k\]: i've started one yes +uurtamo [-\]: there are tournaments every month, and a server where programs are continuously playing one another +blob [5k\]: by not desirable i mean that i find efficiency in computation intuitively more "human" +blob [5k\]: than monte carlo-esque stuff anyway +uurtamo [-\]: http://www.weddslist.com/kgs +blob [5k\]: thanks for that +] +;B[kq]BL[13166.445]C[uurtamo [-\]: i think that you want computation to be a certain way that it might not be. +blob [5k\]: look, the status quo is that humans are stronger than machines at go, yet use less resources (energy wise) +blob [5k\]: i think it's natural to learn by that example somehow +uurtamo [-\]: that idea was very popular in the 70's. +blob [5k\]: so? +uurtamo [-\]: i'm just suggesting that other approaches have been quite a bit more successful, not just in computer go, but machine learning in general and that you ought not discount their success. +uurtamo [-\]: there's a mistake in being fixated on what "seems natural" +uurtamo [-\]: computers aren't all that natural and neither is computation. +blob [5k\]: i am familiar with ai techniques +] +;W[ko]WL[12892.264]C[uurtamo [-\]: there's a mailing list about computer go if you're interested in seeing what people think about. +blob [5k\]: i'm saying that it seems that human level of higher abstraction can lead to significant gains in efficiency +blob [5k\]: i know what's out there already..... +] +;B[hq]BL[12958.564]C[blob [5k\]: there's a huge gain to be had over mere monte carlo, by clustering and classifying patterns dynamically +uurtamo [-\]: well, the easiest way to test your ideas is on CGOS. +uurtamo [-\]: http://cgos.boardspace.net +blob [5k\]: yeah, as soon as i get some more time on my hands ): +blob [5k\]: i will :) +uurtamo [-\]: because otherwise you are simply fantasizing. +uurtamo [-\]: also, "mere monte carlo" has developed quite a bit. +blob [5k\]: i already have a working ML go prototype +uurtamo [-\]: great. you can probably hook it into cgos with very little trouble, then. +] +;W[jp]WL[12747.122]C[blob [5k\]: probably, but i want to work on it more first +blob [5k\]: it's still weak +uurtamo [-\]: cgos will let you see its level exactly compared with other programs. +blob [5k\]: i'll keep that in mind, thanks +] +;B[jq]BL[12854.702]C[maproomad [-\]: pachi2: In 76190780 playouts at 64 machines, black K3 can win with 50.28% probability. +blob [5k\]: btw, any existing go bots that use machine learning out there? +uurtamo [-\]: yes, and they all get crushed by the monte carlo bots. which is why everyone is studying that now. +uurtamo [-\]: just teasing. +blob [5k\]: hah +] +;W[hp]WL[12601.969]C[uurtamo [-\]: there are bots that implement some kinds of machine learning, but they usually also have a very strong monte-carlo component. +blob [5k\]: that's good news for me +uurtamo [-\]: because otherwise they get absolutely crushed. +uurtamo [-\]: but you should ask on the computer go mailing list. there are lots of helpful ideas there and people there. +blob [5k\]: they probably rely on monte carlo so much because their board evaluation sucks +blob [5k\]: and have to play out very far +uurtamo [-\]: be careful with your estimate of other programs you haven't written. +] +;B[gp]BL[12750.975]C[uurtamo [-\]: the only thing you can be sure of is what you've already written. +blob [5k\]: ok :p +blob [5k\]: have you written a bot? +uurtamo [-\]: for instance, many faces of go, which is very strong, not uses some MC, but didn't before. +uurtamo [-\]: nope, i just started writing one. +uurtamo [-\]: *now uses +uurtamo [-\]: i've written a board evaluator, but never turned it into a bot. +blob [5k\]: evaluation is crucial though, so that should take you a long way +uurtamo [-\]: or not. +uurtamo [-\]: words like "should" require deep experience. +] +;W[iq]WL[12457.681]C[blob [5k\]: whatever, either agree or don't +] +;B[ir]BL[12647.293]C[goIngo [-\]: nick, can you give pachi %? +maproomad [-\]: The more people know about computer Go, the less they pontificate about it :) +maproomad [-\]: ok +maproomad [-\]: pachi2: In 130351958 playouts at 64 machines, black J2 can win with 51.04% probability. +goIngo [-\]: thx +klausP [3d?\]: i prefer black here +blob [5k\]: very few ppl "know" about computer go +] +;W[ip]WL[12313.346] +;B[gq]BL[12553.873]C[blob [5k\]: too slow for me.... good nigh +uurtamo [-\]: zen has a bye? +] +;W[lq]WL[12220.093]C[maproomad [-\]: yes - Zen has a bye at last. gghideki will use the time to isntall its new hardware +] +;B[lr]BL[12449.955]C[maproomad [-\]: pachi2: In 115171212 playouts at 64 machines, black M2 can win with 52.80% probability. +uurtamo [-\]: cool, new hardware. +] +;W[jr]WL[12076.655] +;B[kr]BL[12346.161] +;W[qh]WL[11934.434] +;B[lm]BL[12148.375] +;W[ln]WL[11793.194] +;B[mm]BL[11950.846] +;W[go]WL[11653.706]C[uurtamo [-\]: do you know what the hardware is? +] +;B[fo]BL[11785.708] +;W[km]WL[11513.825] +;B[kl]BL[11660.557] +;W[jm]WL[11373.958] +;B[ce]BL[11471.174]C[GoRoGoRo [9k\]: Zen still playing in its old clothes +] +;W[de]WL[11235.322] +;B[df]BL[11370.351] +;W[cf]WL[11098.126]C[goIngo [-\]: why to buy new ones, when the old fit? +] +;B[dd]BL[11269.657] +;W[ee]WL[10960.807] +;B[cd]BL[11184.929]C[maproomad [-\]: pachi2: In 178944910 playouts at 64 machines, black C16 can win with 56.76% probability. +] +;W[dg]WL[10855.87] +;B[ef]BL[11084.087] +;W[ff]WL[10719.349] +;B[eg]BL[10983.134]C[maproomad [-\]: e13 looks like a losing ladder +uurtamo [-\]: everyone wants to look stylin'. +maproomad [-\]: pachi2: In 126328940 playouts at 64 machines, black E13 can win with 57.39% probability. +] +;W[eh]WL[10584.286]C[uurtamo [-\]: oh, this could be a game ender. +uurtamo [-\]: i wonder how far into the ladder pachi2 will notice and tenuki. +] +;B[fg]BL[10862.716]C[maproomad [-\]: pachi2: In 148112137 playouts at 64 machines, black F13 can win with 55.81% probability. +uurtamo [-\]: i guess not yet, then. :) +] +;W[gg]WL[10448.687]C[ARPAnet [?\]: cool, lee sedol's ladder game :) +] +;B[fh]BL[10727.682]C[maproomad [-\]: pachi2: In 164712506 playouts at 64 machines, black F12 can win with 51.06% probability. +] +;W[fi]WL[10313.704] +;B[mo]BL[10544.165]C[maproomad [-\]: pachi2: In 170439444 playouts at 64 machines, black N5 can win with 44.51% probability. +uurtamo [-\]: doh! +] +;W[mn]WL[10180.199] +;B[nm]BL[10357.779] +;W[dc]WL[10057.33] +;B[gh]BL[10162.79]C[maproomad [-\]: pachi2: In 82988479 playouts at 64 machines, black G12 can win with 40.76% probability. +goIngo [-\]: hmm, pachi in downfall? +] +;W[hh]WL[9923.536]C[klausP [4d?\]: jup +] +;B[gn]BL[10004.306]C[maproomad [-\]: pachi2: In 122189851 playouts at 64 machines, black G6 can win with 38.62% probability. +] +;W[gi]WL[9793.004] +;B[ed]BL[9835.3] +;W[ec]WL[9667.99] +;B[fc]BL[9656.224] +;W[fd]WL[9543.048] +;B[gd]BL[9484.417]C[maproomad [-\]: pachi2: In 71631949 playouts at 64 machines, black G16 can win with 38.29% probability. +] +;W[fe]WL[9418.831] +;B[pg]BL[9335.446]C[klausP [3d?\]: poor pachi +] +;W[qe]WL[9293.787] +;B[qg]BL[9194.786] +;W[ph]WL[9171.43] +;B[oh]BL[8952.401] +;W[og]WL[9048.793] +;B[rh]BL[8814.474] +;W[ri]WL[8927.703] +;B[rf]BL[8681.749] +;W[oi]WL[8805.411] +;B[nh]BL[8551.044] +;W[ng]WL[8685.1] +;B[qi]BL[8422.3] +;W[pi]WL[8564.827] +;B[qj]BL[8323.697] +;W[sh]WL[8533.921] +;B[rg]BL[8198.341]C[maproomad [-\]: pachi2: In 169314007 playouts at 64 machines, black S13 can win with 42.44% probability. +] +;W[qk]WL[8451.774] +;B[pj]BL[8074.903]C[klausP [3d?\]: pachi fighting for a comeback, so is gomorra +maproomad [-\]: pachi2: In 142840643 playouts at 64 machines, black Q10 can win with 43.64% probability. +] +;W[rj]WL[8383.772] +;B[pk]BL[7955.361] +;W[hr]WL[8268.369] +;B[js]BL[7835.596]C[maproomad [-\]: pachi2: In 157538848 playouts at 64 machines, black K1 can win with 44.47% probability. +maproomad [-\]: creeping back towards 50% +] +;W[ni]WL[8150.466] +;B[mh]BL[7719.925]C[goIngo [-\]: unbelievable +] +;W[mi]WL[8035.124] +;B[lh]BL[7613.401] +;W[lg]WL[7944.056]C[goIngo [-\]: pachi %, please +] +;B[li]BL[7500.636]C[maproomad [-\]: pachi2: In 141731142 playouts at 64 machines, black M11 can win with 47.00% probability. +goIngo [-\]: thx +maproomad [-\]: if I don't res[pond, it's probably becasue I am not here +goIngo [-\]: ok +] +;W[ok]WL[7830.385]C[HYamashita [3k\]: pachi ran from ladder... if StoneGrid lose, 66th G11 is maybe lose move :-) +] +;B[pl]BL[7314.172]C[goIngo [-\]: KlausP also thought so +maproomad [-\]: pachi2: In 175825257 playouts at 64 machines, black Q8 can win with 48.13% probability. +] +;W[lj]WL[7716.828] +;B[kj]BL[7205.826] +;W[lk]WL[7604.161] +;B[oj]BL[7099.234]C[maproomad [-\]: pachi2: In 156331002 playouts at 64 machines, black P10 can win with 50.68% probability. +] +;W[mj]WL[7490.738] +;B[kk]BL[6994.124] +;W[re]WL[7408.672] +;B[pf]BL[6816.718] +;W[pe]WL[7298.225] +;B[of]BL[6715.798] +;W[ne]WL[7188.132] +;B[cl]BL[6616.812]C[maproomad [-\]: pachi2: In 87049581 playouts at 64 machines, black C8 can win with 54.53% probability. +] +;W[po]WL[7078.102]C[goIngo [-\]: wow, turning the table +maproomad [-\]: seems optimistic - W has a huge top, and B's central group is weak +] +;B[qo]BL[6432.123]C[maproomad [-\]: pachi2: In 136842147 playouts at 64 machines, black R5 can win with 54.94% probability. +] +;W[fr]WL[6970.775] +;B[gr]BL[6337.334] +;W[qp]WL[6863.532] +;B[pn]BL[6166.407] +;W[qq]WL[6758.457] +;B[oo]BL[6075.173] +;W[pr]WL[6653.682] +;B[oq]BL[5988.859] +;W[ro]WL[6548.985] +;B[jh]BL[5901.625] +;W[kg]WL[6446.203] +;B[rn]BL[5808.541] +;W[jg]WL[6343.276] +;B[ji]BL[5656.431] +;W[ik]WL[6242.372] +;B[jc]BL[5510.951] +;W[lc]WL[6140.836] +;B[fb]BL[5431.115] +;W[kh]WL[6041.661] +;B[ki]BL[5351.976] +;W[gc]WL[5942.788] +;B[hc]BL[5274.008] +;W[eb]WL[5844.746] +;B[gb]BL[5197.281] +;W[ib]WL[5747.462] +;B[jb]BL[5121.653] +;W[kb]WL[5651.079] +;B[kc]BL[4999.996] +;W[hd]WL[5553.721] +;B[he]BL[4882.043] +;W[kd]WL[5458.66] +;B[id]BL[4768.85] +;W[jd]WL[5365.841] +;B[lb]BL[4697.354] +;W[ic]WL[5273.326] +;B[ka]BL[4625.948] +;W[ia]WL[5180.722] +;B[mc]BL[4543.569] +;W[ld]WL[5089.09] +;B[hb]BL[4472.629] +;W[nb]WL[4999.019] +;B[nc]BL[4374.154] +;W[oc]WL[4909.481] +;B[od]BL[4276.269]C[maproomad [-\]: pachi2: In 45121745 playouts at 64 machines, black P16 can win with 81.12% probability. +maproomad [-\]: maybe it thinks the bottom left is dead +] +;W[ll]WL[4819.899] +;B[mk]BL[4182.18] +;W[oe]WL[4731.432]C[GoRoGoRo [9k\]: isn't it? +GoRoGoRo [9k\]: well ... W d2 may be a start? +maproomad [-\]: W d2? +GoRoGoRo [9k\]: thx +] +;B[ob]BL[4090.913] +;W[is]WL[4645.672] +;B[hs]BL[4021.198] +;W[pc]WL[4558.392] +;B[eq]BL[3931.121]C[maproomad [-\]: pachi2: In 66792603 playouts at 64 machines, black E3 can win with 83.32% probability. +] +;W[so]WL[4473.459] +;B[rq]BL[3841.046]C[maproomad [-\]: pachi2: In 53617646 playouts at 64 machines, black S3 can win with 84.08% probability. +] +;W[qr]WL[4389.52] +;B[rp]BL[3751.411] +;W[mp]WL[4306.146] +;B[rr]BL[3662.657]C[maproomad [-\]: pachi2: In 53837927 playouts at 64 machines, black S2 can win with 84.37% probability. +] +;W[ie]WL[4224.887] +;B[hd]BL[3595.694]C[maproomad [-\]: pachi2: In 112272634 playouts at 64 machines, black H16 can win with 82.43% probability. +] +;W[nq]WL[4143.842] +;B[np]BL[3529.095] +;W[qm]WL[4062.431] +;B[nn]BL[3441.868] +;W[qn]WL[3983.645] +;B[po]BL[3439.797] +;W[rm]WL[3905.712] +;B[rl]BL[3368.682] +;W[rs]WL[3828.818] +;B[bg]BL[3281.343] +;W[sr]WL[3751.145] +;B[hj]BL[3194.22] +;W[bh]WL[3673.258] +;B[bf]BL[3128.416] +;W[nk]WL[3597.054] +;B[ch]BL[3042.792] +;W[ml]WL[3523.668] +;B[nl]BL[2960.247]C[maproomad [-\]: pachi2: In 93360296 playouts at 64 machines, black O8 can win with 85.51% probability. +jlg [-\]: I should also report the dynamic komi. pachi is currently giving 26 points komi +] +;W[ci]WL[3448.2] +;B[bi]BL[2895.557] +;W[ai]WL[3375.882] +;B[di]BL[2814.737] +;W[bd]WL[3305.694] +;B[il]BL[2734.352] +;W[fl]WL[3232.07] +;B[or]BL[2655.689] +;W[fm]WL[3159.724]C[Winnetou [?\]: The moves are very strange. After pachi lost the ladder the game was terrible. +] +;B[ps]BL[2579.106]C[jlg [-\]: yes this is a known ladder bug indeed awful. But now pachi is trying to maximize the score (komi 35 points) +] +;W[jk]WL[3088.298]C[Winnetou [?\]: It seems that it is not pachi who maximizes the win but Stonegrid who maximizes the loss ;) +] +;B[be]BL[2503.785]C[maproomad [-\]: pachi2: In 93549867 playouts at 64 machines, black B15 can win with 89.42% probability. +] +;W[na]WL[3018.609]C[reticent [5k\]: go seigen would either love or shake his head at the fuseki in this game +reticent [5k\]: not nearly strong enough to know which +] +;B[ho]BL[2432.144]C[reticent [5k\]: and what the hell was the ladder pachi2 played out? +jlg [-\]: known ladder bug +reticent [5k\]: right +reticent [5k\]: anyway, looks like pachi2 spotted stonegrid fifty points and went on to win anyway +] +;W[dp]WL[2948.142] +;B[md]BL[2362.29] +;W[nd]WL[2879.078] +;B[bc]BL[2293.487]C[jlg [-\]: 92%, komi 54 +] +;W[bb]WL[2813.878] +;B[nj]BL[2291.855] +;W[mb]WL[2747.406] +;B[ql]BL[2222.578] +;W[sn]WL[2682.776] +;B[mk]BL[2221.186]C[salty [2d\]: i hope w isn`t planing to win that ^ +]) diff --git a/jni/pachi/t-regress/games/2011-09-04-pachi2-stv.sgf b/jni/pachi/t-regress/games/2011-09-04-pachi2-stv.sgf new file mode 100644 index 0000000..636786e --- /dev/null +++ b/jni/pachi/t-regress/games/2011-09-04-pachi2-stv.sgf @@ -0,0 +1,123 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[1740]OT[10/30 Canadian] +PW[pachi2]PB[stv]WR[3d]DT[2011-09-04]GC[48 +H8:bad!{ladder} pachi plays a broken ladder, which cost not just the game but also the first place in the tournament. This is a more complex ladder than usual, though.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [3d\]: GTP Engine for pachi2 (white): Pachi Distributed Engine version 8.99 (Hakugen-devel): I'm playing the distributed engine. When I'm losing, I will resign, if I think I win, I play until you pass. Anyone can send me 'winrate' in private chat to get my assessment of the position. +stv [-\]: GTP Engine for stv (black): Steenvreter version r105 +]RE[B+Resign] +;B[pp]BL[1739.65] +;W[dd]WL[1738.268] +;B[dp]BL[1739.324] +;W[qd]WL[1737.439] +;B[jp]BL[1739.047] +;W[qn]WL[1736.614] +;B[ql]BL[1738.772] +;W[qq]WL[1735.768] +;B[pq]BL[1738.481] +;W[qp]WL[1734.863] +;B[po]BL[1738.19] +;W[rn]WL[1734.045] +;B[om]BL[1737.901] +;W[cn]WL[1733.151] +;B[cl]BL[1737.629] +;W[en]WL[1732.354] +;B[dm]BL[1721.75] +;W[dn]WL[1719.5] +;B[fl]BL[1705.918] +;W[ci]WL[1706.552] +;B[fp]BL[1690.129] +;W[fm]WL[1683.483] +;B[gm]BL[1674.386] +;W[gl]WL[1670.697] +;B[hm]BL[1658.697] +;W[el]WL[1657.592] +;B[fk]BL[1643.046] +;W[dk]WL[1644.69] +;B[ck]BL[1627.478] +;W[dj]WL[1631.927] +;B[em]BL[1611.97] +;W[fn]WL[1619.185] +;B[gk]BL[1581.375] +;W[go]WL[1606.577] +;B[dl]BL[1566.113] +;W[ek]WL[1593.74] +;B[bi]BL[1550.93] +;W[bh]WL[1581.154] +;B[nc]BL[1535.803] +;W[io]WL[1560.391] +;B[bj]BL[1520.715] +;W[ch]WL[1547.637] +;B[bm]BL[1505.691] +;W[bn]WL[1527.846] +;B[jo]BL[1490.705] +;W[in]WL[1504.5] +;B[hq]BL[1475.244] +;W[hl]WL[1491.885] +;B[il]BL[1460.337] +;W[hk]WL[1479.253] +;B[hj]BL[1445.523] +;W[ik]WL[1466.785] +;B[jk]BL[1430.746] +;W[ij]WL[1454.189]C[jlg [-\]: 61% for pachi +] +;B[ii]BL[1416.054] +;W[jj]WL[1441.464] +;B[kj]BL[1401.437] +;W[ji]WL[1428.837] +;B[jh]BL[1386.857] +;W[ki]WL[1416.175] +;B[li]BL[1372.288] +;W[kh]WL[1403.36] +;B[kg]BL[1357.764]C[evdw [-\]: one must be wrong here... +] +;W[lh]WL[1388.117] +;B[mh]BL[1343.306]C[jlg [-\]: pachi has a know bug for ladders but this time I'm glad it is right :-) +] +;W[lg]WL[1372.638] +;B[lf]BL[1328.966] +;W[mg]WL[1357.012] +;B[ng]BL[1314.64]C[evdw [-\]: is it? +] +;W[gp]WL[1332.229]C[jlg [-\]: oops +] +;B[gq]BL[1300.372]C[maproomad [-\]: pachi2: In 18629996 playouts at 64 machines, white G4 can win with 31.08% probability. +jlg [-\]: the ladder doesn't work +] +;W[iq]WL[1307.691]C[evdw [-\]: stv is at 65% +] +;B[ip]BL[1286.171] +;W[ir]WL[1287.816] +;B[hp]BL[1272]C[jlg [-\]: I can't believe it. pachi agrees with stv +jlg [-\]: I can't see how the ladder works +] +;W[ep]WL[1260.255] +;B[eq]BL[1257.895]C[liquido [4k\]: it does +evdw [-\]: it slides one along the black stone +liquido [4k\]: clone the board and play it out +evdw [-\]: its tricky... +] +;W[mf]WL[1227.852]C[jlg [-\]: ah then we did hit the known ladder bug :-) +] +;B[me]BL[1243.866]C[liquido [4k\]: well, if you just continue to atari, then its not so tricky +] +;W[fr]WL[1195.368] +;B[fq]BL[1229.853] +;W[cq]WL[1177.02]C[jlg [-\]: this ladder is costing the first place in the tournament :-( +] +;B[cp]BL[1215.901] +;W[bp]WL[1156.858]C[evdw [-\]: sorry about that +] +;B[bo]BL[1202.007]C[jlg [-\]: don't be sorry. we just have to fix the known bug. +] +;W[dq]WL[1125.857] +;B[bq]BL[1188.194] +;W[do]WL[1109.106] +;B[ap]BL[1174.503] +;W[co]WL[1092.487] +;B[eo]BL[1160.846] +;W[nf]WL[1073.718] +;B[of]BL[1147.33] +;W[bl]WL[1049.87] +;B[bk]BL[1133.849] +;W[cm]WL[1032.886] +;B[al]BL[1120.451]C[jlg [-\]: thanks for the game +evdw [-\]: thank you +]) diff --git a/jni/pachi/t-regress/games/2012-01-17-pachi2-Soromon-3.sgf b/jni/pachi/t-regress/games/2012-01-17-pachi2-Soromon-3.sgf new file mode 100644 index 0000000..44b78f1 --- /dev/null +++ b/jni/pachi/t-regress/games/2012-01-17-pachi2-Soromon-3.sgf @@ -0,0 +1,291 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]HA[2]KM[0.50]TM[0]OT[5x15 byo-yomi] +PW[pachi2]PB[Soromon]WR[3d]BR[1d]DT[2012-01-17]GC[248 /S14:alive,C3:dead pachi has two problems - black t10-t9-notconnecting and white t10-notconnecting; C3 is missing pattern issue]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [3d\]: GTP Engine for pachi2 (white): Pachi Distributed version 9.99 (Satsugen-devel): If you believe you have won but I am still playing, please help me understand by capturing all dead stones. Anyone can send me 'winrate' in private chat to get my assessment of the position. Have a nice game! +]RE[B+Resign] +;B[ji]BL[15]OB[5] +;B[jk]BL[15]OB[5] +;W[qp]WL[15]OW[5]C[pasky [-\]: haha +] +;B[op]BL[15]OB[5]C[pasky [-\]: oh ok +jlg [-\]: good strategy against bots +] +;W[pn]WL[15]OW[5]C[pasky [-\]: right, he did not have time to read aja's suggestion :) +] +;B[qq]BL[15]OB[5]C[pasky [-\]: i thought he's gonna do that +] +;W[cc]WL[15]OW[5] +;B[pp]BL[15]OB[5]C[pasky [-\]: c17 very daring +] +;W[qo]WL[15]OW[5] +;B[rq]BL[15]OB[5] +;W[qj]WL[15]OW[5] +;B[qd]BL[15]OB[5] +;W[dm]WL[15]OW[5] +;B[qh]BL[15]OB[5] +;W[qf]WL[15]OW[5] +;B[od]BL[15]OB[5] +;W[of]WL[15]OW[5] +;B[oh]BL[15]OB[5] +;W[mf]WL[15]OW[5] +;B[md]BL[15]OB[5] +;W[rh]WL[15]OW[5] +;B[ri]BL[15]OB[5] +;W[qi]WL[15]OW[5] +;B[rg]BL[15]OB[5] +;W[ph]WL[15]OW[5] +;B[qg]BL[15]OB[5] +;W[pg]WL[15]OW[5] +;B[rf]BL[15]OB[5] +;W[rj]WL[15]OW[5] +;B[dp]BL[15]OB[5] +;W[fp]WL[15]OW[5] +;B[eo]BL[15]OB[5] +;W[fn]WL[15]OW[5] +;B[fo]BL[15]OB[5] +;W[go]WL[15]OW[5] +;B[gp]BL[15]OB[5] +;W[hp]WL[15]OW[5] +;B[gq]BL[15]OB[5] +;W[hq]WL[15]OW[5] +;B[fq]BL[15]OB[5] +;W[gn]WL[15]OW[5] +;B[dj]BL[15]OB[5] +;W[fj]WL[15]OW[5] +;B[dd]BL[15]OB[5] +;W[cd]WL[15]OW[5] +;B[de]BL[15]OB[5] +;W[cf]WL[15]OW[5] +;B[ce]BL[15]OB[5] +;W[be]WL[15]OW[5] +;B[df]BL[15]OB[5] +;W[cg]WL[15]OW[5] +;B[dg]BL[15]OB[5] +;W[dh]WL[15]OW[5] +;B[ch]BL[15]OB[5] +;W[eh]WL[15]OW[5] +;B[bh]BL[15]OB[5] +;W[bg]WL[15]OW[5] +;B[ag]BL[15]OB[5] +;W[bf]WL[15]OW[5] +;B[fk]BL[15]OB[5] +;W[ci]WL[15]OW[5] +;B[bi]BL[15]OB[5] +;W[cj]WL[15]OW[5] +;B[ck]BL[15]OB[5] +;W[bj]WL[15]OW[5] +;B[gj]BL[15]OB[5] +;W[gi]WL[15]OW[5] +;B[fi]BL[15]OB[5] +;W[fh]WL[15]OW[5] +;B[ej]BL[15]OB[5] +;W[lq]WL[15]OW[5] +;B[gd]BL[15]OB[5] +;W[ic]WL[15]OW[5] +;B[id]BL[15]OB[5] +;W[jd]WL[15]OW[5] +;B[ie]BL[15]OB[5] +;W[nq]WL[15]OW[5] +;B[or]BL[15]OB[5] +;W[oq]WL[15]OW[5] +;B[pq]BL[15]OB[5] +;W[nr]WL[15]OW[5] +;B[on]BL[15]OB[5] +;W[om]WL[15]OW[5] +;B[nm]BL[15]OB[5] +;W[nn]WL[15]OW[5] +;B[oo]BL[15]OB[5] +;W[ol]WL[15]OW[5] +;B[mn]BL[15]OB[5] +;W[pr]WL[15]OW[5] +;B[bk]BL[15]OB[5] +;W[ai]WL[15]OW[5] +;B[mp]BL[15]OB[5] +;W[ko]WL[15]OW[5] +;B[kd]BL[15]OB[5] +;W[je]WL[15]OW[5] +;B[jc]BL[15]OB[5] +;W[kc]WL[15]OW[5] +;B[jb]BL[15]OB[5] +;W[ke]WL[15]OW[5] +;B[ld]BL[15]OB[5] +;W[qe]WL[15]OW[5] +;B[re]BL[15]OB[5] +;W[qc]WL[15]OW[5] +;B[pc]BL[15]OB[5] +;W[rd]WL[15]OW[5] +;B[pd]BL[15]OB[5] +;W[rb]WL[15]OW[5] +;B[sd]BL[15]OB[5] +;W[rc]WL[15]OW[5] +;B[sh]BL[15]OB[5]C[pasky [-\]: nice +] +;W[pb]WL[15]OW[5] +;B[ob]BL[15]OB[5] +;W[cq]WL[15]OW[5]C[pasky [-\]: pretty awesome, actually +] +;B[bp]BL[15]OB[5] +;W[er]WL[15]OW[5] +;B[eq]BL[15]OB[5]C[Soromon [1d\]: vicious +] +;W[km]WL[15]OW[5] +;B[ml]BL[15]OB[5] +;W[nl]WL[15]OW[5] +;B[no]BL[15]OB[5] +;W[mk]WL[15]OW[5] +;B[ll]BL[15]OB[5] +;W[lm]WL[15]OW[5] +;B[mm]BL[15]OB[5] +;W[oa]WL[15]OW[5] +;B[na]BL[15]OB[5]C[pasky [-\]: grm +] +;W[pa]WL[15]OW[5] +;B[nb]BL[15]OB[5]C[pasky [-\]: b can't kil? +] +;W[qa]WL[15]OW[5]C[pasky [-\]: hmm l18 is sente +pasky [-\]: so probably not +] +;B[oj]BL[15]OB[5] +;W[oi]WL[15]OW[5] +;B[ni]BL[15]OB[5] +;W[pi]WL[15]OW[5] +;B[ng]BL[15]OB[5] +;W[nf]WL[15]OW[5] +;B[nk]BL[15]OB[5] +;W[mj]WL[15]OW[5] +;B[mi]BL[15]OB[5] +;W[nj]WL[15]OW[5] +;B[lj]BL[15]OB[5] +;W[ok]WL[15]OW[5] +;B[en]BL[15]OB[5] +;W[hr]WL[15]OW[5] +;B[dr]BL[15]OB[5] +;W[em]WL[15]OW[5] +;B[fm]BL[15]OB[5] +;W[hm]WL[15]OW[5] +;B[gl]BL[15]OB[5] +;W[hc]WL[15]OW[5] +;B[kb]BL[15]OB[5] +;W[gc]WL[15]OW[5] +;B[fd]BL[15]OB[5] +;W[eb]WL[15]OW[5] +;B[fc]BL[15]OB[5] +;W[fb]WL[15]OW[5] +;B[dc]BL[15]OB[5] +;W[db]WL[15]OW[5] +;B[ig]BL[15]OB[5] +;W[gg]WL[15]OW[5] +;B[lf]BL[15]OB[5] +;W[hf]WL[15]OW[5] +;B[if]BL[15]OB[5] +;W[hd]WL[15]OW[5] +;B[he]BL[15]OB[5] +;W[ge]WL[15]OW[5] +;B[lk]BL[15]OB[5] +;W[pj]WL[15]OW[5] +;B[lp]BL[15]OB[5] +;W[kp]WL[15]OW[5] +;B[mq]BL[15]OB[5] +;W[mr]WL[15]OW[5] +;B[hi]BL[15]OB[4] +;W[gb]WL[15]OW[5] +;B[gh]BL[15]OB[4] +;W[fg]WL[15]OW[5] +;B[lg]BL[15]OB[4] +;W[ak]WL[15]OW[5] +;B[al]BL[15]OB[4] +;W[le]WL[15]OW[5] +;B[aj]BL[15]OB[4] +;W[ah]WL[15]OW[5] +;B[me]BL[15]OB[4] +;W[mg]WL[15]OW[5] +;B[lh]BL[15]OB[4] +;W[cp]WL[15]OW[5] +;B[co]BL[15]OB[4] +;W[dk]WL[15]OW[5] +;B[bm]BL[15]OB[4] +;W[cr]WL[15]OW[5] +;B[dq]BL[15]OB[4] +;W[bq]WL[15]OW[5] +;B[bo]BL[15]OB[4] +;W[gr]WL[15]OW[5] +;B[fr]BL[15]OB[4] +;W[gm]WL[15]OW[5] +;B[fl]BL[15]OB[4] +;W[ne]WL[15]OW[5] +;B[pf]BL[15]OB[4] +;W[pe]WL[15]OW[5] +;B[nd]BL[15]OB[4] +;W[rp]WL[15]OW[5] +;B[qr]BL[15]OB[4] +;W[ps]WL[15]OW[5] +;B[qs]BL[15]OB[4] +;W[os]WL[15]OW[5] +;B[sp]BL[15]OB[4] +;W[so]WL[15]OW[5] +;B[sq]BL[15]OB[4] +;W[sc]WL[15]OW[5] +;B[rn]BL[15]OB[4] +;W[qn]WL[15]OW[5] +;B[jm]BL[15]OB[4] +;W[il]WL[15]OW[5] +;B[hg]BL[15]OB[4] +;W[gf]WL[15]OW[5] +;B[jl]BL[15]OB[4] +;W[jn]WL[15]OW[5] +;B[im]BL[15]OB[4] +;W[hl]WL[15]OW[5] +;B[in]BL[15]OB[4] +;W[io]WL[15]OW[5] +;B[ro]BL[15]OB[4] +;W[rm]WL[15]OW[5] +;B[oe]BL[15]OB[4] +;W[pf]WL[15]OW[5] +;B[sn]BL[15]OB[4] +;W[sm]WL[15]OW[5] +;B[so]BL[15]OB[4] +;W[fs]WL[15]OW[5] +;B[es]BL[15]OB[4] +;W[mh]WL[15]OW[5] +;B[nh]BL[15]OB[4] +;W[hh]WL[15]OW[5] +;B[gi]BL[15]OB[4] +;W[ij]WL[15]OW[5] +;B[ik]BL[15]OB[4] +;W[hk]WL[15]OW[5] +;B[hj]BL[15]OB[4] +;W[kl]WL[15]OW[5] +;B[kk]BL[15]OB[4] +;W[ih]WL[15]OW[5] +;B[jh]BL[15]OB[4] +;W[ib]WL[15]OW[5] +;B[lo]BL[15]OB[4] +;W[lb]WL[15]OW[5] +;B[lc]BL[15]OB[4] +;W[bl]WL[15]OW[5] +;B[cl]BL[15]OB[4] +;W[ja]WL[15]OW[5] +;B[ka]BL[15]OB[4] +;W[jf]WL[15]OW[5] +;B[jg]BL[15]OB[4] +;W[og]WL[15]OW[5] +;B[li]BL[15]OB[4] +;W[kn]WL[15]OW[5] +;B[sj]BL[15]OB[4] +;W[sk]WL[15]OW[5] +;B[si]BL[15]OB[4] +;W[cs]WL[15]OW[5] +;B[gs]BL[15]OB[4] +;W[se]WL[15]OW[5] +;B[sf]BL[15]OB[4] +;W[np]WL[15]OW[5] +;B[rk]BL[15]OB[4] +;W[ln]WL[15]OW[5] +;B[mo]BL[15]OB[4] +;W[rl]WL[15]OW[5] +;B[qk]BL[15]OB[4]C[pasky [-\]: what...? +pasky [-\]: thanks for the game +jlg [-\]: bug. score dropped very sharply at the end +Soromon [1d\]: ty +pasky [-\]: hmm! +Soromon [1d\]: seems close +]) diff --git a/jni/pachi/t-regress/games/2012-03-15-pachi2-Leech.sgf b/jni/pachi/t-regress/games/2012-03-15-pachi2-Leech.sgf new file mode 100644 index 0000000..edec809 --- /dev/null +++ b/jni/pachi/t-regress/games/2012-03-15-pachi2-Leech.sgf @@ -0,0 +1,247 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]HA[3]KM[0.50]TM[0]OT[5x15 byo-yomi] +PW[pachi2]PB[Leech]WR[4d]BR[1d]DT[2012-03-15]GC[104 L10:bad{semeai} white loses the semeai by many moves, this move is essentially death in gote]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [4d\]: GTP Engine for pachi2 (white): Pachi Distributed version 9.99 (Satsugen-devel): If you believe you have won but I am still playing, please help me understand by capturing all dead stones. Anyone can send me 'winrate' in private chat to get my assessment of the position. Have a nice game! +]RE[B+Resign] +;B[pd]BL[15]OB[5] +;B[dd]BL[15]OB[5] +;B[dp]BL[15]OB[5] +;W[qg]WL[15]OW[5]C[Leech [1d\]: hello mister pachi +] +;B[qi]BL[15]OB[5] +;W[hc]WL[15]OW[5] +;B[qp]BL[15]OB[5] +;W[po]WL[15]OW[5] +;B[pp]BL[15]OB[5]C[fluidistic [6k\]: pachi is close to poland, will be tough game +] +;W[oo]WL[15]OW[5] +;B[rn]BL[15]OB[5] +;W[qo]WL[15]OW[5] +;B[ro]BL[15]OB[5] +;W[qm]WL[15]OW[5] +;B[oi]BL[15]OB[5] +;W[op]WL[15]OW[5] +;B[og]BL[15]OB[5] +;W[cm]WL[15]OW[5] +;B[jq]BL[15]OB[5] +;W[ip]WL[15]OW[5] +;B[jp]BL[15]OB[5] +;W[io]WL[15]OW[5] +;B[kn]BL[15]OB[5] +;W[iq]WL[15]OW[5] +;B[om]BL[15]OB[5] +;W[jo]WL[15]OW[5] +;B[ko]BL[15]OB[5] +;W[lq]WL[15]OW[5] +;B[kp]BL[15]OB[5] +;W[mn]WL[15]OW[5] +;B[gp]BL[15]OB[5] +;W[go]WL[15]OW[5] +;B[fo]BL[15]OB[5] +;W[gn]WL[15]OW[5]C[sztanguo [2k\]: hellow,jlg,how do it setup param pachi on gogui is good?my pc is i72600,i want pachi within 5sec per move? +] +;B[kl]BL[15]OB[5] +;W[ml]WL[15]OW[5] +;B[ok]BL[15]OB[5] +;W[kk]WL[15]OW[5] +;B[il]BL[15]OB[5] +;W[jm]WL[15]OW[5] +;B[km]BL[15]OB[5]C[jlg [-\]: I told you yesterday :-) +] +;W[jl]WL[15]OW[5] +;B[lk]BL[15]OB[5] +;W[jk]WL[15]OW[5]C[sztanguo [2k\]: but it is too weak +] +;B[ll]BL[15]OB[5] +;W[oq]WL[15]OW[5] +;B[pr]BL[15]OB[5]C[jlg [-\]: buy more cores or give it a handicap :-) +] +;W[jr]WL[15]OW[5] +;B[kr]BL[15]OB[5]C[sztanguo [2k\]: how to handicap? +] +;W[hr]WL[15]OW[5] +;B[js]BL[15]OB[5] +;W[qr]WL[15]OW[5] +;B[pq]BL[15]OB[5]C[sztanguo [2k\]: ok ,i find it +] +;W[or]WL[15]OW[5] +;B[qn]BL[15]OB[5]C[jlg [-\]: Gogui -> Help +] +;W[ps]WL[15]OW[5] +;B[rq]BL[15]OB[5] +;W[nl]WL[15]OW[5] +;B[ol]BL[15]OB[5] +;W[nj]WL[15]OW[5] +;B[nk]BL[15]OB[5]C[sztanguo [2k\]: thx +] +;W[mk]WL[15]OW[5] +;B[mm]BL[15]OB[5] +;W[sp]WL[15]OW[5] +;B[rp]BL[15]OB[5] +;W[nm]WL[15]OW[5] +;B[mj]BL[15]OB[5] +;W[nn]WL[15]OW[5] +;B[lj]BL[15]OB[5] +;W[ni]WL[15]OW[5] +;B[oh]BL[15]OB[5] +;W[pn]WL[15]OW[5] +;B[pm]BL[15]OB[5] +;W[ir]WL[15]OW[5] +;B[kq]BL[15]OB[5] +;W[lr]WL[15]OW[5] +;B[mo]BL[15]OB[4] +;W[mp]WL[15]OW[5] +;B[lo]BL[15]OB[4] +;W[mh]WL[15]OW[5] +;B[no]BL[15]OB[4]C[sztanguo [2k\]: i have setup:pachi -t _1200 threads=8,max_tree_size=3072,pondering. can i add book at the same time?and how ? +] +;W[on]WL[15]OW[5] +;B[np]BL[15]OB[4]C[jlg [-\]: do you have 8 cores? +] +;W[kh]WL[15]OW[5]C[sztanguo [2k\]: yes +] +;B[nq]BL[15]OB[4] +;W[nr]WL[15]OW[5] +;B[mq]BL[15]OB[4] +;W[mr]WL[15]OW[5] +;B[os]BL[15]OB[4]C[jlg [-\]: to add book just add -f book.dat as I told you yesterday +] +;W[ls]WL[15]OW[5] +;B[ks]BL[15]OB[4] +;W[oj]WL[15]OW[5] +;B[ql]BL[15]OB[4] +;W[ln]WL[15]OW[5] +;B[ns]BL[15]OB[4]C[yoyoma [2d\]: So this pachi is running >1000 cores? They're connected just by ethernet or... ? +] +;W[lm]WL[15]OW[5] +;B[qs]BL[15]OB[4] +;W[pk]WL[15]OW[5] +;B[rm]BL[15]OB[4] +;W[pi]WL[15]OW[5] +;B[ph]BL[15]OB[4]C[sztanguo [2k\]: pachi -f book.dat,-t_1200 threads=8,max_size=3072,pondering is this ok? +] +;W[pl]WL[15]OW[5] +;B[qm]BL[15]OB[4] +;W[kj]WL[15]OW[5]C[jlg [-\]: no, space instead of , after book.dat +] +;B[pj]BL[15]OB[3] +;W[jn]WL[15]OW[5] +;B[ps]BL[15]OB[3] +;W[de]WL[15]OW[5] +;B[ce]BL[15]OB[3]C[jlg [-\]: yoyoma: see http://pasky.or.cz/go/pachi-tr.pdf +] +;W[cf]WL[15]OW[5] +;B[cd]BL[15]OB[3] +;W[cq]WL[15]OW[5] +;B[cp]BL[15]OB[3] +;W[ed]WL[15]OW[5] +;B[df]BL[15]OB[3] +;W[dg]WL[15]OW[5] +;B[ee]BL[15]OB[3] +;W[ef]WL[15]OW[5] +;B[dq]BL[15]OB[3] +;W[bp]WL[15]OW[5] +;B[bo]BL[15]OB[3] +;W[br]WL[15]OW[5] +;B[ap]BL[15]OB[3] +;W[bq]WL[15]OW[5] +;B[cn]BL[15]OB[3] +;W[dr]WL[15]OW[5] +;B[bm]BL[15]OB[3] +;W[bl]WL[15]OW[5] +;B[cl]BL[15]OB[3] +;W[dm]WL[15]OW[5] +;B[bk]BL[15]OB[3] +;W[ck]WL[15]OW[5] +;B[dl]BL[15]OB[3] +;W[el]WL[15]OW[5] +;B[dk]BL[15]OB[3] +;W[dj]WL[15]OW[5] +;B[ek]BL[15]OB[3] +;W[fk]WL[15]OW[5] +;B[cj]BL[15]OB[3] +;W[ej]WL[15]OW[5] +;B[al]BL[15]OB[3] +;W[pe]WL[15]OW[5]C[yoyoma [2d\]: thx +] +;B[fe]BL[15]OB[3] +;W[li]WL[15]OW[5] +;B[ms]BL[15]OB[3] +;W[ec]WL[15]OW[5] +;B[eg]BL[15]OB[3] +;W[de]WL[15]OW[5] +;B[ff]BL[15]OB[3] +;W[df]WL[15]OW[5] +;B[dh]BL[15]OB[3] +;W[eh]WL[15]OW[5] +;B[bf]BL[15]OB[3] +;W[fg]WL[15]OW[5] +;B[cg]BL[15]OB[3] +;W[eg]WL[15]OW[5] +;B[ch]BL[15]OB[3] +;W[bg]WL[15]OW[5] +;B[be]BL[15]OB[3] +;W[od]WL[15]OW[5] +;B[oc]BL[15]OB[3] +;W[bh]WL[15]OW[5] +;B[ci]BL[15]OB[3] +;W[pc]WL[15]OW[5] +;B[qd]BL[15]OB[3] +;W[nc]WL[15]OW[5] +;B[ob]BL[15]OB[3] +;W[qc]WL[15]OW[5]C[Naphthalin [9k?\]: how is "pachi" pronounced? +] +;B[nd]BL[15]OB[3] +;W[oe]WL[15]OW[5] +;B[mc]BL[15]OB[3] +;W[nb]WL[15]OW[5] +;B[mb]BL[15]OB[3]C[pasky [-\]: like how a japanese person would produce it (at least i hope they'd pronounce it like i think they would :) +pasky [-\]: s/produce/pronounce/ +] +;W[na]WL[15]OW[5] +;B[ma]BL[15]OB[3]C[Naphthalin [9k?\]: and in german? +Naphthalin [9k?\]: or czech +] +;W[md]WL[15]OW[5]C[Naphthalin [9k?\]: or english? +] +;B[oa]BL[15]OB[3]C[pasky [-\]: pachi in big trouble in this game :( +] +;W[kb]WL[15]OW[5]C[pasky [-\]: naphthalin: i think you misunderstood :( +] +;B[rd]BL[15]OB[3]C[yoyoma [2d\]: I believe the English pronounciation of Pachi should be the same as Japanese +Naphthalin [9k?\]: no, i just don't speak japanese +] +;W[rc]WL[15]OW[5]C[yoyoma [2d\]: pa-chi +] +;B[sb]BL[15]OB[3]C[Naphthalin [9k?\]: and asked for a written name which is in english pronounced as pachi should +Leech [1d\]: misclick :( +jlg [-\]: pasky: you can add move 104 L10 to the semai category in t-regress. the semeai was already lost there +] +;W[re]WL[15]OW[5] +;B[ld]BL[15]OB[3] +;W[me]WL[15]OW[5]C[yoyoma [2d\]: oh googletalk is using wrong type of "a" vowel when I use it for english +] +;B[qb]BL[15]OB[3]C[pasky [-\]: yes i'd say in english it's slightly different +Naphthalin [9k?\]: like pudgee? +] +;W[rb]WL[15]OW[5]C[Naphthalin [9k?\]: or pachi? +] +;B[pb]BL[15]OB[3]C[pasky [-\]: naphthalin: I think the "pa" is like in "parkinson" +Leech [1d\]: lucky leech +] +;W[qe]WL[15]OW[5]C[Naphthalin [9k?\]: and the "ch"? +] +;B[kc]BL[15]OB[3] +;W[qa]WL[15]OW[5] +;B[jb]BL[15]OB[3]C[pasky [-\]: like in "chick" +yoyoma [2d\]: go to http://translate.google.com/ +Naphthalin [9k?\]: ah ok +Naphthalin [9k?\]: thx :) +] +;W[jc]WL[15]OW[5]C[yoyoma [2d\]: and enter パチ +Naphthalin [9k?\]: so in the way i thought +] +;B[jd]BL[15]OB[3]C[yoyoma [2d\]: There should be way for me to give direct link to google TTS of it... +jlg [-\]: thanks for the game +pasky [-\]: thanks for the game +Leech [1d\]: thanks +]) diff --git a/jni/pachi/t-regress/games/2012-03-16-IMC-pachi2.sgf b/jni/pachi/t-regress/games/2012-03-16-IMC-pachi2.sgf new file mode 100644 index 0000000..7a4738c --- /dev/null +++ b/jni/pachi/t-regress/games/2012-03-16-IMC-pachi2.sgf @@ -0,0 +1,100 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[7.50]TM[0]OT[5x15 byo-yomi] +PW[IMC]PB[pachi2]WR[4d]BR[4d]DT[2012-03-16]GC[73 +K13:bad!{ladder} pachi plays a (barely) broken ladder]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [4d\]: GTP Engine for pachi2 (black): Pachi Distributed version 9.99 (Satsugen-devel): If you believe you have won but I am still playing, please help me understand by capturing all dead stones. Anyone can send me 'winrate' in private chat to get my assessment of the position. Have a nice game! +]RE[W+Resign] +;B[pp]BL[15]OB[5] +;W[fi]WL[15]OW[5] +;B[cp]BL[15]OB[5] +;W[of]WL[15]OW[5] +;B[pc]BL[15]OB[5] +;W[fp]WL[15]OW[5] +;B[hp]BL[15]OB[5] +;W[fn]WL[15]OW[5] +;B[cm]BL[15]OB[5] +;W[kp]WL[15]OW[5] +;B[mp]BL[15]OB[5] +;W[kn]WL[15]OW[5] +;B[hn]BL[15]OB[5] +;W[fl]WL[15]OW[5] +;B[hl]BL[15]OB[5] +;W[kl]WL[15]OW[5] +;B[cj]BL[15]OB[5] +;W[dl]WL[15]OW[5] +;B[cl]BL[15]OB[5] +;W[ik]WL[15]OW[5] +;B[gk]BL[15]OB[5] +;W[ej]WL[15]OW[5] +;B[fq]BL[15]OB[5] +;W[eq]WL[15]OW[5] +;B[gq]BL[15]OB[5] +;W[dp]WL[15]OW[5] +;B[cq]BL[15]OB[5] +;W[er]WL[15]OW[5] +;B[do]BL[15]OB[5] +;W[eo]WL[15]OW[5] +;B[jq]BL[15]OB[5] +;W[kq]WL[15]OW[5] +;B[jp]BL[15]OB[5] +;W[jo]WL[15]OW[5] +;B[gi]BL[15]OB[5] +;W[gj]WL[15]OW[5] +;B[fk]BL[15]OB[5] +;W[ek]WL[15]OW[5] +;B[fj]BL[15]OB[5] +;W[hj]WL[15]OW[5] +;B[lo]BL[15]OB[5] +;W[ko]WL[15]OW[5] +;B[il]BL[15]OB[5] +;W[jl]WL[15]OW[5] +;B[ei]BL[15]OB[5] +;W[fh]WL[15]OW[5] +;B[eh]BL[15]OB[5] +;W[io]WL[15]OW[5] +;B[ho]BL[15]OB[5] +;W[gm]WL[15]OW[5] +;B[hm]BL[15]OB[5] +;W[dn]WL[15]OW[5] +;B[co]BL[15]OB[5] +;W[kr]WL[15]OW[5] +;B[dm]BL[15]OB[5] +;W[em]WL[15]OW[5] +;B[hi]BL[15]OB[5] +;W[ii]WL[15]OW[5] +;B[fg]BL[15]OB[5] +;W[gh]WL[15]OW[5] +;B[hh]BL[15]OB[5] +;W[gg]WL[15]OW[5] +;B[gf]BL[15]OB[5] +;W[hg]WL[15]OW[5] +;B[ih]BL[15]OB[5] +;W[jh]WL[15]OW[5] +;B[ig]BL[15]OB[5] +;W[hf]WL[15]OW[5] +;B[jm]BL[15]OB[5] +;W[km]WL[15]OW[5] +;B[jk]BL[15]OB[5] +;W[if]WL[15]OW[4] +;B[jg]BL[15]OB[5] +;W[kg]WL[15]OW[4] +;B[jf]BL[15]OB[5] +;W[je]WL[15]OW[4]C[IMC [4d\]: lol +] +;B[dk]BL[15]OB[5] +;W[el]WL[15]OW[4] +;B[kf]BL[15]OB[5] +;W[lf]WL[15]OW[4] +;B[fr]BL[15]OB[5] +;W[ir]WL[15]OW[4] +;B[es]BL[15]OB[5] +;W[dr]WL[15]OW[4] +;B[cr]BL[15]OB[5] +;W[ds]WL[15]OW[4] +;B[jr]BL[15]OB[5] +;W[hr]WL[15]OW[4] +;B[ks]BL[15]OB[5] +;W[ip]WL[15]OW[4] +;B[hq]BL[15]OB[5] +;W[iq]WL[15]OW[4] +;B[dq]BL[15]OB[5] +;W[ep]WL[15]OW[4]C[IMC [4d\]: lol +]) diff --git a/jni/pachi/t-regress/games/2012-03-21-pachi2-Hujisawa-3.sgf b/jni/pachi/t-regress/games/2012-03-21-pachi2-Hujisawa-3.sgf new file mode 100644 index 0000000..b15765d --- /dev/null +++ b/jni/pachi/t-regress/games/2012-03-21-pachi2-Hujisawa-3.sgf @@ -0,0 +1,531 @@ +(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2] +RU[Chinese]SZ[19]KM[0.50]TM[0]OT[5x15 byo-yomi] +PW[pachi2]PB[Hujisawa]WR[4d]BR[3k]DT[2012-03-21]GC[438 +N5:bad! pachi doing fatal self atari because of seki. Should play D7 or A1.]PC[The KGS Go Server at http://www.gokgs.com/]C[pachi2 [4d\]: GTP Engine for pachi2 (white): Pachi Distributed version 9.99 (Satsugen-devel): If you believe you have won but I am still playing, please help me understand by capturing all dead stones. Anyone can send me 'winrate' in private chat to get my assessment of the position. Have a nice game! +] +;B[cp]BL[15]OB[5] +;W[pp]WL[15]OW[5] +;B[nq]BL[15]OB[5] +;W[qn]WL[15]OW[5] +;B[qk]BL[15]OB[5] +;W[qi]WL[15]OW[5] +;B[qg]BL[15]OB[5] +;W[oi]WL[15]OW[5] +;B[ro]BL[15]OB[5] +;W[qo]WL[15]OW[5] +;B[rp]BL[15]OB[5] +;W[qq]WL[15]OW[5] +;B[rn]BL[15]OB[5] +;W[ql]WL[15]OW[5] +;B[rl]BL[15]OB[5] +;W[rm]WL[15]OW[5] +;B[sm]BL[15]OB[5] +;W[qm]WL[15]OW[5] +;B[rj]BL[15]OB[5] +;W[sn]WL[15]OW[5] +;B[sl]BL[15]OB[5] +;W[ri]WL[15]OW[5] +;B[ok]BL[15]OB[5] +;W[pk]WL[15]OW[5] +;B[pj]BL[15]OB[5]C[Ootakamoku [3d\]: b dead +] +;W[pl]WL[15]OW[5] +;B[qj]BL[15]OB[5]C[Ootakamoku [3d\]: t6 hurts now +] +;W[oj]WL[15]OW[5] +;B[pi]BL[15]OB[5] +;W[ph]WL[15]OW[5] +;B[sj]BL[15]OB[5] +;W[si]WL[15]OW[5] +;B[so]BL[15]OB[5]C[Ootakamoku [3d\]: t11 mistake +Ootakamoku [3d\]: could have killed with r12 +] +;W[nk]WL[15]OW[5] +;B[qh]BL[15]OB[5] +;W[kq]WL[15]OW[5] +;B[fq]BL[15]OB[5] +;W[hq]WL[15]OW[5] +;B[jp]BL[15]OB[5] +;W[kp]WL[15]OW[5] +;B[jo]BL[15]OB[5] +;W[jq]WL[15]OW[5] +;B[ko]BL[15]OB[5] +;W[lo]WL[15]OW[5] +;B[ln]BL[15]OB[5] +;W[mn]WL[15]OW[5] +;B[mo]BL[15]OB[5] +;W[lp]WL[15]OW[5] +;B[mm]BL[15]OB[5] +;W[nn]WL[15]OW[5] +;B[km]BL[15]OB[5] +;W[ip]WL[15]OW[5] +;B[nm]BL[15]OB[5] +;W[ol]WL[15]OW[5] +;B[on]BL[15]OB[5] +;W[no]WL[15]OW[5] +;B[oo]BL[15]OB[5] +;W[np]WL[15]OW[5] +;B[op]BL[15]OB[5] +;W[mp]WL[15]OW[5] +;B[pq]BL[15]OB[5] +;W[pr]WL[15]OW[5] +;B[oq]BL[15]OB[5] +;W[qp]WL[15]OW[5] +;B[or]BL[15]OB[5] +;W[ll]WL[15]OW[5] +;B[lm]BL[15]OB[5] +;W[om]WL[15]OW[5] +;B[qr]BL[15]OB[5] +;W[rr]WL[15]OW[5] +;B[ps]BL[15]OB[5] +;W[rq]WL[15]OW[5] +;B[lr]BL[15]OB[5] +;W[il]WL[15]OW[5] +;B[kl]BL[15]OB[5] +;W[io]WL[15]OW[5] +;B[kj]BL[15]OB[5] +;W[kh]WL[15]OW[5] +;B[ji]BL[15]OB[5]C[BOThater36 [5d\]: does b look like 3k? +] +;W[jh]WL[15]OW[5] +;B[ii]BL[15]OB[5] +;W[hg]WL[15]OW[5] +;B[gi]BL[15]OB[5] +;W[pg]WL[15]OW[5] +;B[qf]BL[15]OB[5]C[BOThater36 [5d\]: jlg may pull the plug :-( +] +;W[gl]WL[15]OW[5] +;B[jc]BL[15]OB[5]C[jlg [-\]: I said PROPER HANDICAP in upper case. I will kill pachi at the end of this gamel +] +;W[rs]WL[15]OW[5] +;B[qs]BL[15]OB[5]C[BOThater36 [5d\]: :-( +] +;W[fh]WL[15]OW[5] +;B[ih]BL[15]OB[5]C[BOThater36 [5d\]: at least pachi is winning :-) +] +;W[sk]WL[15]OW[5] +;B[rk]BL[15]OB[5] +;W[ig]WL[15]OW[5] +;B[gh]BL[15]OB[5] +;W[gg]WL[15]OW[5] +;B[fi]BL[15]OB[5] +;W[eh]WL[15]OW[5] +;B[gc]BL[15]OB[5]C[BOThater36 [5d\]: well I have played pachi today, see you guys around :-) +] +;W[dj]WL[15]OW[5] +;B[fk]BL[15]OB[5] +;W[el]WL[15]OW[5] +;B[lk]BL[15]OB[5] +;W[jk]WL[15]OW[5] +;B[mk]BL[15]OB[5] +;W[nl]WL[15]OW[5] +;B[li]BL[15]OB[5] +;W[lh]WL[15]OW[5] +;B[ml]BL[15]OB[5] +;W[mi]WL[15]OW[5] +;B[ki]BL[15]OB[5] +;W[hc]WL[15]OW[5] +;B[hb]BL[15]OB[5] +;W[ic]WL[15]OW[5] +;B[ib]BL[15]OB[5] +;W[ep]WL[15]OW[5] +;B[eq]BL[15]OB[5] +;W[dp]WL[15]OW[5] +;B[dq]BL[15]OB[5] +;W[co]WL[15]OW[5] +;B[bp]BL[15]OB[5] +;W[bo]WL[15]OW[5] +;B[fl]BL[15]OB[5] +;W[fm]WL[15]OW[5] +;B[ek]BL[15]OB[5] +;W[dk]WL[15]OW[5] +;B[ei]BL[15]OB[5] +;W[di]WL[15]OW[5] +;B[em]BL[15]OB[5] +;W[dl]WL[15]OW[5] +;B[gm]BL[15]OB[5] +;W[fn]WL[15]OW[5] +;B[hm]BL[15]OB[5] +;W[im]WL[15]OW[5] +;B[hl]BL[15]OB[5] +;W[hk]WL[15]OW[5] +;B[gk]BL[15]OB[5] +;W[hn]WL[15]OW[5] +;B[hj]BL[15]OB[5] +;W[cd]WL[15]OW[5] +;B[dc]BL[15]OB[5] +;W[dd]WL[15]OW[5] +;B[cc]BL[15]OB[5] +;W[ed]WL[15]OW[5] +;B[fc]BL[15]OB[5] +;W[bc]WL[15]OW[5] +;B[bb]BL[15]OB[5] +;W[ik]WL[15]OW[5] +;B[jn]BL[15]OB[5] +;W[gn]WL[15]OW[5] +;B[gl]BL[15]OB[5] +;W[jm]WL[15]OW[5] +;B[kn]BL[15]OB[5] +;W[in]WL[15]OW[5] +;B[bd]BL[15]OB[5] +;W[be]WL[15]OW[5] +;B[ac]BL[15]OB[5] +;W[pc]WL[15]OW[5] +;B[rc]BL[15]OB[5] +;W[qd]WL[15]OW[5] +;B[rd]BL[15]OB[5] +;W[pf]WL[15]OW[5] +;B[qe]BL[15]OB[5] +;W[hh]WL[15]OW[5] +;B[hi]BL[15]OB[5] +;W[sp]WL[15]OW[5] +;B[sq]BL[15]OB[5] +;W[pe]WL[15]OW[5] +;B[qc]BL[15]OB[5] +;W[br]WL[15]OW[5] +;B[cq]BL[15]OB[5] +;W[gr]WL[15]OW[5] +;B[fr]BL[15]OB[5] +;W[lc]WL[15]OW[5] +;B[kd]BL[15]OB[5] +;W[ld]WL[15]OW[5] +;B[pd]BL[15]OB[5] +;W[od]WL[15]OW[5] +;B[ao]BL[15]OB[5] +;W[an]WL[15]OW[5] +;B[ap]BL[15]OB[5] +;W[bn]WL[15]OW[5] +;B[oc]BL[15]OB[5] +;W[pb]WL[15]OW[5] +;B[ob]BL[15]OB[5] +;W[qd]WL[15]OW[5] +;B[kf]BL[15]OB[5] +;W[pd]WL[15]OW[5] +;B[le]BL[15]OB[5] +;W[jf]WL[15]OW[5] +;B[md]BL[15]OB[5] +;W[mc]WL[15]OW[5] +;B[ge]BL[15]OB[5] +;W[je]WL[15]OW[5] +;B[ke]BL[15]OB[5] +;W[me]WL[15]OW[5] +;B[mf]BL[15]OB[5] +;W[cr]WL[15]OW[5] +;B[dr]BL[15]OB[5] +;W[es]WL[15]OW[5] +;B[fs]BL[15]OB[5] +;W[fd]WL[15]OW[5] +;B[gd]BL[15]OB[5] +;W[ec]WL[15]OW[5] +;B[eb]BL[15]OB[5] +;W[ff]WL[15]OW[5] +;B[fe]BL[15]OB[5] +;W[ee]WL[15]OW[5] +;B[ae]BL[15]OB[5] +;W[bf]WL[15]OW[5] +;B[af]BL[15]OB[5] +;W[ag]WL[15]OW[5] +;B[ad]BL[15]OB[5] +;W[ns]WL[15]OW[5] +;B[nr]BL[15]OB[5] +;W[kr]WL[15]OW[5] +;B[ls]BL[15]OB[5] +;W[gq]WL[15]OW[5] +;B[fp]BL[15]OB[5] +;W[kb]WL[15]OW[5] +;B[id]BL[15]OB[5] +;W[fo]WL[15]OW[5] +;B[gs]BL[15]OB[5] +;W[hs]WL[15]OW[5] +;B[ds]BL[15]OB[5] +;W[bg]WL[15]OW[5] +;B[qb]BL[15]OB[5] +;W[qa]WL[15]OW[5] +;B[ra]BL[15]OB[5] +;W[pa]WL[15]OW[5] +;B[ne]BL[15]OB[5] +;W[nd]WL[15]OW[5] +;B[rh]BL[15]OB[5] +;W[nf]WL[15]OW[5] +;B[me]BL[15]OB[5] +;W[db]WL[15]OW[5] +;B[cb]BL[15]OB[5] +;W[da]WL[15]OW[5] +;B[fb]BL[15]OB[5] +;W[ar]WL[15]OW[5] +;B[gp]BL[15]OB[5] +;W[cs]WL[15]OW[5] +;B[bs]BL[15]OB[5] +;W[go]WL[15]OW[5] +;B[is]BL[15]OB[5] +;W[hr]WL[15]OW[5] +;B[ks]BL[15]OB[5] +;W[hp]WL[15]OW[5] +;B[ng]BL[15]OB[5] +;W[js]WL[15]OW[5] +;B[lq]BL[15]OB[5] +;W[mg]WL[15]OW[5] +;B[of]BL[15]OB[5] +;W[os]WL[15]OW[5] +;B[ms]BL[15]OB[5] +;W[mq]WL[15]OW[5] +;B[mr]BL[15]OB[5] +;W[os]WL[15]OW[5] +;B[ns]BL[15]OB[5] +;W[he]WL[15]OW[5] +;B[ie]BL[15]OB[5] +;W[kc]WL[15]OW[5] +;B[jd]BL[15]OB[5] +;W[nh]WL[15]OW[5] +;B[if]BL[15]OB[5] +;W[hf]WL[15]OW[5] +;B[jg]BL[15]OB[5] +;W[jf]WL[15]OW[5] +;B[hd]BL[15]OB[5] +;W[kg]WL[15]OW[5] +;B[ja]BL[15]OB[5] +;W[do]WL[15]OW[5] +;B[jr]BL[15]OB[5] +;W[sr]WL[15]OW[5] +;B[sh]BL[15]OB[5] +;W[sp]WL[15]OW[5] +;B[sn]BL[15]OB[5] +;W[mj]WL[15]OW[5] +;B[gf]BL[15]OB[5] +;W[lj]WL[15]OW[5] +;B[kk]BL[15]OB[5] +;W[ef]WL[15]OW[5] +;B[ka]BL[15]OB[5] +;W[la]WL[15]OW[5] +;B[lg]BL[15]OB[5] +;W[lf]WL[15]OW[5] +;B[jb]BL[15]OB[5] +;W[ab]WL[15]OW[5] +;B[aa]BL[15]OB[5] +;W[og]WL[15]OW[5] +;B[nf]BL[15]OB[5] +;W[lg]WL[15]OW[5] +;B[ma]BL[15]OB[5] +;W[na]WL[15]OW[5] +;B[lb]BL[15]OB[5] +;W[mb]WL[15]OW[5] +;B[la]BL[15]OB[5] +;W[ba]WL[15]OW[5] +;B[ca]BL[15]OB[5] +;W[nc]WL[15]OW[5] +;B[ej]BL[15]OB[5] +;W[ir]WL[15]OW[5] +;B[fg]BL[15]OB[5] +;W[eg]WL[15]OW[5] +;B[js]BL[15]OB[5] +;W[sq]WL[15]OW[5] +;B[pn]BL[15]OB[5] +;W[jg]WL[15]OW[5] +;B[po]BL[15]OB[5]C[replicator [3d\]: perhaps someone from Korea cannot read that and used automatch to set up a game +] +;W[jj]WL[15]OW[5] +;B[jl]BL[15]OB[5] +;W[rb]WL[15]OW[5] +;B[sb]BL[15]OB[5] +;W[je]WL[15]OW[5] +;B[oe]BL[15]OB[5] +;W[bj]WL[15]OW[5] +;B[pm]BL[15]OB[5] +;W[ij]WL[15]OW[5] +;B[er]BL[15]OB[5] +;W[aq]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[eo]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[bm]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[re]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[sc]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[sd]WL[15]OW[5] +;B[se]BL[15]OB[5] +;W[fa]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[sf]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[bh]WL[15]OW[5] +;B[]BL[15]OB[5]C[jlg [-\]: pachi will play till the end because of the seki. +] +;W[ha]WL[15]OW[5] +;B[]BL[15]OB[5]C[Learn [4d\]: 30k +] +;W[sc]WL[15]OW[5] +(;B[rf]BL[15]OB[5]C[BOThater36 [5d\]: if you can kill fill all the liberty of a group, that grup is alive +BOThater36 [5d\]: can not +replicator [3d\]: Yeah! +] +;W[ea]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[gb]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[hc]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[ok]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[oa]WL[15]OW[5] +;B[]BL[15]OB[5]C[BOThater36 [5d\]: a game of 350+ moves +jlg [-\]: pachi will pass once all eyes are reduced to one point only +] +;W[cf]WL[15]OW[5] +;B[]BL[15]OB[5]C[BOThater36 [5d\]: yes +] +;W[ce]WL[15]OW[5] +;B[]BL[15]OB[5]C[BOThater36 [5d\]: programmers know this, but 50% of Go players dont know +] +;W[ah]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[ss]WL[15]OW[5] +;B[]BL[15]OB[5]C[jlg [-\]: it is not easy to handle seki correctly in a program. We would do it if there was an easy solution +] +;W[rg]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[ia]WL[15]OW[5] +;B[ga]BL[15]OB[5]C[Learn [4d\]: thx god +] +;W[db]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[qi]WL[15]OW[5] +;B[]BL[15]OB[5]C[replicator [3d\]: Solution= In addition to marking dead stones- have a seki marking tool +Learn [4d\]: w need b3 +] +;W[si]WL[15]OW[5]C[BOThater36 [5d\]: In Yahoo go, in rder to finish a game, u need to make all territory to honeycumb +] +;B[]BL[15]OB[5] +;W[da]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[fa]WL[15]OW[5] +;B[]BL[15]OB[5]C[alpha080 [?\]: 应氏规则 +] +;W[ha]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[ai]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[ci]WL[15]OW[5]C[alpha080 [?\]: ying chang qi rules +] +;B[]BL[15]OB[5]C[replicator [3d\]: I think pachi only does this when it wants you to resign +Mateman [2k\]: lol +] +;W[bi]WL[15]OW[5] +;B[]BL[15]OB[5]C[Learn [4d\]: w need b3. thx +jlg [-\]: of course if pachi was losing it would have resigned already +] +;W[dg]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[cg]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[nj]WL[15]OW[5] +;B[]BL[15]OB[5] +;W[ni]WL[15]OW[5] +;B[]BL[15]OB[5]C[BOThater36 [5d\]: B must feel so happy that wite keep filling his own territory +] +;W[nb]WL[15]OW[5] +;B[]BL[15]OB[5]C[BOThater36 [5d\]: and end up he still lose +] +;W[ob]WL[15]OW[5] +;B[]BL[15]OB[5]C[jlg [-\]: koreans use chinese rules normally right ? +BOThater36 [5d\]: no, japanese +] +;W[am]WL[15]OW[5] +;B[]BL[15]OB[5]C[jlg [-\]: ah that may explain it +] +;W[fj]WL[15]OW[5] +;B[cl]BL[15]OB[5]C[BOThater36 [5d\]: lol +] +;W[cm]WL[15]OW[5] +;B[bl]BL[15]OB[5]C[BOThater36 [5d\]: so before you play bot, you need to know the bot (Chinese rule) :-) +] +;W[al]WL[15]OW[5] +;B[bk]BL[15]OB[5] +;W[oh]WL[15]OW[5] +;B[dn]BL[15]OB[5]C[CptObvious [5k?\]: jlg, when i say winrate to pachi, does it get my username? if yes, one could implement a queue command, for the next players +] +;W[ch]WL[15]OW[5] +;B[dm]BL[15]OB[5] +;W[ho]WL[15]OW[5] +;B[en]BL[15]OB[5]C[jlg [-\]: yes it gets your name +] +;W[dh]WL[15]OW[5] +;B[ck]BL[15]OB[5]C[jlg [-\]: but kgs decides who plays next, not pachi +] +;W[cn]WL[15]OW[5] +;B[en]BL[15]OB[5]C[BOThater36 [5d\]: I hate bots, but I dont hate bot rule. in fact I love bot Go rule +CptObvious [5k?\]: you can put it into the config, i think +] +;W[ak]WL[15]OW[5] +;B[em]BL[15]OB[5] +;W[cj]WL[15]OW[5]C[CptObvious [5k?\]: you would have to reconnect after every game +] +;B[cl]BL[15]OB[5]C[jlg [-\]: I would have to kill the kgs connection to reload the config +] +;W[gb]WL[15]OW[5] +;B[ck]BL[15]OB[5] +;W[ea]WL[15]OW[5] +;B[bk]BL[15]OB[5] +;W[ia]WL[15]OW[5] +;B[ga]BL[15]OB[5] +;W[ia]WL[15]OW[5] +;B[dm]BL[15]OB[5] +;W[db]WL[15]OW[5] +;B[ea]BL[15]OB[5] +;W[dn]WL[15]OW[5] +;B[em]BL[15]OB[5]C[Learn [4d\]: :) +] +;W[bl]WL[15]OW[5] +;B[ck]BL[15]OB[5] +;W[df]WL[15]OW[5] +;B[cl]BL[15]OB[5]C[Learn [4d\]: panchi2 must fill its terriyery +] +;W[bk]WL[15]OW[5] +;B[cl]BL[15]OB[5]C[BOThater36 [5d\]: eventually all has to pass +] +;W[ck]WL[15]OW[5] +;B[dm]BL[15]OB[5] +;W[en]WL[15]OW[5] +;B[em]BL[15]OB[5]C[replicator [3d\]: upto 435 moves now....going for the record +] +;W[mo]WL[15]OW[5] +;B[iq]BL[15]OB[5] +;W[jq]WL[15]OW[5] +;B[kq]BL[15]OB[5]C[jlg [-\]: Killing pachi because of incorrect handicap. +BOThater36 [5d\]: why pachi doing that? desperate or bug? +Mateman [2k\]: 0-5 +]) +(;B[]BL[15]OB[5]C[alpha080 [?\]: how to end the game with pachi? +] +;W[]WL[15]OW[5]TW[oa][ra][sa][nb][ob][qb][rb][sb][oc][qc][rc][rd][sd][ce][de][qe][se][cf][df][qf][rf][cg][dg][fg][qg][rg][sg][ah][ch][dh][mh][oh][qh][rh][sh][ai][bi][ci][ni][pi][qi][ri][si][aj][cj][nj][pj][qj][rj][sj][ak][bk][ck][ok][qk][rk][sk][al][bl][cl][rl][sl][am][cm][dm][em][sm][cn][dn][en][rn][sn][ho][mo][ro][so][rp][iq][as][bs][ss]TB[ba][da][ea][fa][ga][ha][ia][ab][db][gb][bc][hc][ic][fj][gj][ll][pr][es][os]C[Learn [4d\]: it will take seki stones after finished +pachi2 [4d\]: You and the engine seem to disagree about which stones are dead. To solve this problem, you can press "undo", play until all dead stones are removed from the board, then score again. +Learn [4d\]: ha ha +jlg [-\]: the game ends normally by resign or 2 passes. ony in case of seki pachi has a problem. +jlg [-\]: B will get tired of this before pachi +Learn [4d\]: :) +jlg [-\]: In case of seki you have to capture all dead stones +Learn [4d\]: panchi2 stop it +jlg [-\]: this is explained in pachi2 info +replicator [3d\]: play probably can't read the recommended solution +replicator [3d\]: player +Learn [4d\]: u canot take alive stones +CptObvious [5k?\]: would s14 solve the problem here? +ihatelag [3k\]: just play S14! +jlg [-\]: to solve the problem it is sufficient to capture all dead stones +alpha080 [?\]: ... +ihatelag [3k\]: why doesn't black just play s14 +jlg [-\]: again this is only needed in case of seki +justice [1k\]: hahaha.... +ihatelag [3k\]: its chiense rules +replicator [3d\]: you are assuming they understand english +alpha080 [?\]: chinese rules or japanese rules? +jlg [-\]: is there a korean reading this? please tell B to either capture the dead stones or resign. +jlg [-\]: this is chinese rules, capturing dead stones doesn't cost anything +Learn [4d\]: canot talk when playing +replicator [3d\]: all the bots play by chinese counting so their final moves inside their own territory do not lose points +replicator [3d\]: but I have seen many such moves before dame are filled so they lose points anyway +jlg [-\]: yes you must fill all dame before capturing the dead stones +BOThater36 [5d\]: welcme to the cyber world :-) +BOThater36 [5d\]: thats why Ing rule is perfect in cyber Go +Learn [4d\]: i knew b had a bad yose to creat such ......... +])) diff --git a/jni/pachi/t-regress/test-game.sh b/jni/pachi/t-regress/test-game.sh new file mode 100644 index 0000000..03e1268 --- /dev/null +++ b/jni/pachi/t-regress/test-game.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# test-game: Run testcases within a single game +# +# Usage: test-game.sh FILENAME +# +# Examine a game record and run testcases for all moves described +# in the "game comment". +# +# Pass any extra Pachi parameters in PACHIARGS. E.g. +# PACHIARGS='-t =100000 -d 2 threads=2' testgame.sh ... + +sgf="$1" + +sed -ne '/GC\[[0-9][0-9]* .*\]/{s/.*GC\[\([^]]*\)\].*/\1/p; q;}; /GC\[/,/\]/{ s/.*GC\[//; s/\].*//; /^[0-9][0-9]* /p; }' <"$sgf" | + while read moven cases desc; do + echo "Examining move $moven"; sleep 1 + tools/sgf2gtp.pl -g -n $((moven-1)) <"$sgf" | ./pachi -t =20000 $PACHIARGS + echo "Testcases: $cases ($desc)" + echo "Confirm and press enter..."; read xx +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "tactics/selfatari.h" +#include "random.h" + +static bool board_printed; + +void +board_load(struct board *b, FILE *f, unsigned int size) +{ + board_printed = false; + board_resize(b, size); + board_clear(b); + for (int y = size - 1; y >= 0; y--) { + char line[256]; + if (!fgets(line, sizeof(line), f)) { + fprintf(stderr, "Premature EOF.\n"); + exit(EXIT_FAILURE); + } + line[strlen(line) - 1] = 0; // chomp + if (strlen(line) != size) { + fprintf(stderr, "Line not %d char long: %s\n", size, line); + exit(EXIT_FAILURE); + } + for (unsigned int x = 0; x < size; x++) { + enum stone s; + switch (line[x]) { + case '.': s = S_NONE; break; + case 'X': s = S_BLACK; break; + case 'O': s = S_WHITE; break; + default: fprintf(stderr, "Invalid stone %c\n", line[x]); + exit(EXIT_FAILURE); + } + if (s == S_NONE) continue; + struct move m = { .color = s, .coord = coord_xy(b, x + 1, y + 1) }; + if (board_play(b, &m) < 0) { + fprintf(stderr, "Failed to play %s %s\n", + stone2str(s), coord2sstr(m.coord, b)); + board_print(b, stderr); + exit(EXIT_FAILURE); + } + } + } + if (DEBUGL(2)) + board_print(b, stderr); +} + +void +test_sar(struct board *b, char *arg) +{ + enum stone color = str2stone(arg); + arg += 2; + coord_t *cc = str2coord(arg, board_size(b)); + coord_t c = *cc; coord_done(cc); + arg += strcspn(arg, " ") + 1; + int eres = atoi(arg); + if (DEBUGL(1)) + printf("sar %s %s %d...\t", stone2str(color), coord2sstr(c, b), eres); + + int rres = is_bad_selfatari(b, color, c); + + if (rres == eres) { + if (DEBUGL(1)) + printf("OK\n"); + } else { + if (debug_level <= 2) { + if (DEBUGL(0) && !board_printed) { + board_print(b, stderr); + board_printed = true; + } + printf("sar %s %s %d...\t", stone2str(color), coord2sstr(c, b), eres); + } + printf("FAILED (%d)\n", rres); + } + +} + +void +unittest(char *filename) +{ + FILE *f = fopen(filename, "r"); + if (!f) { + perror(filename); + exit(EXIT_FAILURE); + } + + struct board *b = board_init(NULL); + char line[256]; + while (fgets(line, sizeof(line), f)) { + line[strlen(line) - 1] = 0; // chomp + switch (line[0]) { + case '%': printf("\n%s\n", line); continue; + case 0: continue; + } + if (!strncmp(line, "boardsize ", 10)) { + board_load(b, f, atoi(line + 10)); + } else if (!strncmp(line, "sar ", 4)) { + test_sar(b, line + 4); + } else { + fprintf(stderr, "Syntax error: %s\n", line); + exit(EXIT_FAILURE); + } + } + + fclose(f); +} diff --git a/jni/pachi/t-unit/test.h b/jni/pachi/t-unit/test.h new file mode 100644 index 0000000..adc27d6 --- /dev/null +++ b/jni/pachi/t-unit/test.h @@ -0,0 +1,6 @@ +#ifndef PACHI_T_UNIT_TEST_H +#define PACHI_T_UNIT_TEST_H + +void unittest(char *filename); + +#endif diff --git a/jni/pachi/tactics/1lib.c b/jni/pachi/tactics/1lib.c new file mode 100644 index 0000000..8b6a1dd --- /dev/null +++ b/jni/pachi/tactics/1lib.c @@ -0,0 +1,160 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "mq.h" +#include "tactics/1lib.h" +#include "tactics/ladder.h" +#include "tactics/selfatari.h" + + +/* Whether to avoid capturing/atariing doomed groups (this is big + * performance hit and may reduce playouts balance; it does increase + * the strength, but not quite proportionally to the performance). */ +//#define NO_DOOMED_GROUPS + + +static bool +can_play_on_lib(struct board *b, group_t g, enum stone to_play) +{ + coord_t capture = board_group_info(b, g).lib[0]; + if (DEBUGL(6)) + fprintf(stderr, "can capture group %d (%s)?\n", + g, coord2sstr(capture, b)); + /* Does playing on the liberty usefully capture the group? */ + if (board_is_valid_play(b, to_play, capture) + && !is_bad_selfatari(b, to_play, capture)) + return true; + + return false; +} + +/* For given position @c, decide if this is a group that is in danger from + * @capturer and @to_play can do anything about it (play at the last + * liberty to either capture or escape). */ +/* Note that @to_play is important; e.g. consider snapback, it's good + * to play at the last liberty by attacker, but not defender. */ +static inline __attribute__((always_inline)) bool +capturable_group(struct board *b, enum stone capturer, coord_t c, + enum stone to_play) +{ + group_t g = group_at(b, c); + if (likely(board_at(b, c) != stone_other(capturer) + || board_group_info(b, g).libs > 1)) + return false; + + return can_play_on_lib(b, g, to_play); +} + +bool +can_countercapture(struct board *b, enum stone owner, group_t g, + enum stone to_play, struct move_queue *q, int tag) +{ + if (b->clen < 2) + return false; + + unsigned int qmoves_prev = q ? q->moves : 0; + + foreach_in_group(b, g) { + foreach_neighbor(b, c, { + if (!capturable_group(b, owner, c, to_play)) + continue; + + if (!q) { + return true; + } + mq_add(q, board_group_info(b, group_at(b, c)).lib[0], tag); + mq_nodup(q); + }); + } foreach_in_group_end; + + bool can = q ? q->moves > qmoves_prev : false; + return can; +} + +#ifdef NO_DOOMED_GROUPS +static bool +can_be_rescued(struct board *b, group_t group, enum stone color, int tag) +{ + /* Does playing on the liberty rescue the group? */ + if (can_play_on_lib(b, group, color)) + return true; + + /* Then, maybe we can capture one of our neighbors? */ + return can_countercapture(b, color, group, color, NULL, tag); +} +#endif + +void +group_atari_check(unsigned int alwaysccaprate, struct board *b, group_t group, enum stone to_play, + struct move_queue *q, coord_t *ladder, bool middle_ladder, int tag) +{ + enum stone color = board_at(b, group_base(group)); + coord_t lib = board_group_info(b, group).lib[0]; + + assert(color != S_OFFBOARD && color != S_NONE); + if (DEBUGL(5)) + fprintf(stderr, "[%s] atariiiiiiiii %s of color %d\n", + coord2sstr(group, b), coord2sstr(lib, b), color); + assert(board_at(b, lib) == S_NONE); + + if (to_play != color) { + /* We are the attacker! In that case, do not try defending + * our group, since we can capture the culprit. */ +#ifdef NO_DOOMED_GROUPS + /* Do not remove group that cannot be saved by the opponent. */ + if (!can_be_rescued(b, group, color, tag)) + return; +#endif + if (can_play_on_lib(b, group, to_play)) { + mq_add(q, lib, tag); + mq_nodup(q); + } + return; + } + + /* Can we capture some neighbor? */ + bool ccap = can_countercapture(b, color, group, to_play, q, tag); + if (ccap && !ladder && alwaysccaprate > fast_random(100)) + return; + + /* Otherwise, do not save kos. */ + if (group_is_onestone(b, group) + && neighbor_count_at(b, lib, color) + neighbor_count_at(b, lib, S_OFFBOARD) == 4) { + /* Except when the ko is for an eye! */ + bool eyeconnect = false; + foreach_diag_neighbor(b, lib) { + if (board_at(b, c) == S_NONE && neighbor_count_at(b, c, color) + neighbor_count_at(b, c, S_OFFBOARD) == 4) { + eyeconnect = true; + break; + } + } foreach_diag_neighbor_end; + if (!eyeconnect) + return; + } + + /* Do not suicide... */ + if (!can_play_on_lib(b, group, to_play)) + return; + if (DEBUGL(6)) + fprintf(stderr, "...escape route valid\n"); + + /* ...or play out ladders (unless we can counter-capture anytime). */ + if (!ccap) { + if (is_ladder(b, lib, group, middle_ladder)) { + /* Sometimes we want to keep the ladder move in the + * queue in order to discourage it. */ + if (!ladder) + return; + else + *ladder = lib; + } else if (DEBUGL(6)) + fprintf(stderr, "...no ladder\n"); + } + + mq_add(q, lib, tag); + mq_nodup(q); +} diff --git a/jni/pachi/tactics/1lib.h b/jni/pachi/tactics/1lib.h new file mode 100644 index 0000000..a9a0024 --- /dev/null +++ b/jni/pachi/tactics/1lib.h @@ -0,0 +1,24 @@ +#ifndef PACHI_TACTICS_1LIB_H +#define PACHI_TACTICS_1LIB_H + +/* One-liberty tactical checks (i.e. dealing with atari situations). */ + +#include "board.h" +#include "debug.h" + +struct move_queue; + + +/* For given atari group @group owned by @owner, decide if @to_play + * can save it / keep it in danger by dealing with one of the + * neighboring groups. */ +bool can_countercapture(struct board *b, enum stone owner, group_t g, + enum stone to_play, struct move_queue *q, int tag); + +/* Examine given group in atari, suggesting suitable moves for player + * @to_play to deal with it (rescuing or capturing it). */ +/* ladder != NULL implies to always enqueue all relevant moves. */ +void group_atari_check(unsigned int alwaysccaprate, struct board *b, group_t group, enum stone to_play, + struct move_queue *q, coord_t *ladder, bool middle_ladder, int tag); + +#endif diff --git a/jni/pachi/tactics/2lib.c b/jni/pachi/tactics/2lib.c new file mode 100644 index 0000000..f6ff082 --- /dev/null +++ b/jni/pachi/tactics/2lib.c @@ -0,0 +1,251 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "mq.h" +#include "tactics/2lib.h" +#include "tactics/selfatari.h" + + +/* Whether to avoid capturing/atariing doomed groups (this is big + * performance hit and may reduce playouts balance; it does increase + * the strength, but not quite proportionally to the performance). */ +//#define NO_DOOMED_GROUPS + + +static bool +miai_2lib(struct board *b, group_t group, enum stone color) +{ + bool can_connect = false, can_pull_out = false; + /* We have miai if we can either connect on both libs, + * or connect on one lib and escape on another. (Just + * having two escape routes can be risky.) We must make + * sure that we don't consider following as miai: + * X X X O + * X . . O + * O O X O - left dot would be pull-out, right dot connect */ + foreach_neighbor(b, board_group_info(b, group).lib[0], { + enum stone cc = board_at(b, c); + if (cc == S_NONE && cc != board_at(b, board_group_info(b, group).lib[1])) { + can_pull_out = true; + } else if (cc != color) { + continue; + } + + group_t cg = group_at(b, c); + if (cg && cg != group && board_group_info(b, cg).libs > 1) + can_connect = true; + }); + foreach_neighbor(b, board_group_info(b, group).lib[1], { + enum stone cc = board_at(b, c); + if (c == board_group_info(b, group).lib[0]) + continue; + if (cc == S_NONE && can_connect) { + return true; + } else if (cc != color) { + continue; + } + + group_t cg = group_at(b, c); + if (cg && cg != group && board_group_info(b, cg).libs > 1) + return (can_connect || can_pull_out); + }); + return false; +} + +static bool +defense_is_hopeless(struct board *b, group_t group, enum stone owner, + enum stone to_play, coord_t lib, coord_t otherlib, + bool use) +{ + /* If we are the defender not connecting out, do not + * escape with moves that do not gain liberties anyway + * - either the new extension has just single extra + * liberty, or the "gained" liberties are shared. */ + /* XXX: We do not check connecting to a short-on-liberty + * group (e.g. ourselves). */ + if (DEBUGL(7)) + fprintf(stderr, "\tif_check %d and defending %d and uscount %d ilcount %d\n", + use, to_play == owner, + neighbor_count_at(b, lib, owner), + immediate_liberty_count(b, lib)); + if (!use) + return false; + if (to_play == owner && neighbor_count_at(b, lib, owner) == 1) { + if (immediate_liberty_count(b, lib) == 1) + return true; + if (immediate_liberty_count(b, lib) == 2 + && coord_is_adjecent(lib, otherlib, b)) + return true; + } + return false; +} + +void +can_atari_group(struct board *b, group_t group, enum stone owner, + enum stone to_play, struct move_queue *q, + int tag, bool use_def_no_hopeless) +{ + bool have[2] = { false, false }; + bool preference[2] = { true, true }; + for (int i = 0; i < 2; i++) { + coord_t lib = board_group_info(b, group).lib[i]; + assert(board_at(b, lib) == S_NONE); + if (!board_is_valid_play(b, to_play, lib)) + continue; + + if (DEBUGL(6)) + fprintf(stderr, "- checking liberty %s of %s %s, filled by %s\n", + coord2sstr(lib, b), + stone2str(owner), coord2sstr(group, b), + stone2str(to_play)); + + /* Don't play at the spot if it is extremely short + * of liberties... */ + /* XXX: This looks harmful, could significantly + * prefer atari to throwin: + * + * XXXOOOOOXX + * .OO.....OX + * XXXOOOOOOX */ +#if 0 + if (neighbor_count_at(b, lib, stone_other(owner)) + immediate_liberty_count(b, lib) < 2) + continue; +#endif + + /* Prevent hopeless escape attempts. */ + if (defense_is_hopeless(b, group, owner, to_play, lib, + board_group_info(b, group).lib[1 - i], + use_def_no_hopeless)) + continue; + +#ifdef NO_DOOMED_GROUPS + /* If the owner can't play at the spot, we don't want + * to bother either. */ + if (is_bad_selfatari(b, owner, lib)) + continue; +#endif + + /* Of course we don't want to play bad selfatari + * ourselves, if we are the attacker... */ + if ( +#ifdef NO_DOOMED_GROUPS + to_play != owner && +#endif + is_bad_selfatari(b, to_play, lib)) { + if (DEBUGL(7)) + fprintf(stderr, "\tliberty is selfatari\n"); + coord_t coord = pass; + group_t bygroup = 0; + if (to_play != owner) { + /* Okay! We are attacker; maybe we just need + * to connect a false eye before atari - this + * is very common in the corner. */ + coord = selfatari_cousin(b, to_play, lib, &bygroup); + } + if (is_pass(coord)) + continue; + /* Ok, connect, but prefer not to. */ + enum stone byowner = board_at(b, bygroup); + if (DEBUGL(7)) + fprintf(stderr, "\treluctantly switching to cousin %s (group %s %s)\n", + coord2sstr(coord, b), coord2sstr(bygroup, b), stone2str(byowner)); + /* One more thing - is the cousin sensible defense + * for the other group? */ + if (defense_is_hopeless(b, bygroup, byowner, to_play, + coord, lib, + use_def_no_hopeless)) + continue; + lib = coord; + preference[i] = false; + + /* By now, we must be decided we add the move to the + * queue! [comment intentionally misindented] */ + + } + + have[i] = true; + + /* If the move is too "lumpy", prefer the alternative: + * + * ####### + * ..O.X.X <- always play the left one! + * OXXXXXX */ + if (neighbor_count_at(b, lib, to_play) + neighbor_count_at(b, lib, S_OFFBOARD) >= 3) { + if (DEBUGL(7)) + fprintf(stderr, "\tlumpy: mine %d + edge %d\n", + neighbor_count_at(b, lib, to_play), + neighbor_count_at(b, lib, S_OFFBOARD)); + preference[i] = false; + } + + if (DEBUGL(6)) + fprintf(stderr, "+ liberty %s ready with preference %d\n", coord2sstr(lib, b), preference[i]); + + /* If we prefer only one of the moves, pick that one. */ + if (i == 1 && have[0] && preference[0] != preference[1]) { + if (!preference[0]) { + if (q->move[q->moves - 1] == board_group_info(b, group).lib[0]) + q->moves--; + /* ...else{ may happen, since we call + * mq_nodup() and the move might have + * been there earlier. */ + } else { + assert(!preference[1]); + continue; + } + } + + /* Tasty! Crispy! Good! */ + mq_add(q, lib, tag); + mq_nodup(q); + } + + if (DEBUGL(7)) { + char label[256]; + snprintf(label, 256, "= final %s %s liberties to play by %s", + stone2str(owner), coord2sstr(group, b), + stone2str(to_play)); + mq_print(q, b, label); + } +} + +void +group_2lib_check(struct board *b, group_t group, enum stone to_play, struct move_queue *q, int tag, bool use_miaisafe, bool use_def_no_hopeless) +{ + enum stone color = board_at(b, group_base(group)); + assert(color != S_OFFBOARD && color != S_NONE); + + if (DEBUGL(5)) + fprintf(stderr, "[%s] 2lib check of color %d\n", + coord2sstr(group, b), color); + + /* Do not try to atari groups that cannot be harmed. */ + if (use_miaisafe && miai_2lib(b, group, color)) + return; + + can_atari_group(b, group, color, to_play, q, tag, use_def_no_hopeless); + + /* Can we counter-atari another group, if we are the defender? */ + if (to_play != color) + return; + foreach_in_group(b, group) { + foreach_neighbor(b, c, { + if (board_at(b, c) != stone_other(color)) + continue; + group_t g2 = group_at(b, c); + if (board_group_info(b, g2).libs == 1) { + /* We can capture a neighbor. */ + mq_add(q, board_group_info(b, g2).lib[0], tag); + mq_nodup(q); + continue; + } + if (board_group_info(b, g2).libs != 2) + continue; + can_atari_group(b, g2, stone_other(color), to_play, q, tag, use_def_no_hopeless); + }); + } foreach_in_group_end; +} diff --git a/jni/pachi/tactics/2lib.h b/jni/pachi/tactics/2lib.h new file mode 100644 index 0000000..d261dd3 --- /dev/null +++ b/jni/pachi/tactics/2lib.h @@ -0,0 +1,15 @@ +#ifndef PACHI_TACTICS_2LIB_H +#define PACHI_TACTICS_2LIB_H + +/* Two-liberty tactical checks (i.e. dealing with two-step capturing races, + * preventing atari). */ + +#include "board.h" +#include "debug.h" + +struct move_queue; + +void can_atari_group(struct board *b, group_t group, enum stone owner, enum stone to_play, struct move_queue *q, int tag, bool use_def_no_hopeless); +void group_2lib_check(struct board *b, group_t group, enum stone to_play, struct move_queue *q, int tag, bool use_miaisafe, bool use_def_no_hopeless); + +#endif diff --git a/jni/pachi/tactics/Makefile b/jni/pachi/tactics/Makefile new file mode 100644 index 0000000..344dde3 --- /dev/null +++ b/jni/pachi/tactics/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=1lib.o 2lib.o nlib.o ladder.o nakade.o selfatari.o util.o + +all: tactics.a +tactics.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/tactics/ladder.c b/jni/pachi/tactics/ladder.c new file mode 100644 index 0000000..6dd9567 --- /dev/null +++ b/jni/pachi/tactics/ladder.c @@ -0,0 +1,229 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "mq.h" +#include "tactics/1lib.h" +#include "tactics/ladder.h" + + +bool +is_border_ladder(struct board *b, coord_t coord, enum stone lcolor) +{ + int x = coord_x(coord, b), y = coord_y(coord, b); + + if (DEBUGL(5)) + fprintf(stderr, "border ladder\n"); + /* Direction along border; xd is horiz. border, yd vertical. */ + int xd = 0, yd = 0; + if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD) + yd = 1; + else + xd = 1; + /* Direction from the border; -1 is above/left, 1 is below/right. */ + int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1; + if (DEBUGL(6)) + fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd); + /* | ? ? + * | . O # + * | c X # + * | . O # + * | ? ? */ + /* This is normally caught, unless we have friends both above + * and below... */ + if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor + && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor) + return false; + /* ...or can't block where we need because of shortage + * of liberties. */ + int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs; + int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs; + if (DEBUGL(6)) + fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2); + if (libs1 < 2 || libs2 < 2) + return false; + if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3) + return false; + if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3) + return false; + return true; +} + + +/* This is a rather expensive ladder reader. It can read out any sequences + * where laddered group should be kept at two liberties. The recursion + * always makes a "to-be-laddered" move and then considers the chaser's + * two alternatives (usually, one of them is trivially refutable). The + * function returns true if there is a branch that ends up with laddered + * group captured, false if not (i.e. for each branch, laddered group can + * gain three liberties). */ + +static bool +middle_ladder_walk(struct board *b, struct board *bset, group_t laddered, coord_t nextmove, enum stone lcolor) +{ + assert(board_group_info(b, laddered).libs == 1); + + /* First, escape. */ + if (DEBUGL(6)) + fprintf(stderr, " ladder escape %s\n", coord2sstr(nextmove, b)); + struct move m = { nextmove, lcolor }; + int res = board_play(b, &m); + laddered = group_at(b, laddered); + assert(res >= 0); + if (DEBUGL(8)) { + board_print(b, stderr); + fprintf(stderr, "%s c %d\n", coord2sstr(laddered, b), board_group_info(b, laddered).libs); + } + + if (board_group_info(b, laddered).libs == 1) { + if (DEBUGL(6)) + fprintf(stderr, "* we can capture now\n"); + return true; + } + if (board_group_info(b, laddered).libs > 2) { + if (DEBUGL(6)) + fprintf(stderr, "* we are free now\n"); + return false; + } + + foreach_neighbor(b, m.coord, { + if (board_at(b, c) == stone_other(lcolor) && board_group_info(b, group_at(b, c)).libs == 1) { + /* We can capture one of the ladder stones + * anytime later. */ + /* XXX: If we were very lucky, capturing + * this stone will not help us escape. + * That should be pretty rate. */ + if (DEBUGL(6)) + fprintf(stderr, "* can capture chaser\n"); + return false; + } + }); + + /* Now, consider alternatives. */ + int liblist[2], libs = 0; + for (int i = 0; i < 2; i++) { + coord_t ataristone = board_group_info(b, laddered).lib[i]; + coord_t escape = board_group_info(b, laddered).lib[1 - i]; + if (immediate_liberty_count(b, escape) > 2 + coord_is_adjecent(ataristone, escape, b)) { + /* Too much free space, ignore. */ + continue; + } + liblist[libs++] = i; + } + + /* Try out the alternatives. */ + bool is_ladder = false; + for (int i = 0; !is_ladder && i < libs; i++) { + struct board *b2 = b; + if (i != libs - 1) { + b2 = bset++; + board_copy(b2, b); + } + + coord_t ataristone = board_group_info(b2, laddered).lib[liblist[i]]; + // coord_t escape = board_group_info(b2, laddered).lib[1 - liblist[i]]; + struct move m = { ataristone, stone_other(lcolor) }; + int res = board_play(b2, &m); + /* If we just played self-atari, abandon ship. */ + /* XXX: If we were very lucky, capturing this stone will + * not help us escape. That should be pretty rate. */ + if (DEBUGL(6)) + fprintf(stderr, "(%d=%d) ladder atari %s (%d libs)\n", i, res, coord2sstr(ataristone, b2), board_group_info(b2, group_at(b2, ataristone)).libs); + if (res >= 0 && board_group_info(b2, group_at(b2, ataristone)).libs > 1) + is_ladder = middle_ladder_walk(b2, bset, laddered, board_group_info(b2, laddered).lib[0], lcolor); + + if (i != libs - 1) { + board_done_noalloc(b2); + } + } + if (DEBUGL(6)) + fprintf(stderr, "propagating %d\n", is_ladder); + return is_ladder; +} + +bool +is_middle_ladder(struct board *b, coord_t coord, group_t laddered, enum stone lcolor) +{ + /* TODO: Remove the redundant parameters. */ + assert(board_group_info(b, laddered).libs == 1); + assert(board_group_info(b, laddered).lib[0] == coord); + assert(board_at(b, laddered) == lcolor); + + /* If we can move into empty space or do not have enough space + * to escape, this is obviously not a ladder. */ + if (immediate_liberty_count(b, coord) != 2) { + if (DEBUGL(5)) + fprintf(stderr, "no ladder, wrong free space\n"); + return false; + } + + /* A fair chance for a ladder. Group in atari, with some but limited + * space to escape. Time for the expensive stuff - set up a temporary + * board and start selective 2-liberty search. */ + + struct board *bset = malloc2(BOARD_MAX_SIZE * 2 * sizeof(struct board)); + + struct move_queue ccq = { .moves = 0 }; + if (can_countercapture(b, lcolor, laddered, lcolor, &ccq, 0)) { + /* We could escape by countercapturing a group. + * Investigate. */ + assert(ccq.moves > 0); + for (unsigned int i = 0; i < ccq.moves; i++) { + struct board b2; + board_copy(&b2, b); + bool is_ladder = middle_ladder_walk(&b2, bset, laddered, ccq.move[i], lcolor); + board_done_noalloc(&b2); + if (!is_ladder) { + free(bset); + return false; + } + } + } + + struct board b2; + board_copy(&b2, b); + bool is_ladder = middle_ladder_walk(&b2, bset, laddered, board_group_info(&b2, laddered).lib[0], lcolor); + board_done_noalloc(&b2); + free(bset); + return is_ladder; +} + +bool +wouldbe_ladder(struct board *b, group_t group, coord_t escapelib, coord_t chaselib, enum stone lcolor) +{ + assert(board_group_info(b, group).libs == 2); + assert(board_at(b, group) == lcolor); + + if (DEBUGL(6)) + fprintf(stderr, "would-be ladder check - does %s %s play out chasing move %s?\n", + stone2str(lcolor), coord2sstr(escapelib, b), coord2sstr(chaselib, b)); + + if (!coord_is_8adjecent(escapelib, chaselib, b)) { + if (DEBUGL(5)) + fprintf(stderr, "cannot determine ladder for remote simulated stone\n"); + return false; + } + + if (neighbor_count_at(b, chaselib, lcolor) != 1 || immediate_liberty_count(b, chaselib) != 2) { + if (DEBUGL(5)) + fprintf(stderr, "overly trivial for a ladder\n"); + return false; + } + + bool is_ladder = false; + struct board *bset = malloc2(BOARD_MAX_SIZE * 2 * sizeof(struct board)); + struct board b2; + board_copy(&b2, b); + + struct move m = { chaselib, stone_other(lcolor) }; + int res = board_play(&b2, &m); + if (res >= 0) + is_ladder = middle_ladder_walk(&b2, bset, group, board_group_info(&b2, group).lib[0], lcolor); + + board_done_noalloc(&b2); + free(bset); + return is_ladder; +} diff --git a/jni/pachi/tactics/ladder.h b/jni/pachi/tactics/ladder.h new file mode 100644 index 0000000..ad53f80 --- /dev/null +++ b/jni/pachi/tactics/ladder.h @@ -0,0 +1,45 @@ +#ifndef PACHI_TACTICS_LADDER_H +#define PACHI_TACTICS_LADDER_H + +/* Reading ladders. */ + +#include "board.h" +#include "debug.h" + +/* Check if escaping on this liberty by given group in atari would play out + * a simple ladder. */ +/* Two ways of ladder reading can be enabled separately; simple first-line + * ladders and trivial middle-board ladders. */ +static bool is_ladder(struct board *b, coord_t coord, group_t laddered, bool test_middle); + +/* Check if a 2-lib group of color @lcolor escaping at @escapelib would be + * caught in a ladder given opponent stone at @chaselib. */ +bool wouldbe_ladder(struct board *b, group_t group, coord_t escapelib, coord_t chaselib, enum stone lcolor); + + +bool is_border_ladder(struct board *b, coord_t coord, enum stone lcolor); +bool is_middle_ladder(struct board *b, coord_t coord, group_t group, enum stone lcolor); +static inline bool +is_ladder(struct board *b, coord_t coord, group_t laddered, bool test_middle) +{ + enum stone lcolor = board_at(b, group_base(laddered)); + + if (DEBUGL(6)) + fprintf(stderr, "ladder check - does %s play out %s's laddered group %s?\n", + coord2sstr(coord, b), stone2str(lcolor), coord2sstr(laddered, b)); + + /* First, special-case first-line "ladders". This is a huge chunk + * of ladders we actually meet and want to play. */ + if (neighbor_count_at(b, coord, S_OFFBOARD) == 1 + && neighbor_count_at(b, coord, lcolor) == 1) { + bool l = is_border_ladder(b, coord, lcolor); + if (DEBUGL(6)) fprintf(stderr, "border ladder solution: %d\n", l); + return l; + } + + bool l = test_middle && is_middle_ladder(b, coord, laddered, lcolor); + if (DEBUGL(6)) fprintf(stderr, "middle ladder solution: %d\n", l); + return l; +} + +#endif diff --git a/jni/pachi/tactics/nakade.c b/jni/pachi/tactics/nakade.c new file mode 100644 index 0000000..257dc49 --- /dev/null +++ b/jni/pachi/tactics/nakade.c @@ -0,0 +1,82 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "move.h" +#include "tactics/nakade.h" + + +coord_t +nakade_point(struct board *b, coord_t around, enum stone color) +{ + /* First, examine the nakade area. For sure, it must be at most + * six points. And it must be within color group(s). */ +#define NAKADE_MAX 6 + coord_t area[NAKADE_MAX]; int area_n = 0; + + area[area_n++] = around; + + for (int i = 0; i < area_n; i++) { + foreach_neighbor(b, area[i], { + if (board_at(b, c) == stone_other(color)) + return pass; + if (board_at(b, c) == S_NONE) { + bool dup = false; + for (int j = 0; j < area_n; j++) + if (c == area[j]) { + dup = true; + break; + } + if (dup) continue; + + if (area_n >= NAKADE_MAX) { + /* Too large nakade area. */ + return pass; + } + area[area_n++] = c; + } + }); + } + + /* We also collect adjecency information - how many neighbors + * we have for each area point, and histogram of this. This helps + * us verify the appropriate bulkiness of the shape. */ + int neighbors[area_n]; int ptbynei[9] = {area_n, 0}; + memset(neighbors, 0, sizeof(neighbors)); + for (int i = 0; i < area_n; i++) { + for (int j = i + 1; j < area_n; j++) + if (coord_is_adjecent(area[i], area[j], b)) { + ptbynei[neighbors[i]]--; + neighbors[i]++; + ptbynei[neighbors[i]]++; + ptbynei[neighbors[j]]--; + neighbors[j]++; + ptbynei[neighbors[j]]++; + } + } + + /* For each given neighbor count, arbitrary one coordinate + * featuring that. */ + coord_t coordbynei[9]; + for (int i = 0; i < area_n; i++) + coordbynei[neighbors[i]] = area[i]; + + switch (area_n) { + case 1: return pass; + case 2: return pass; + case 3: assert(ptbynei[2] == 1); + return coordbynei[2]; // middle point + case 4: if (ptbynei[3] != 1) return pass; // long line + return coordbynei[3]; // tetris four + case 5: if (ptbynei[3] == 1 && ptbynei[1] == 1) return coordbynei[3]; // bulky five + if (ptbynei[4] == 1) return coordbynei[4]; // cross five + return pass; // long line + case 6: if (ptbynei[4] == 1 && ptbynei[2] == 3) + return coordbynei[4]; // rabbity six + return pass; // anything else + default: assert(0); + } +} diff --git a/jni/pachi/tactics/nakade.h b/jni/pachi/tactics/nakade.h new file mode 100644 index 0000000..34741e7 --- /dev/null +++ b/jni/pachi/tactics/nakade.h @@ -0,0 +1,14 @@ +#ifndef PACHI_TACTICS_NAKADE_H +#define PACHI_TACTICS_NAKADE_H + +/* Piercing eyes. */ + +#include "board.h" +#include "debug.h" + +/* Find an eye-piercing point within the @around area of empty board + * internal to group of color @color. + * Returns pass if the area is not a nakade shape or not internal. */ +coord_t nakade_point(struct board *b, coord_t around, enum stone color); + +#endif diff --git a/jni/pachi/tactics/nlib.c b/jni/pachi/tactics/nlib.c new file mode 100644 index 0000000..cd6e54d --- /dev/null +++ b/jni/pachi/tactics/nlib.c @@ -0,0 +1,87 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "mq.h" +#include "tactics/2lib.h" +#include "tactics/nlib.h" +#include "tactics/selfatari.h" + + +void +group_nlib_defense_check(struct board *b, group_t group, enum stone to_play, struct move_queue *q, int tag) +{ + enum stone color = to_play; + assert(color != S_OFFBOARD && color != S_NONE + && color == board_at(b, group_base(group))); + + if (DEBUGL(5)) + fprintf(stderr, "[%s] nlib defense check of color %d\n", + coord2sstr(group, b), color); + +#if 0 + /* XXX: The code below is specific for 3-liberty groups. Its impact + * needs to be tested first, and possibly moved to a more appropriate + * place. */ + + /* First, look at our liberties. */ + int continuous = 0, enemy = 0, spacy = 0, eyes = 0; + for (int i = 0; i < 3; i++) { + coord_t c = board_group_info(b, group).lib[i]; + eyes += board_is_one_point_eye(b, c, to_play); + continuous += coord_is_adjecent(c, board_group_info(b, group).lib[(i + 1) % 3], b); + enemy += neighbor_count_at(b, c, stone_other(color)); + spacy += immediate_liberty_count(b, c) > 1; + } + + /* Safe groups are boring. */ + if (eyes > 1) + return; + + /* If all our liberties are in single line and they are internal, + * this is likely a tiny three-point eyespace that we rather want + * to live at! */ + assert(continuous < 3); + if (continuous == 2 && !enemy && spacy == 1) { + assert(!eyes); + int i; + for (i = 0; i < 3; i++) + if (immediate_liberty_count(b, board_group_info(b, group).lib[i]) == 2) + break; + /* Play at middle point. */ + mq_add(q, board_group_info(b, group).lib[i], tag); + mq_nodup(q); + return; + } +#endif + + /* "Escaping" (gaining more liberties) with many-liberty group + * is difficult. Do not even try. */ + + /* There is another way to gain safety - through winning semeai + * with another group. */ + /* We will not look at taking liberties of enemy n-groups, since + * we do not try to gain liberties for own n-groups. That would + * be really unbalanced (and most of our liberty-taking moves + * would be really stupid, most likely). */ + + /* However, it is possible that we must start capturing a 2-lib + * neighbor right now, because of approach liberties. Therefore, + * we will check for this case. If we take a liberty of a group + * even if we could have waited another move, no big harm done + * either. */ + + foreach_in_group(b, group) { + foreach_neighbor(b, c, { + if (board_at(b, c) != stone_other(color)) + continue; + group_t g2 = group_at(b, c); + if (board_group_info(b, g2).libs != 2) + continue; + can_atari_group(b, g2, stone_other(color), to_play, q, tag, true /* XXX */); + }); + } foreach_in_group_end; +} diff --git a/jni/pachi/tactics/nlib.h b/jni/pachi/tactics/nlib.h new file mode 100644 index 0000000..c328420 --- /dev/null +++ b/jni/pachi/tactics/nlib.h @@ -0,0 +1,13 @@ +#ifndef PACHI_TACTICS_NLIB_H +#define PACHI_TACTICS_NLIB_H + +/* N-liberty semeai defense tactical checks. */ + +#include "board.h" +#include "debug.h" + +struct move_queue; + +void group_nlib_defense_check(struct board *b, group_t group, enum stone to_play, struct move_queue *q, int tag); + +#endif diff --git a/jni/pachi/tactics/selfatari.c b/jni/pachi/tactics/selfatari.c new file mode 100644 index 0000000..7cc366e --- /dev/null +++ b/jni/pachi/tactics/selfatari.c @@ -0,0 +1,566 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "random.h" +#include "tactics/selfatari.h" + + +struct selfatari_state { + int groupcts[S_MAX]; + group_t groupids[S_MAX][4]; + coord_t groupneis[S_MAX][4]; + + /* This is set if this move puts a group out of _all_ + * liberties; we need to watch out for snapback then. */ + bool friend_has_no_libs; + /* We may have one liberty, but be looking for one more. + * In that case, @needs_more_lib is id of group + * already providing one, don't consider it again. */ + group_t needs_more_lib; + /* ID of the first liberty, providing it again is not + * interesting. */ + coord_t needs_more_lib_except; +}; + +static bool +three_liberty_suicide(struct board *b, group_t g, enum stone color, coord_t to, struct selfatari_state *s) +{ + /* If a group has three liberties, by playing on one of + * them it is possible to kill the group clumsily. Check + * against that condition: "After our move, the opponent + * can unconditionally capture the group." + * + * Examples: + * + * O O O O O O O X X O O O O O O v-v- ladder + * O X X X X X O . O X X X X X O . . . O O + * O X ! . ! X O . O X ! . ! O . O X X . O + * O X X X X X O # # # # # # # # O O O O O */ + + /* Extract the other two liberties. */ + coord_t other_libs[2]; + bool other_libs_adj[2]; + for (int i = 0, j = 0; i < 3; i++) { + coord_t lib = board_group_info(b, g).lib[i]; + if (lib != to) { + other_libs_adj[j] = coord_is_adjecent(lib, to, b); + other_libs[j++] = lib; + } + } + + /* Make sure this move is not useful by gaining liberties, + * splitting the other two liberties (quite possibly splitting + * 3-eyespace!) or connecting to a different group. */ + if (immediate_liberty_count(b, to) - (other_libs_adj[0] || other_libs_adj[1]) > 0) + return false; + assert(!(other_libs_adj[0] && other_libs_adj[1])); + if (s->groupcts[color] > 1) + return false; + + /* Playing on the third liberty might be useful if it enables + * capturing some group (are we doing nakade or semeai?). */ + for (int i = 0; i < s->groupcts[stone_other(color)]; i++) + if (board_group_info(b, s->groupids[stone_other(color)][i]).libs <= 3) + return false; + + + /* Okay. This looks like a pretty dangerous situation. The + * move looks useless, it definitely converts us to a 2-lib + * group. But we still want to play it e.g. if it takes off + * liberties of some unconspicous enemy group, and of course + * also at the game end to leave just single-point eyes. */ + + if (DEBUGL(6)) + fprintf(stderr, "3-lib danger\n"); + + /* Therefore, the final suicidal test is: (After filling this + * liberty,) when opponent fills liberty [0], playing liberty + * [1] will not help the group, or vice versa. */ + bool other_libs_neighbors = coord_is_adjecent(other_libs[0], other_libs[1], b); + for (int i = 0; i < 2; i++) { + int null_libs = other_libs_neighbors + other_libs_adj[i]; + if (board_is_one_point_eye(b, other_libs[1 - i], color)) { + /* The other liberty is an eye, happily go ahead. + * There are of course situations where this will + * take off semeai liberties, but without this check, + * many terminal endgame plays will be messed up. */ + return false; + } + if (immediate_liberty_count(b, other_libs[i]) - null_libs > 1) { + /* Gains liberties. */ + /* TODO: Check for ladder! */ +next_lib: + continue; + } + foreach_neighbor(b, other_libs[i], { + if (board_at(b, c) == color + && group_at(b, c) != g + && board_group_info(b, group_at(b, c)).libs > 1) { + /* Can connect to a friend. */ + /* TODO: > 2? But maybe the group can capture + * a neighbor! But then better let it do that + * first? */ + goto next_lib; + } + }); + /* If we can capture a neighbor, better do it now + * before wasting a liberty. So no need to check. */ + /* Ok, the last liberty has no way to get out. */ + if (DEBUGL(6)) + fprintf(stderr, "3-lib dangerous: %s\n", coord2sstr(other_libs[i], b)); + return true; + } + + return false; +} + +static int +examine_friendly_groups(struct board *b, enum stone color, coord_t to, struct selfatari_state *s) +{ + for (int i = 0; i < s->groupcts[color]; i++) { + /* We can escape by connecting to this group if it's + * not in atari. */ + group_t g = s->groupids[color][i]; + + if (board_group_info(b, g).libs == 1) { + if (!s->needs_more_lib) + s->friend_has_no_libs = true; + // or we already have a friend with 1 lib + continue; + } + + /* Could we self-atari the group here? */ + if (board_group_info(b, g).libs > 2) { + if (board_group_info(b, g).libs == 3 + && three_liberty_suicide(b, g, color, to, s)) + return true; + return false; + } + + /* We need to have another liberty, and + * it must not be the other liberty of + * the group. */ + int lib2 = board_group_other_lib(b, g, to); + /* Maybe we already looked at another + * group providing one liberty? */ + if (s->needs_more_lib && s->needs_more_lib != g + && s->needs_more_lib_except != lib2) + return false; + + /* Can we get the liberty locally? */ + /* Yes if we are route to more liberties... */ + if (s->groupcts[S_NONE] > 1) + return false; + /* ...or one liberty, but not lib2. */ + if (s->groupcts[S_NONE] > 0 + && !coord_is_adjecent(lib2, to, b)) + return false; + + /* ...ok, then we can still contribute a liberty + * later by capturing something. */ + s->needs_more_lib = g; + s->needs_more_lib_except = lib2; + s->friend_has_no_libs = false; + } + + return -1; +} + +static int +examine_enemy_groups(struct board *b, enum stone color, coord_t to, struct selfatari_state *s) +{ + /* We may be able to gain a liberty by capturing this group. */ + group_t can_capture = 0; + + /* Examine enemy groups: */ + for (int i = 0; i < s->groupcts[stone_other(color)]; i++) { + /* We can escape by capturing this group if it's in atari. */ + group_t g = s->groupids[stone_other(color)][i]; + if (board_group_info(b, g).libs > 1) + continue; + + /* But we need to get to at least two liberties by this; + * we already have one outside liberty, or the group is + * more than 1 stone (in that case, capturing is always + * nice!). */ + if (s->groupcts[S_NONE] > 0 || !group_is_onestone(b, g)) + return false; + /* ...or, it's a ko stone, */ + if (neighbor_count_at(b, g, color) + neighbor_count_at(b, g, S_OFFBOARD) == 3) { + /* and we don't have a group to save: then, just taking + * single stone means snapback! */ + if (!s->friend_has_no_libs) + return false; + } + /* ...or, we already have one indirect liberty provided + * by another group. */ + if (s->needs_more_lib || (can_capture && can_capture != g)) + return false; + can_capture = g; + + } + + if (DEBUGL(6)) + fprintf(stderr, "no cap group\n"); + + if (!s->needs_more_lib && !can_capture && !s->groupcts[S_NONE]) { + /* We have no hope for more fancy tactics - this move is simply + * a suicide, not even a self-atari. */ + if (DEBUGL(6)) + fprintf(stderr, "suicide\n"); + return true; + } + /* XXX: I wonder if it makes sense to continue if we actually + * just !s->needs_more_lib. */ + + return -1; +} + +static int +setup_nakade_or_snapback(struct board *b, enum stone color, coord_t to, struct selfatari_state *s) +{ + /* There is another possibility - we can self-atari if it is + * a nakade: we put an enemy group in atari from the inside. */ + /* This branch also allows eyes falsification: + * O O O . . (This is different from throw-in to false eye + * X X O O . checked below in that there is no X stone at the + * X . X O . right of the star point in this diagram.) + * X X X O O + * X O * . . */ + /* TODO: Allow to only nakade if the created shape is dead + * (http://senseis.xmp.net/?Nakade). */ + + /* This branch also covers snapback, which is kind of special + * nakade case. ;-) */ + + /* Look at the enemy groups and determine the other contended + * liberty. We must make sure the liberty: + * (i) is an internal liberty + * (ii) filling it to capture our group will not gain safety */ + coord_t lib2 = pass; + for (int i = 0; i < s->groupcts[stone_other(color)]; i++) { + group_t g = s->groupids[stone_other(color)][i]; + if (board_group_info(b, g).libs != 2) + continue; + + coord_t this_lib2 = board_group_other_lib(b, g, to); + if (is_pass(lib2)) + lib2 = this_lib2; + else if (this_lib2 != lib2) { + /* If we have two neighboring groups that do + * not share the other liberty, this for sure + * is not a good nakade. */ + return -1; + } + } + if (is_pass(lib2)) { + /* Not putting any group in atari. Therefore, this + * self-atari is not nakade or snapback. */ + return -1; + } + + /* Let's look at neighbors of the other liberty: */ + foreach_neighbor(b, lib2, { + /* This neighbor of course does not contribute + * anything to the enemy. */ + if (board_at(b, c) == S_OFFBOARD) + continue; + + /* If the other liberty has empty neighbor, + * it must be the original liberty; otherwise, + * since the whole group has only 2 liberties, + * the other liberty may not be internal and + * we are nakade'ing eyeless group from outside, + * which is stupid. */ + if (board_at(b, c) == S_NONE) { + if (c == to) + continue; + else + return -1; + } + + int g2 = group_at(b, c); + /* If the neighbor is of our color, it must + * be also a 2-lib group. If it is more, + * we CERTAINLY want that liberty to be played + * first, what if it is an alive group? If it + * is in atari, we want to extend from it to + * prevent eye-making capture. However, if it + * is 2-lib, it is selfatari connecting two + * nakade'ing groups! */ + /* X X X X We will not allow play on 'a', + * X X a X because 'b' would capture two + * X O b X different groups, forming two + * X X X X eyes. */ + if (board_at(b, c) == color) { + if (board_group_info(b, group_at(b, c)).libs == 2) + continue; + return -1; + } + + /* The neighbor is enemy color. It's ok if this is its + * only liberty or it's one of our neighbor groups. */ + if (board_group_info(b, g2).libs == 1) + continue; + if (board_group_info(b, g2).libs == 2 + && (board_group_info(b, g2).lib[0] == to + || board_group_info(b, g2).lib[1] == to)) + continue; + + /* Stronger enemy group. No nakade. */ + return -1; + }); + + /* Now, we must distinguish between nakade and eye + * falsification; we must not falsify an eye by more + * than two stones. */ + if (s->groupcts[color] < 1) + return false; // simple throw-in + if (s->groupcts[color] == 1 && group_is_onestone(b, s->groupids[color][0])) { + /* More complex throw-in - we are in one of + * three situations: + * a O O O O X b O O O X c O O O X + * O . X . O O X . . O . X . + * # # # # # # # # # # # # # + * b is desirable here (since maybe O has no + * backup two eyes); a may be desirable, but + * is tested next in check_throwin(). c is + * never desirable. */ + group_t g2 = s->groupids[color][0]; + assert(board_group_info(b, g2).libs <= 2); + if (board_group_info(b, g2).libs == 1) + return false; // b + return -1; // a or c + } + + /* We would create more than 2-stone group; in that + * case, the liberty of our result must be lib2, + * indicating this really is a nakade. */ + int stones = 0; + for (int j = 0; j < s->groupcts[color]; j++) { + group_t g2 = s->groupids[color][j]; + assert(board_group_info(b, g2).libs <= 2); + if (board_group_info(b, g2).libs == 2) { + if (board_group_info(b, g2).lib[0] != lib2 + && board_group_info(b, g2).lib[1] != lib2) + return -1; + } else { + assert(board_group_info(b, g2).lib[0] == to); + } + /* See below: */ + stones += group_stone_count(b, g2, 6); + // fprintf(stderr, "%d (%d,%d) %d,%d\n", __LINE__, j, g2, stones); + if (stones > 5) + return true; + } + + /* It also remains to be seen whether it is nakade + * and not seki destruction. To do this properly, we + * would have to look at the group shape. But we can + * cheat too! Brett Combs helps to introduce a static + * rule that should in fact cover *all* cases: + * 1. Total number of pre-selfatari nakade stones must + * be 5 or smaller. (See above for that.) + * 2. If the selfatari is 8-touching all nakade stones, + * it is proper nakade. + * 3. Otherwise, there must be only a single nakade + * group, it must be at least 4-stone and its other + * liberty must be 8-touching the same number of + * stones as us. */ + int touch8 = neighbor_count_at(b, to, color); + foreach_diag_neighbor(b, to) { + if (board_at(b, c) != color) continue; + /* Consider only internal stones. Otherwise, e.g. + * X O . X + * X . O X can make trouble, bottom O is + * O X X X irrelevant. */ + if (board_group_info(b, group_at(b, c)).lib[0] == to + || board_group_info(b, group_at(b, c)).lib[1] == to) + touch8++; + } foreach_diag_neighbor_end; + if (touch8 == stones) + return false; + + if (s->groupcts[color] > 1 || stones < 4) + return true; + int ltouch8 = neighbor_count_at(b, lib2, color); + foreach_diag_neighbor(b, lib2) { + if (board_at(b, c) != color) continue; + if (board_group_info(b, group_at(b, c)).lib[0] == to + || board_group_info(b, group_at(b, c)).lib[1] == to) + ltouch8++; + } foreach_diag_neighbor_end; + return ltouch8 != touch8; +} + +static int +check_throwin(struct board *b, enum stone color, coord_t to, struct selfatari_state *s) +{ + /* We can be throwing-in to false eye: + * X X X O X X X O X X X X X + * X . * X * O . X * O O . X + * # # # # # # # # # # # # # */ + /* We cannot sensibly throw-in into a corner. */ + if (neighbor_count_at(b, to, S_OFFBOARD) < 2 + && neighbor_count_at(b, to, stone_other(color)) + + neighbor_count_at(b, to, S_OFFBOARD) == 3 + && board_is_false_eyelike(b, to, stone_other(color))) { + assert(s->groupcts[color] <= 1); + /* Single-stone throw-in may be ok... */ + if (s->groupcts[color] == 0) { + /* O X . There is one problem - when it's + * . * X actually not a throw-in! + * # # # */ + foreach_neighbor(b, to, { + if (board_at(b, c) == S_NONE) { + /* Is the empty neighbor an escape path? */ + /* (Note that one S_NONE neighbor is already @to.) */ + if (neighbor_count_at(b, c, stone_other(color)) + + neighbor_count_at(b, c, S_OFFBOARD) < 2) + return -1; + } + }); + return false; + } + + /* Multi-stone throwin...? */ + assert(s->groupcts[color] == 1); + group_t g = s->groupids[color][0]; + + assert(board_group_info(b, g).libs <= 2); + /* Suicide is definitely NOT ok, no matter what else + * we could test. */ + if (board_group_info(b, g).libs == 1) + return true; + + /* In that case, we must be connected to at most one stone, + * or throwin will not destroy any eyes. */ + if (group_is_onestone(b, g)) + return false; + } + return -1; +} + +bool +is_bad_selfatari_slow(struct board *b, enum stone color, coord_t to) +{ + if (DEBUGL(5)) + fprintf(stderr, "sar check %s %s\n", stone2str(color), coord2sstr(to, b)); + /* Assess if we actually gain any liberties by this escape route. + * Note that this is not 100% as we cannot check whether we are + * connecting out or just to ourselves. */ + + struct selfatari_state s; + memset(&s, 0, sizeof(s)); + int d; + + foreach_neighbor(b, to, { + enum stone color = board_at(b, c); + group_t group = group_at(b, c); + bool dup = false; + for (int i = 0; i < s.groupcts[color]; i++) + if (s.groupids[color][i] == group) { + dup = true; + break; + } + if (!dup) { + s.groupneis[color][s.groupcts[color]] = c; + s.groupids[color][s.groupcts[color]++] = group_at(b, c); + } + }); + + /* We have shortage of liberties; that's the point. */ + assert(s.groupcts[S_NONE] <= 1); + + d = examine_friendly_groups(b, color, to, &s); + if (d >= 0) + return d; + + if (DEBUGL(6)) + fprintf(stderr, "no friendly group\n"); + + d = examine_enemy_groups(b, color, to, &s); + if (d >= 0) + return d; + + if (DEBUGL(6)) + fprintf(stderr, "no escape\n"); + + d = setup_nakade_or_snapback(b, color, to, &s); + if (d >= 0) + return d; + + if (DEBUGL(6)) + fprintf(stderr, "no nakade group\n"); + + d = check_throwin(b, color, to, &s); + if (d >= 0) + return d; + + if (DEBUGL(6)) + fprintf(stderr, "no throw-in group\n"); + + /* No way to pull out, no way to connect out. This really + * is a bad self-atari! */ + return true; +} + + +coord_t +selfatari_cousin(struct board *b, enum stone color, coord_t coord, group_t *bygroup) +{ + group_t groups[4]; int groups_n = 0; + int groupsbycolor[4] = {0, 0, 0, 0}; + if (DEBUGL(6)) + fprintf(stderr, "cousin group search: "); + foreach_neighbor(b, coord, { + enum stone s = board_at(b, c); + group_t g = group_at(b, c); + if (board_group_info(b, g).libs == 2) { + groups[groups_n++] = g; + groupsbycolor[s]++; + if (DEBUGL(6)) + fprintf(stderr, "%s(%s) ", coord2sstr(c, b), stone2str(s)); + } + }); + if (DEBUGL(6)) + fprintf(stderr, "\n"); + + if (!groups_n) + return pass; + + int gn; + if (groupsbycolor[stone_other(color)]) { + /* Prefer to fill the other liberty of an opponent + * group to filling own approach liberties. */ + int gl = fast_random(groups_n); + for (gn = gl; gn < groups_n; gn++) + if (board_at(b, groups[gn]) == stone_other(color)) + goto found; + for (gn = 0; gn < gl; gn++) + if (board_at(b, groups[gn]) == stone_other(color)) + goto found; +found:; + } else { + gn = fast_random(groups_n); + } + int gl = gn; + for (; gn - gl < groups_n; gn++) { + int gnm = gn % groups_n; + group_t group = groups[gnm]; + + coord_t lib2 = board_group_other_lib(b, group, coord); + if (board_is_one_point_eye(b, lib2, board_at(b, group))) + continue; + if (is_bad_selfatari(b, color, lib2)) + continue; + if (bygroup) + *bygroup = group; + return lib2; + } + return pass; +} diff --git a/jni/pachi/tactics/selfatari.h b/jni/pachi/tactics/selfatari.h new file mode 100644 index 0000000..087cfd6 --- /dev/null +++ b/jni/pachi/tactics/selfatari.h @@ -0,0 +1,36 @@ +#ifndef PACHI_TACTICS_SELFATARI_H +#define PACHI_TACTICS_SELFATARI_H + +/* A fairly reliable elf-atari detector. */ + +#include "board.h" +#include "debug.h" + +/* Check if this move is undesirable self-atari (resulting group would have + * only single liberty and not capture anything; ko is allowed); we mostly + * want to avoid these moves. The function actually does a rather elaborate + * tactical check, allowing self-atari moves that are nakade, eye falsification + * or throw-ins. */ +static bool is_bad_selfatari(struct board *b, enum stone color, coord_t to); + +/* Move (color, coord) is a selfatari; this means that it puts a group of + * ours in atari; i.e., the group has two liberties now. Return the other + * liberty of such a troublesome group (optionally stored at *bygroup) + * if that one is not a self-atari. + * (In case (color, coord) is a multi-selfatari, consider a randomly chosen + * candidate.) */ +coord_t selfatari_cousin(struct board *b, enum stone color, coord_t coord, group_t *bygroup); + + +bool is_bad_selfatari_slow(struct board *b, enum stone color, coord_t to); +static inline bool +is_bad_selfatari(struct board *b, enum stone color, coord_t to) +{ + /* More than one immediate liberty, thumbs up! */ + if (immediate_liberty_count(b, to) > 1) + return false; + + return is_bad_selfatari_slow(b, color, to); +} + +#endif diff --git a/jni/pachi/tactics/util.c b/jni/pachi/tactics/util.c new file mode 100644 index 0000000..a17bbe0 --- /dev/null +++ b/jni/pachi/tactics/util.c @@ -0,0 +1,124 @@ +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "tactics/util.h" + + +bool +board_stone_radar(struct board *b, coord_t coord, int distance) +{ + int bounds[4] = { + coord_x(coord, b) - distance, + coord_y(coord, b) - distance, + coord_x(coord, b) + distance, + coord_y(coord, b) + distance + }; + for (int i = 0; i < 4; i++) + if (bounds[i] < 1) + bounds[i] = 1; + else if (bounds[i] > board_size(b) - 2) + bounds[i] = board_size(b) - 2; + for (int x = bounds[0]; x <= bounds[2]; x++) + for (int y = bounds[1]; y <= bounds[3]; y++) + if (board_atxy(b, x, y) != S_NONE) { + /* fprintf(stderr, "radar %d,%d,%d: %d,%d (%d)\n", + coord_x(coord, b), coord_y(coord, b), + distance, x, y, board_atxy(b, x, y)); */ + return true; + } + return false; +} + + +void +cfg_distances(struct board *b, coord_t start, int *distances, int maxdist) +{ + /* Queue for d+1 spots; no two spots of the same group + * should appear in the queue. */ +#define qinc(x) (x = ((x + 1) >= board_size2(b) ? ((x) + 1 - board_size2(b)) : (x) + 1)) + coord_t queue[board_size2(b)]; int qstart = 0, qstop = 0; + + foreach_point(b) { + distances[c] = board_at(b, c) == S_OFFBOARD ? maxdist + 1 : -1; + } foreach_point_end; + + queue[qstop++] = start; + for (int d = 0; d <= maxdist; d++) { + /* Process queued moves, while setting the queue + * for new wave. */ + int qa = qstart, qb = qstop; + qstart = qstop; + for (int q = qa; q < qb; qinc(q)) { +#define cfg_one(coord, grp) do {\ + distances[coord] = d; \ + foreach_neighbor (b, coord, { \ + if (distances[c] < 0 && (!grp || group_at(b, coord) != grp)) { \ + queue[qstop] = c; \ + qinc(qstop); \ + } \ + }); \ +} while (0) + coord_t cq = queue[q]; + if (distances[cq] >= 0) + continue; /* We already looked here. */ + if (board_at(b, cq) == S_NONE) { + cfg_one(cq, 0); + } else { + group_t g = group_at(b, cq); + foreach_in_group(b, g) { + cfg_one(c, g); + } foreach_in_group_end; + } +#undef cfg_one + } + } + + foreach_point(b) { + if (distances[c] < 0) + distances[c] = maxdist + 1; + } foreach_point_end; +} + + +floating_t +board_effective_handicap(struct board *b, int first_move_value) +{ + /* This can happen if the opponent passes during handicap + * placing phase. */ + // assert(b->handicap != 1); + + /* Always return 0 for even games, in particular if + * first_move_value is set on purpose to a value different + * from the correct theoretical value (2*komi). */ + if (!b->handicap) + return b->komi == 0.5 ? 0.5 * first_move_value : 7.5 - b->komi; + return b->handicap * first_move_value + 0.5 - b->komi; +} + + +bool +pass_is_safe(struct board *b, enum stone color, struct move_queue *mq) +{ + floating_t score = board_official_score(b, mq); + if (color == S_BLACK) + score = -score; + //fprintf(stderr, "%d score %f\n", color, score); + return (score >= 0); +} + + +/* On average 20% of points remain empty at the end of a game */ +#define EXPECTED_FINAL_EMPTY_PERCENT 20 + +/* Returns estimated number of remaining moves for one player until end of game. */ +int +board_estimated_moves_left(struct board *b) +{ + int total_points = (board_size(b)-2)*(board_size(b)-2); + int moves_left = (b->flen - total_points*EXPECTED_FINAL_EMPTY_PERCENT/100)/2; + return moves_left > MIN_MOVES_LEFT ? moves_left : MIN_MOVES_LEFT; +} diff --git a/jni/pachi/tactics/util.h b/jni/pachi/tactics/util.h new file mode 100644 index 0000000..418935e --- /dev/null +++ b/jni/pachi/tactics/util.h @@ -0,0 +1,88 @@ +#ifndef PACHI_TACTICS_UTIL_H +#define PACHI_TACTICS_UTIL_H + +/* Advanced tactical checks non-essential to the board implementation. */ + +#include "board.h" +#include "debug.h" + +struct move_queue; + +/* Checks if there are any stones in n-vincinity of coord. */ +bool board_stone_radar(struct board *b, coord_t coord, int distance); + +/* Measure various distances on the board: */ +/* Distance from the edge; on edge returns 0. */ +static int coord_edge_distance(coord_t c, struct board *b); +/* Distance of two points in gridcular metric - this metric defines + * circle-like structures on the square grid. */ +static int coord_gridcular_distance(coord_t c1, coord_t c2, struct board *b); + +/* Construct a "common fate graph" from given coordinate; that is, a weighted + * graph of intersections where edges between all neighbors have weight 1, + * but edges between neighbors of same color have weight 0. Thus, this is + * "stone chain" metric in a sense. */ +/* The output are distanes from start stored in given [board_size2()] array; + * intersections further away than maxdist have all distance maxdist+1 set. */ +void cfg_distances(struct board *b, coord_t start, int *distances, int maxdist); + +/* Compute an extra komi describing the "effective handicap" black receives + * (returns 0 for even game with 7.5 komi). @stone_value is value of single + * handicap stone, 7 is a good default. */ +/* This is just an approximation since in reality, handicap seems to be usually + * non-linear. */ +floating_t board_effective_handicap(struct board *b, int first_move_value); + +/* Decide if the given player wins counting on the board, considering + * that given groups are dead. (To get the list of dead groups, use + * e.g. groups_of_status().) */ +bool pass_is_safe(struct board *b, enum stone color, struct move_queue *mq); + +/* Returns estimated number of remaining moves for one player until end of game. */ +int board_estimated_moves_left(struct board *b); + +/* To avoid running out of time, assume we always have at least 30 more moves + * to play if we don't have more precise information from gtp time_left: */ +#define MIN_MOVES_LEFT 30 + +/* Tactical evaluation of move @coord by color @color, given + * simulation end position @b. I.e., a move is tactically good + * if the resulting group stays on board until the game end. + * The value is normalized to [0,1]. */ +/* We can also take into account surrounding stones, e.g. to + * encourage taking off external liberties during a semeai. */ +static double board_local_value(bool scan_neis, struct board *b, coord_t coord, enum stone color); + + +static inline int +coord_edge_distance(coord_t c, struct board *b) +{ + int x = coord_x(c, b), y = coord_y(c, b); + int dx = x > board_size(b) / 2 ? board_size(b) - 1 - x : x; + int dy = y > board_size(b) / 2 ? board_size(b) - 1 - y : y; + return (dx < dy ? dx : dy) - 1 /* S_OFFBOARD */; +} + +static inline int +coord_gridcular_distance(coord_t c1, coord_t c2, struct board *b) +{ + int dx = abs(coord_dx(c1, c2, b)), dy = abs(coord_dy(c1, c2, b)); + return dx + dy + (dx > dy ? dx : dy); +} + +static inline double +board_local_value(bool scan_neis, struct board *b, coord_t coord, enum stone color) +{ + if (scan_neis) { + /* Count surrounding friendly stones and our eyes. */ + int friends = 0; + foreach_neighbor(b, coord, { + friends += board_at(b, c) == color || board_at(b, c) == S_OFFBOARD || board_is_one_point_eye(b, c, color); + }); + return (double) (2 * (board_at(b, coord) == color) + friends) / 6.f; + } else { + return (board_at(b, coord) == color) ? 1.f : 0.f; + } +} + +#endif diff --git a/jni/pachi/timeinfo.c b/jni/pachi/timeinfo.c new file mode 100644 index 0000000..81b86df --- /dev/null +++ b/jni/pachi/timeinfo.c @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#include "debug.h" +#include "tactics/util.h" +#include "timeinfo.h" + +/* Max net lag in seconds. TODO: estimate dynamically. */ +#define MAX_NET_LAG 2.0 +/* Minimal thinking time; in case reserved time gets smaller than MAX_NET_LAG, + * this makes sure we play minimally sensible moves even in massive time + * pressure; we still keep MAX_NET_LAG-MIN_THINK_WITH_LAG safety margin. + * Note that this affects only lag adjustmnet - if reserved time *before* + * lag adjustment gets too small, we still respect it and don't apply + * MIN_THINK_WITH_LAG. */ +#define MIN_THINK_WITH_LAG (MAX_NET_LAG / 2) +/* Reserve 15% of byoyomi time as safety margin if risk of losing on time */ +#define RESERVED_BYOYOMI_PERCENT 15 + +/* For safety, use at most 2 times the desired time on a single move + * in sudden death and 1.1 times in byoyomi. */ +#define MAX_SUDDEN_DEATH_RATIO 2.0 +#define MAX_BYOYOMI_TIME_RATIO 1.1 + +bool +time_parse(struct time_info *ti, char *s) +{ + switch (s[0]) { + case '_': ti->period = TT_TOTAL; s++; break; + default: ti->period = TT_MOVE; break; + } + switch (s[0]) { + case '=': + ti->dim = TD_GAMES; + ti->len.games = atoi(++s); + break; + default: + if (!isdigit(s[0])) + return false; + ti->dim = TD_WALLTIME; + ti->len.t.timer_start = 0; + if (ti->period == TT_TOTAL) { + ti->len.t.main_time = atof(s); + ti->len.t.byoyomi_time = 0.0; + ti->len.t.byoyomi_time_max = 0.0; + ti->len.t.byoyomi_periods = 0; + ti->len.t.byoyomi_stones = 0; + ti->len.t.byoyomi_stones_max = 0; + } else { assert(ti->period == TT_MOVE); + ti->len.t.main_time = 0.0; + ti->len.t.byoyomi_time = atof(s); + ti->len.t.byoyomi_time_max = ti->len.t.byoyomi_time; + ti->len.t.byoyomi_periods = 1; + ti->len.t.byoyomi_stones = 1; + ti->len.t.byoyomi_stones_max = 1; + } + break; + } + return true; +} + +/* Update time settings according to gtp time_settings or kgs-time_settings command. */ +void +time_settings(struct time_info *ti, int main_time, int byoyomi_time, int byoyomi_stones, int byoyomi_periods) +{ + if (main_time < 0) { + ti->period = TT_NULL; // no time limit, rely on engine default + } else { + ti->period = main_time > 0 ? TT_TOTAL : TT_MOVE; + ti->dim = TD_WALLTIME; + ti->len.t.timer_start = 0; + ti->len.t.main_time = (double) main_time; + ti->len.t.byoyomi_time = (double) byoyomi_time; + ti->len.t.byoyomi_periods = byoyomi_periods; + ti->len.t.byoyomi_stones = byoyomi_stones; + ti->len.t.canadian = byoyomi_stones > 0; + if (byoyomi_time > 0) { + /* Normally, only one of byoyomi_periods and + * byoyomi_stones arguments will be > 0. However, + * our data structure uses generalized byoyomi + * specification that will assume "1 byoyomi period + * of N stones" for Canadian byoyomi and "N byoyomi + * periods of 1 stone" for Japanese byoyomi. */ + if (ti->len.t.byoyomi_periods < 1) + ti->len.t.byoyomi_periods = 1; + if (ti->len.t.byoyomi_stones < 1) + ti->len.t.byoyomi_stones = 1; + } else { + assert(!ti->len.t.byoyomi_periods && !ti->len.t.byoyomi_stones); + } + ti->len.t.byoyomi_time_max = ti->len.t.byoyomi_time; + ti->len.t.byoyomi_stones_max = ti->len.t.byoyomi_stones; + } +} + +/* Update time information according to gtp time_left command. + * kgs doesn't give time_left for the first move, so make sure + * that just time_settings + time_stop_conditions still work. */ +void +time_left(struct time_info *ti, int time_left, int stones_left) +{ + assert(ti->period != TT_NULL); + ti->dim = TD_WALLTIME; + + if (!time_left && !stones_left) { + /* Some GTP peers send time_left 0 0 at the end of main time. */ + ti->period = TT_MOVE; + ti->len.t.main_time = 0; + ti->len.t.byoyomi_time = ti->len.t.byoyomi_time_max; + ti->len.t.byoyomi_stones = ti->len.t.byoyomi_stones_max; + + } else if (!stones_left) { + /* Main time */ + ti->period = TT_TOTAL; + ti->len.t.main_time = time_left; + ti->len.t.byoyomi_time = ti->len.t.byoyomi_time_max; + ti->len.t.byoyomi_stones = ti->len.t.byoyomi_stones_max; + + } else { + /* Byoyomi */ + ti->period = TT_MOVE; + ti->len.t.main_time = 0; + ti->len.t.byoyomi_time = time_left; + if (ti->len.t.canadian) { + ti->len.t.byoyomi_stones = stones_left; + } else { + // field misused by kgs + ti->len.t.byoyomi_periods = stones_left; + } + } +} + +/* Start our timer. kgs does this (correctly) on "play" not "genmove" + * unless we are making the first move of the game. */ +void +time_start_timer(struct time_info *ti) +{ + if (ti->period != TT_NULL && ti->dim == TD_WALLTIME) + ti->len.t.timer_start = time_now(); +} + +void +time_sub(struct time_info *ti, double interval, bool new_move) +{ + assert(ti->dim == TD_WALLTIME && ti->period != TT_NULL); + + if (ti->period == TT_TOTAL) { + ti->len.t.main_time -= interval; + if (ti->len.t.main_time >= 0) + return; + if (ti->len.t.byoyomi_time <= 0) { + /* No byoyomi to save us. */ + fprintf(stderr, "*** LOST ON TIME internally! (%0.2f, spent %0.2fs on last move)\n", + ti->len.t.main_time, interval); + /* What can we do? Pretend this didn't happen. */ + ti->len.t.main_time = 1.0f; + return; + } + /* Fall-through to byoyomi. */ + ti->period = TT_MOVE; + interval = -ti->len.t.main_time; + ti->len.t.main_time = 0; + } + + ti->len.t.byoyomi_time -= interval; + if (ti->len.t.byoyomi_time < 0) { + /* Lost a period. */ + if (--ti->len.t.byoyomi_periods < 1) { + fprintf(stderr, "*** LOST ON TIME internally! (%0.2f, spent %0.2fs on last move)\n", + ti->len.t.byoyomi_time, interval); + /* Well, what can we do? Pretend this didn't happen. */ + ti->len.t.byoyomi_periods = 1; + } + ti->len.t.byoyomi_time = ti->len.t.byoyomi_time_max; + ti->len.t.byoyomi_stones = ti->len.t.byoyomi_stones_max; + return; + } + if (new_move && --ti->len.t.byoyomi_stones < 1) { + /* Finished a period. */ + ti->len.t.byoyomi_time = ti->len.t.byoyomi_time_max; + ti->len.t.byoyomi_stones = ti->len.t.byoyomi_stones_max; + } +} + +/* Returns the current time. */ +double +time_now(void) +{ +#if _POSIX_TIMERS > 0 + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + return now.tv_sec + now.tv_nsec/1000000000.0; +#else + struct timeval now; + gettimeofday(&now, NULL); + return now.tv_sec + now.tv_usec/1000000.0; +#endif +} + +/* Sleep for a given interval (in seconds). Return immediately if interval < 0. */ +void +time_sleep(double interval) +{ +#ifdef _WIN32 + unsigned int t = interval * 1000.0; + Sleep(t); +#else + struct timespec ts; + double sec; + ts.tv_nsec = (int)(modf(interval, &sec)*1000000000.0); + ts.tv_sec = (int)sec; + nanosleep(&ts, NULL); /* ignore error if interval was < 0 */ +#endif +} + + +/* Returns true if we are in byoyomi (or should play as if in byo yomi + * because remaining time per move in main time is less than byoyomi time + * per move). */ +static bool +time_in_byoyomi(struct time_info *ti) { + assert(ti->dim == TD_WALLTIME); + if (!ti->len.t.byoyomi_time) + return false; // there is no byoyomi! + assert(ti->len.t.byoyomi_stones > 0); + if (!ti->len.t.main_time) + return true; // we _are_ in byoyomi + if (ti->len.t.main_time <= ti->len.t.byoyomi_time / ti->len.t.byoyomi_stones + 0.001) + return true; // our basic time left is less than byoyomi time per move + return false; +} + +/* Set worst.time to all available remaining time (main time plus usable + * byoyomi), to be spread over returned number of moves (expected game + * length minus moves to be played in final byoyomi - if we would not be + * able to spend more time on them in main time anyway). */ +static int +time_stop_set_remaining(struct time_info *ti, struct board *b, double net_lag, struct time_stop *stop) +{ + int moves_left = board_estimated_moves_left(b); + stop->worst.time = ti->len.t.main_time; + + if (!ti->len.t.byoyomi_time) + return moves_left; + + /* Time for one move in byoyomi. */ + assert(ti->len.t.byoyomi_stones > 0); + double move_time = ti->len.t.byoyomi_time / ti->len.t.byoyomi_stones; + + /* (i) Plan to extend our thinking time to make use of byoyom. */ + + /* For Japanese byoyomi with N>1 periods, we use N-1 periods + * as main time, keeping the last one as insurance against + * unexpected net lag. */ + if (ti->len.t.byoyomi_periods > 2) { + stop->worst.time += (ti->len.t.byoyomi_periods - 2) * move_time; + // Will add 1 more byoyomi_time just below + } + + /* In case of Canadian byoyomi, include time that can be spent + * on its first move. */ + stop->worst.time += move_time; + + /* (ii) Do not play faster in main time than we would in byoyomi. */ + + /* Maximize the number of moves played uniformly in main time, + * while not playing faster in main time than in byoyomi. + * At this point, the main time remaining is stop->worst.time and + * already includes the first (canadian) or N-1 byoyomi periods. */ + double real_move_time = move_time - net_lag; + if (real_move_time > 0) { + int main_moves = stop->worst.time / real_move_time; + if (moves_left > main_moves) { + /* We plan to do too many moves in main time, + * do the rest in byoyomi. */ + moves_left = main_moves; + } + if (moves_left <= 0) // possible if too much lag + moves_left = 1; + } + + return moves_left; +} + +/* Adjust the recommended per-move time based on the current game phase. + * We expect stop->worst to be total time available, stop->desired the current + * per-move time allocation, and set stop->desired to adjusted per-move time. */ +static void +time_stop_phase_adjust(struct board *b, int fuseki_end, int yose_start, struct time_stop *stop) +{ + int bsize = (board_size(b)-2)*(board_size(b)-2); + fuseki_end = fuseki_end * bsize / 100; // move nb at fuseki end + yose_start = yose_start * bsize / 100; // move nb at yose start + assert(fuseki_end < yose_start); + + /* No adjustments in yose. */ + if (b->moves >= yose_start) + return; + int moves_to_yose = (yose_start - b->moves) / 2; + // ^- /2 because we only consider the moves we have to play ourselves + int left_at_yose_start = board_estimated_moves_left(b) - moves_to_yose; + if (left_at_yose_start < MIN_MOVES_LEFT) + left_at_yose_start = MIN_MOVES_LEFT; + + /* This particular value of middlegame_time will continuously converge + * to effective "yose_time" value as we approach yose_start. */ + double middlegame_time = stop->worst.time / left_at_yose_start; + if (middlegame_time < stop->desired.time) + return; + + if (b->moves < fuseki_end) { + assert(fuseki_end > 0); + /* At the game start, use stop->desired.time (rather + * conservative estimate), then gradually prolong it. */ + double beta = b->moves / fuseki_end; + stop->desired.time = middlegame_time * beta + stop->desired.time * (1 - beta); + + } else { assert(b->moves < yose_start); + /* Middlegame, start with relatively large value, then + * converge to the uniform-timeslice yose value. */ + stop->desired.time = middlegame_time; + } +} + +void +lag_adjust(double *time, double net_lag) +{ + double nolag_time = *time; + *time -= net_lag; + if (*time < MIN_THINK_WITH_LAG && nolag_time > MIN_THINK_WITH_LAG) + *time = MIN_THINK_WITH_LAG; +} + +/* Pre-process time_info for search control and sets the desired stopping conditions. */ +void +time_stop_conditions(struct time_info *ti, struct board *b, int fuseki_end, int yose_start, + floating_t max_maintime_ratio, struct time_stop *stop) +{ + /* We must have _some_ limits by now, be it random default values! */ + assert(ti->period != TT_NULL); + + /* Special-case limit by number of simulations. */ + if (ti->dim == TD_GAMES) { + if (ti->period == TT_TOTAL) { + ti->period = TT_MOVE; + ti->len.games /= board_estimated_moves_left(b); + } + + stop->desired.playouts = ti->len.games; + /* We force worst == desired, so note that we will NOT loop + * until best == winner. */ + stop->worst.playouts = ti->len.games; + return; + } + + assert(ti->dim == TD_WALLTIME); + + + /* Minimum net lag (seconds) to be reserved in the time for move. */ + double net_lag = MAX_NET_LAG; + net_lag += time_now() - ti->len.t.timer_start; + // TODO: keep statistics to get good estimate of lag not just current move + + + if (ti->period == TT_TOTAL && time_in_byoyomi(ti)) { + /* Technically, we are still in main time, but we can + * effectively switch to byoyomi scheduling since we + * have less time available than one byoyomi move takes. */ + ti->period = TT_MOVE; + } + + + if (ti->period == TT_MOVE) { + /* We are in byoyomi, or almost! */ + + /* The period can still include some tiny remnant of main + * time if we are just switching to byoyomi. */ + double period_len = ti->len.t.byoyomi_time + ti->len.t.main_time; + + stop->worst.time = period_len; + assert(ti->len.t.byoyomi_stones > 0); + stop->desired.time = period_len / ti->len.t.byoyomi_stones; + + /* Use a larger safety margin if we risk losing on time on + * this move; it makes no sense to have 30s byoyomi and wait + * until 28s to play our move). */ + if (stop->desired.time >= period_len - net_lag) { + double safe_margin = RESERVED_BYOYOMI_PERCENT * stop->desired.time / 100; + if (safe_margin > net_lag) + net_lag = safe_margin; + } + + /* Make recommended_old == average(recommended_new, max) */ + double worst_time = stop->desired.time * MAX_BYOYOMI_TIME_RATIO; + if (worst_time < stop->worst.time) + stop->worst.time = worst_time; + stop->desired.time *= (2 - MAX_BYOYOMI_TIME_RATIO); + + } else { assert(ti->period == TT_TOTAL); + /* We are in main time. */ + + assert(ti->len.t.main_time > 0); + /* Set worst.time to all available remaining time, to be spread + * over returned number of moves. */ + int moves_left = time_stop_set_remaining(ti, b, net_lag, stop); + + /* Allocate even slice of the remaining time for next move. */ + stop->desired.time = stop->worst.time / moves_left; + assert(stop->desired.time > 0 && stop->worst.time > 0); + assert(stop->desired.time <= stop->worst.time + 0.001); + + /* Furthermore, tweak the slice based on the game phase. */ + time_stop_phase_adjust(b, fuseki_end, yose_start, stop); + + /* Put final upper bound on maximal time spent on the move. + * Keep enough time for sudden death (or near SD) games. */ + double worst_time = stop->desired.time; + if (ti->len.t.byoyomi_time_max > ti->len.t.byoyomi_stones_max) { + worst_time *= max_maintime_ratio; + } else { + worst_time *= MAX_SUDDEN_DEATH_RATIO; + } + if (worst_time < stop->worst.time) + stop->worst.time = worst_time; + if (stop->desired.time > stop->worst.time) + stop->desired.time = stop->worst.time; + } + + if (DEBUGL(1)) + fprintf(stderr, "desired %0.2f, worst %0.2f, clock [%d] %0.2f + %0.2f/%d*%d, lag %0.2f\n", + stop->desired.time, stop->worst.time, + ti->dim, ti->len.t.main_time, + ti->len.t.byoyomi_time, ti->len.t.byoyomi_stones, + ti->len.t.byoyomi_periods, net_lag); + + /* Account for lag. */ + lag_adjust(&stop->desired.time, net_lag); + lag_adjust(&stop->worst.time, net_lag); +} diff --git a/jni/pachi/timeinfo.h b/jni/pachi/timeinfo.h new file mode 100644 index 0000000..ff7e01f --- /dev/null +++ b/jni/pachi/timeinfo.h @@ -0,0 +1,116 @@ +#ifndef PACHI_TIMEINFO_H +#define PACHI_TIMEINFO_H + +/* Time-keeping information about time to spend on the next move and/or + * rest of the game. This is only a hint, an engine may decide to spend + * more or less time on a given move, provided it never forfeits on time. */ + +/* Note that some ways of specifying time (TD_GAMES) may not make sense + * with all engines. */ + +#include + +#include "board.h" + +struct time_info { + /* For how long we can spend the time? */ + enum time_period { + TT_NULL, // No time limit. Other structure elements are undef. + TT_MOVE, // Time for the next move. + TT_TOTAL, // Time for the rest of the game. Never seen by engine. + } period; + /* How are we counting the time? */ + enum time_dimension { + TD_GAMES, // Fixed number of simulations to perform. + TD_WALLTIME, // Wall time to spend performing simulations. + } dim; + /* The actual time count. */ + union { + int games; // TD_GAMES + struct { // TD_WALLTIME + /* Main thinking time. 0 if we are already completely + * in byoyomi. */ + double main_time; + + /* Byoyomi time. This time must be remembered to avoid + * rushing at the end of the main period. If no byoyomi, + * set to 0. Otherwise, both periods and stones are + * larger than zero, and initially we have _periods + * periods of length _time and have to play _stones + * stones in each. If we play in canadian byoyomi, + * _time will shrink until we play all stones of the + * current period; _max always keeps period length + * for reference. */ + /* (In normal time settings, one of _periods or _stones + * is 1.) */ + double byoyomi_time; + int byoyomi_periods; + int byoyomi_stones; + double byoyomi_time_max; + int byoyomi_stones_max; + bool canadian; // time_left field meaning changes + + /* Absolute time at which our timer started for current move, + * 0 if not yet known. The engine always sees > 0. */ + double timer_start; + } t; + } len; + /* If true, this time info is independent from GTP time_left updates, + * which will be ignored. This is the case if the time settings were + * forced on the command line. */ + bool ignore_gtp; +}; + +/* Parse time information provided in custom format: + * =NUM - fixed number of simulations per move + * NUM - number of seconds to spend per move (can be floating_t) + * _NUM - number of seconds to spend per game + * + * Returns false on parse error. */ +bool time_parse(struct time_info *ti, char *s); + +/* Update time settings according to gtp time_settings command. + * main_time < 0 implies no time limit. */ +void time_settings(struct time_info *ti, int main_time, int byoyomi_time, int byoyomi_stones, int byoyomi_periods); + +/* Update time information according to gtp time_left command. */ +void time_left(struct time_info *ti, int time_left, int stones_left); + +/* Start our timer. kgs does this (correctly) on "play" not "genmove" + * unless we are making the first move of the game. */ +void time_start_timer(struct time_info *ti); + +/* Subtract given amount of elapsed time from time settings. */ +void time_sub(struct time_info *ti, double interval, bool new_move); + +/* Returns the current time. */ +double time_now(void); + +/* Sleep for a given interval (in seconds). Return immediately if interval < 0. */ +void time_sleep(double interval); + + +/* Based on existing time information, compute the optimal/maximal time + * to be spent on this move. */ +/* The values can be negative, indicating severe time shortage (less time + * available than netlag safety margin) and consequently need to choose + * a move ASAP. */ + +struct time_stop { + /* spend this amount of time if possible */ + union { + double time; // TD_WALLTIME + int playouts; // TD_GAMES + } desired; + /* spend no more than this time */ + union { + double time; // TD_WALLTIME + int playouts; // TD_GAMES + } worst; +}; + +/* fuseki_end and yose_start are percentages of expected game length. */ +void time_stop_conditions(struct time_info *ti, struct board *b, int fuseki_end, int yose_start, + floating_t max_maintime_ratio, struct time_stop *stop); + +#endif diff --git a/jni/pachi/tools/autobook/README b/jni/pachi/tools/autobook/README new file mode 100644 index 0000000..2505bd9 --- /dev/null +++ b/jni/pachi/tools/autobook/README @@ -0,0 +1,29 @@ +This is a shell-based UCT implementation. ;-) + +From another perspective, this is a tool for automated opening book +construction. The book is based on UCT tree with large exploration +coefficient and fed with results from games against a compenent +opponent. + +The UCT tree is stored in a directory tree, directories are nodes, +UCT statistics are stored in the 'stats' file of each directory +(first line is # of wins, second is # of playouts). Start the book +building by running the + + autobook.sh PACHI_CMD OPPONENT_CMD BOOKDIR + +script, specifying complete Pachi and opponent's invocation details +in the two parameters. If BOOKDIR exists, autobook.sh will continue +an existing opening book build; multiple autobook.sh scripts may +be running too. Just break it (Ctrl-C) when you want to stop. + +These helper scripts are used by autobook.sh: + + walk.sh A single iteration of the algorithm + eval.sh Choose next move to choose in the current directory (node) + expand.sh Create subdirectories (followup nodes) in the cwd + +The built autobook can be converted to an opening book in the fbook +format (on the most-simulated basis) using: + + autobook2fbook.sh BOOKDIR diff --git a/jni/pachi/tools/autobook/autobook.sh b/jni/pachi/tools/autobook/autobook.sh new file mode 100644 index 0000000..d41e675 --- /dev/null +++ b/jni/pachi/tools/autobook/autobook.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if [ $# -ne 3 ]; then + echo "Usage: $0 PLAYER1 PLAYER2 BOOKDIR" >&2 + exit 1 +fi + +export CMDDIR=$(pwd) +export SEQDIR=$(mktemp -d) + +if [ ! -d "$3" ]; then + mkdir "$3" + { echo 12; echo 24; } >"$3"/stats +fi + +while true; do + (cd "$3"; "$CMDDIR"/walk.sh "$1" "$2") + sleep 5 +done diff --git a/jni/pachi/tools/autobook/autobook2fbook.sh b/jni/pachi/tools/autobook/autobook2fbook.sh new file mode 100644 index 0000000..7f1c08a --- /dev/null +++ b/jni/pachi/tools/autobook/autobook2fbook.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Config: +# Minimal number of simulations for the result to be considered trustworthy +min_sims=50 + +if [ $# -ne 1 ]; then + echo "Usage: $0 BOOKDIR" >&2 + exit 1 +fi + +export CMDDIR=$(pwd) + +scan() { + seq="$1" + for i in */; do echo $(sed -ne 2p $i/stats) $i; done | sort -rn | + { first=1 + while read sims move; do + move="${move%/}" + if [ "$sims" -lt "$min_sims" ]; then + break; + fi + [ -z "$first" ] || echo "$seq | $move" + first= + (cd "$move"; scan "$seq $move") + done + } +} + +cd "$1" +scan "" diff --git a/jni/pachi/tools/autobook/eval.sh b/jni/pachi/tools/autobook/eval.sh new file mode 100644 index 0000000..027ccd5 --- /dev/null +++ b/jni/pachi/tools/autobook/eval.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Choose next move to choose in the current directory (node) + +# Config: +# Exploration coefficient +explore_p=0.8 + +best_move= +best_val=-9999 + +{ read mwins; read msims; } "$move"/stats + done diff --git a/jni/pachi/tools/autobook/walk.sh b/jni/pachi/tools/autobook/walk.sh new file mode 100644 index 0000000..7d4cd7a --- /dev/null +++ b/jni/pachi/tools/autobook/walk.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# +# A single iteration of the algorithm + +# Config: +# After how many visits (including prior_sims!) a node is expanded. +expand_sims=26 +# Virtual loss +vloss=4 +# Twogtp path +twogtp_path=/home/pasky/gogui-1.3.2/bin/gogui-twogtp + +# Expected env: +# $CMDDIR +# $SEQDIR + +pachi="$1"; shift +opponent="$1" + +seq="" +color=W +while true; do + { read wins; read sims; } stats.new + mv stats.new stats # atomic + + children=$(find . -maxdepth 1 -type d | wc -l); + case $children in + 1) expanded=;; + *) expanded=1;; + esac + + if [ -z "$expanded" -a "$sims" -ge "$expand_sims" ]; then + echo "(;FF[4]GM[1]CA[UTF-8]RU[Chinese]SZ[9]HA[0]KM[7.5]PW[white]PB[black]$seq)" >"$SEQDIR/a.sgf" + "$CMDDIR"/expand.sh "$pachi" + expanded=1 + fi + if [ -z "$expanded" ]; then + break; + fi + + move=$("$CMDDIR"/eval.sh) + + cd "$move" + case $color in + B) color=W;; + W) color=B;; + esac + + sgfmove=$(echo "$move" | perl -nle 'my ($x,$y) = /(.)(.)/; $x=lc($x); $x=chr(ord($x)-1) if ord(lc $x) > ord("i"); $y = chr(96+10-$y); print "$x$y"') + seq="$seq;${color}[$sgfmove]" +done + +echo " *** Sequence: $seq" +echo "(;FF[4]GM[1]CA[UTF-8]RU[Chinese]SZ[9]HA[0]KM[7.5]PW[white]PB[black]$seq)" >"$SEQDIR/a.sgf" +rm -f "$SEQDIR"/r* + +# last move has been... - we want to simulate this being _our_ move yet, +# i.e. start the simulation with the opponent to play +case $color in + B) + black="$pachi" + white="$opponent";; + W) + black="$opponent" + white="$pachi";; +esac +$twogtp_path -black "$black" -white "$white" -auto -verbose -size 9 -komi 7.5 -sgffile "$SEQDIR/r" -games 1 -openings "$SEQDIR" +wincolor=$(cat "$SEQDIR"/r-0.sgf | sed -ne 's/.*RE\[\(.\).*/\1/p') +case $wincolor in + B) result=1;; + W) result=0;; +esac +echo " *** Result: $wincolor wins => $result" +pwd; ls + +while [ -e stats ]; do + case $color in + B) nresult=$result;; + W) nresult=$((1-result)); + esac + echo " + Recording $nresult .. $(pwd) color $color" + + { read wins; read sims; } stats.new + mv stats.new stats # atomic + + cd .. + case $color in + B) color=W;; + W) color=B;; + esac +done diff --git a/jni/pachi/tools/complete-tromptaylor.gtp b/jni/pachi/tools/complete-tromptaylor.gtp new file mode 100644 index 0000000..3458fbc --- /dev/null +++ b/jni/pachi/tools/complete-tromptaylor.gtp @@ -0,0 +1,66 @@ +boardsize 9 +komi 7.5 +clear_board +play B f5 +play W d7 +play B e6 +play W c5 +play B e7 +play W d3 +play B e8 +play W f3 +play B e3 +play W g4 +play B f4 +play W e4 +play B e2 +play W d4 +play B g3 +play W h4 +play B f2 +play W g6 +play B d8 +play W d6 +play B e5 +play W d5 +play B d2 +play W g5 +play B c8 +play W f7 +play B c2 +play W f8 +play B c7 +play W e9 +play B b6 +play W d9 +play B b5 +play W c9 +play B b7 +play W h3 +play B g2 +play W h2 +play B a8 +play W g1 +play B b4 +play W f1 +play B e1 +play W h1 +play B a7 +play W b9 +play B b2 +play W a9 +play B a4 +play W f9 +play B a6 +play W pass +play B a3 +play W pass +play B a5 +play W pass +play B a2 +play W pass +play B a1 +play W pass +play B c3 +play W pass +play B pass diff --git a/jni/pachi/tools/complete.gtp b/jni/pachi/tools/complete.gtp new file mode 100644 index 0000000..5a3c0c6 --- /dev/null +++ b/jni/pachi/tools/complete.gtp @@ -0,0 +1,252 @@ +boardsize 9 + +clear_board + +komi 7.5 + + +play b C4 + + +play w E5 + +play b C6 + + +play w E6 + +play b D3 + + +play w F4 + +play b D7 + + +play w E7 + +play b E4 + + +play w E3 + +play b D4 + + +play w F3 + +play b D5 + + +play w F5 + +play b D6 + + +play w E8 + +play b D2 + + +play w D8 + +play b C8 + + +play w E2 + +play b D9 + + +play w H5 + +play b E1 + + +play w G2 + +play b E9 + + +play w F8 + +play b F9 + + +play w G9 + +play b C9 + + +play w G8 + +play b G1 + + +play w H2 + +play b F1 + + +play w J2 + +play b H1 + + +play w G3 + +play b J1 + + +play w G6 + +play b D1 + + +play w H4 + +play b PASS + + +play w H7 + +play b PASS + + +play w A2 + +play b B2 + + +play w A1 + +play b A3 + + +play w A5 + +play b B1 + + +play w A2 + +play b A1 + + +play w A4 + +play b B4 + + +play w A7 + +play b B5 + + +play w A6 + +play b B6 + + +play w pass + +play b B7 + + +play w B8 + +play b A8 + + +play w G5 + +play b B9 + + +play w F2 + +play b PASS + + +play w C2 + +play b C3 + + +play w H8 + +play b C1 + + +play w A4 + +play b A5 + + +play w A7 + +play b A6 + + +play w F6 + +play b PASS + + +play w F7 + +play b PASS + + +play w G4 + +play b PASS + + +play w H6 + +play b PASS + + +play w J4 + +play b PASS + + +play w G7 + +play b PASS + + +play w J9 + +play b PASS + + +play w H9 + +play b PASS + + +play w J6 + +play b PASS + + +play w H3 + +play b PASS + + +play w J3 + +play b PASS + + +play w J7 + +play b PASS + + +genmove w + diff --git a/jni/pachi/tools/genmove.gtp b/jni/pachi/tools/genmove.gtp new file mode 100644 index 0000000..8ba6795 --- /dev/null +++ b/jni/pachi/tools/genmove.gtp @@ -0,0 +1,5 @@ +boardsize 9 +clear_board +komi 7.5 +genmove black +genmove white diff --git a/jni/pachi/tools/genmove19.gtp b/jni/pachi/tools/genmove19.gtp new file mode 100644 index 0000000..ae29f34 --- /dev/null +++ b/jni/pachi/tools/genmove19.gtp @@ -0,0 +1,5 @@ +boardsize 19 +clear_board +komi 7.5 +genmove black +genmove white diff --git a/jni/pachi/tools/gentbook.sh b/jni/pachi/tools/gentbook.sh new file mode 100644 index 0000000..01844c5 --- /dev/null +++ b/jni/pachi/tools/gentbook.sh @@ -0,0 +1,33 @@ +#!/bin/sh +size="$1" # board size +opts="$2" # UCT engine options; must NOT specify different policy +popts="$3" # UCT policy options +[ -n "$size" ] || size=9 +[ -z "$opts" ] || opts=",$opts" +[ -z "$popts" ] || popts=":$popts" + +if [ "$size" -le 13 ]; then + games=400000 +else + games=200000 +fi + +rm ucttbook-$size-7.5.pachitree +n=0 +gentbook1() +{ + echo "[#$n:$1]" + n=$((n+1)) + echo -e 'boardsize '$size'\nclear_board\nkomi 7.5\npachi-gentbook b' | + ./pachi -t =$games "policy=ucb1amaf:explore_p=$1$popts$opts" +} +gentbook1 0.0 +gentbook1 0.1 +gentbook1 0.2 +gentbook1 2.0 +gentbook1 0.2 +gentbook1 2.0 +gentbook1 0.1 +gentbook1 0.0 +gentbook1 0.0 +gentbook1 0.0 diff --git a/jni/pachi/tools/kgslog2gtp.pl b/jni/pachi/tools/kgslog2gtp.pl new file mode 100644 index 0000000..428af4a --- /dev/null +++ b/jni/pachi/tools/kgslog2gtp.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl -ln +# +# kgslog2gtp - Convert kgsGtp log file to GTP command stream +# +# The command stream contains two special GTP commands intended +# for further script processing (unrecognized by Pachi itself): +# +# pachi-mygame COLOR OPPONENT DATE TIME +# pachi-mymove COORD VALUE +# +# (You probably want to convert pachi-mymove to play.) + +chomp; + +# 19.1.2012 20:47:47 com.gokgs.client.gtp.a +if (s/^(\S+ \S+) com.gokgs.client.gtp.a $/$1/) { + $a = $1; + +# FINE: Starting game as white against Mateman +} elsif (s/^FINE:.*as (\S+) against (\w+)/$1 $2/) { + print "pachi-mygame $_ $a"; + +# IN: play b o3 +} elsif (s/^IN: // and not /genmove/ and not /kgs-chat/ and not /time/) { + print $_; + +# *** WINNER is C4 (3,4) with score 0.4333 (49243/93153:93153/93393 games), extra komi 0.000000 +} elsif (s/^\*\*\* WINNER is (\S+) .*score (\S+) .*komi 0\.0.*/$1 $2/) { + print "pachi-mymove $_"; +} diff --git a/jni/pachi/tools/pattern3_show.pl b/jni/pachi/tools/pattern3_show.pl new file mode 100644 index 0000000..525652c --- /dev/null +++ b/jni/pachi/tools/pattern3_show.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +# ./pattern3_show.pl PATNUM + +use warnings; +no warnings "qw"; +use strict; + +my $pat = shift @ARGV; +my @s = qw(. X O #); + +my @p = map { ($pat >> (2*$_)) & 3 } (0..7); +splice @p, 4, 0, (0); +for my $y (0..2) { + for my $x (reverse 0..2) { + print $s[$p[$x + $y*3]]; + } + print "\n"; +} diff --git a/jni/pachi/tools/pattern_bayes_gen.sh b/jni/pachi/tools/pattern_bayes_gen.sh new file mode 100644 index 0000000..1d00293 --- /dev/null +++ b/jni/pachi/tools/pattern_bayes_gen.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# pattern_bayes_gen: Generate pattern probability table from a SGF collection +# (or stdin GTP stream). + +(if [ x"$1" = x"-" ]; then cat; else for i in "$@"; do echo $i >&2; tools/sgf2gtp.pl <$i; done; fi) | + ./pachi -d 0 -e patternscan competition,spat_split_sizes | + perl -nle ' + BEGIN { use List::MoreUtils qw(uniq); } + + s/^= // or next; + chomp; + my ($winner, $witness) = (/^\[(.*)\] \[(.*)\]$/); + + sub parse { $_ = $_[0]; s/\) \(/),(/g; return split(/,/); } + + for (uniq(parse($winner))) { + $choices{$_}++; + } + for (uniq(parse($witness))) { + $counts{$_}++; + } + + END { + for (keys %counts) { + $p{$_} = $choices{$_} / $counts{$_}; + } + for (sort { $counts{$a} <=> $counts{$b} } keys %p) { + next if ($counts{$_} < 2); + printf("%.3f %d %d %s\n", $p{$_}, $choices{$_}, $counts{$_}, $_); + } + }' diff --git a/jni/pachi/tools/pattern_bayes_merge.sh b/jni/pachi/tools/pattern_bayes_merge.sh new file mode 100644 index 0000000..5989288 --- /dev/null +++ b/jni/pachi/tools/pattern_bayes_merge.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# pattern_bayes_merge: Merge pattern probability tables; pass them +# as arguments. + +cat "$@" | perl -nle ' + chomp; + my ($p, $ch, $co, $pa) = /^(.*?) (\d+) (\d+) (.*)$/; + $pa or die "parse error: $_"; + $choices{$pa} += $ch; + $counts{$pa} += $co; + + END { + for (keys %counts) { + $p{$_} = $choices{$_} / $counts{$_}; + } + for (sort { $counts{$a} <=> $counts{$b} } keys %p) { + next if ($counts{$_} < 2); + printf("%.3f %d %d %s\n", $p{$_}, $choices{$_}, $counts{$_}, $_); + } + } + ' diff --git a/jni/pachi/tools/pattern_byplayer.sh b/jni/pachi/tools/pattern_byplayer.sh new file mode 100644 index 0000000..1e82c3e --- /dev/null +++ b/jni/pachi/tools/pattern_byplayer.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# pattern_byplayer - Build a per-player database of used patterns +# +# Invoke this script for each played game, it will add patterns +# to the database incrementally; each file in the database contains +# patterns played by one player. +# +# You must already have a spatial dictionary generated. + +dbdir="$1"; shift +sgf="$1"; shift +# rest of parameters is passed to the patternscan engine + +sgf_attr() { + # TODO: Does not work correctly for attributes containing escaped ] + # e.g. "PB[Tony \[Killer\] Nowak]" gets cropped to "Tony \[Killer" + # Not a big problem, since usually the player name is just + # [a-zA-Z0-9]* + + sed -n 's/^.*'$1'\[\([^]]*\)\].*$/\1/p' "$sgf" +} + +black="$(sgf_attr PB)" +white="$(sgf_attr PW)" +handi="$(sgf_attr HA)" + +if [ -n "$handi" ] && [ "$handi" -gt 0 ]; then + to_play=white + # Comment following out if you want to include handi games. + echo "$sgf: Skipping handicap game" >&2 + exit 1 +fi + +to_play=black +cat "$sgf" | tools/sgf2gtp.pl | ./pachi -d 0 -e patternscan "$@" | + sed -n -e 's/^= //p' | grep -v '^ *$' | # skip irrelevant replies + while read pattern; do + if [ "$to_play" = black ]; then + player="$black" + to_play=white + else + player="$white" + to_play=black + fi + echo "$pattern" >>"$dbdir/$player" + done diff --git a/jni/pachi/tools/pattern_getdrops.pl b/jni/pachi/tools/pattern_getdrops.pl new file mode 100644 index 0000000..99bff56 --- /dev/null +++ b/jni/pachi/tools/pattern_getdrops.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl -n +# +# pattern_getdrops - Find significant value drops in kgslog GTP stream +# and convert them to lead-in game records for pattern learning +# +# Run as pipeline: +# tools/kgslog2gtp.pl | tools/pattern_getdrops.pl | tools/pattern_bayes_gen.sh - + +BEGIN { + use vars qw(@game $lastval $valthres $valfrac); + $valthres = 0.8; + $valfrac = 0.95; +} + +chomp; +@_ = split(/\s+/); + +if ($_[0] eq 'pachi-mygame') { + @game = @_[1..4]; + print STDERR "*** new game [@game]\n"; + $lastval = 0; + @commands = (); + +} elsif ($_[0] eq 'pachi-mymove') { + if ($lastval > 0 and $lastval < $valthres and $_[2] / $lastval < $valfrac) { + print STDERR "large value drop $lastval -> $_[2] [@game :: @_]\n"; + print "$_\n" for @commands; + print "play $game[0] $_[1] 1\n"; + } + $lastval = $_[2]; + my $cmd = "play $game[0] $_[1] 0 $_[2]"; + push @commands, $cmd; + +} elsif ($_[0] eq 'play') { + push @_, 0; + push @commands, "@_"; + +} else { + push @commands, "@_"; +} diff --git a/jni/pachi/tools/pattern_spatial_gen.sh b/jni/pachi/tools/pattern_spatial_gen.sh new file mode 100644 index 0000000..73eef4f --- /dev/null +++ b/jni/pachi/tools/pattern_spatial_gen.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# pattern_spatial_gen: Initialize spatial dictionary from a SGF collection +# +# We will first gather all spatial features from the SGF collection +# (we take files as arguments) and store these occuring more than 4 times +# in a freshly created spatial dictionary; afterwards, you will probably want +# to do standard pattern-matching. +# +# DO NOT RUN THIS CONCURRENTLY! The dictionary will get corrupted. +# +# To get spatials in competion mode (also get information about unplayed +# spatials present at the board, not just the single played one per move), +# run this script as: +# +# PATARGS="competition" ./pattern_spatial_gen.sh ... +# +# Similarly, you can set SPATMIN to different number than 4 to include +# spatial features with other number of occurences. + +[ -n "$SPATMIN" ] || SPATMIN=4 + +rm -f patterns.spat + +echo " Gathering population of spatials occuring more than $SPATMIN times..." +(for i in "$@"; do echo $i >&2; tools/sgf2gtp.pl <$i; done) | + ./pachi -d 0 -e patternscan gen_spat_dict,no_pattern_match,spat_threshold=$SPATMIN${PATARGS:+,$PATARGS} >/dev/null + +echo " Renumbering patterns.spat..." +perl -i -pe '/^#/ and next; s/^\d+/++$a/e' patterns.spat + +echo -n " Counting hash collisions... " +perl -lne 'chomp; my ($id, $d, $p, @h) = split(/ /, $_); foreach (@h) { next if $h{$_} = $id; print "collision $id - $h{$_} ($_)" if $h{$_}; $h{$_}=$id; }' patterns.spat | wc -l diff --git a/jni/pachi/tools/pattern_spatial_show.pl b/jni/pachi/tools/pattern_spatial_show.pl new file mode 100644 index 0000000..04a06ff --- /dev/null +++ b/jni/pachi/tools/pattern_spatial_show.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl +# pattern_spatial_show: Show given spatial dictionary entry on board + +my $f = "patterns.spat"; +my $id = $ARGV[0]; + +my @ofs = (); + +open D, "$f" or die "$f: $!"; +while () { + chomp; + if (s/^# Point order: d=(\d+) //) { + my $d = $1; next if ($d < 1); + # XXX: Distances must be in proper order! + s/ *$//; + push @ofs, map { [ split(/,/, $_) ] } split(/ /, $_); + # print "#### $_\n"; + # print "[$d] ($#ofs) " . join(' ', map { $_->[0].",".$_->[1] } @ofs) . "\n"; + next; + } + /^#/ and next; + my ($lid, $d, $pat, @hashes) = split (/\s+/, $_); + if ($id == $lid) { + print "$d $pat\n"; + my @b; + my @pc = split (//, $pat); + for my $i (0 .. $#pc) { + # print "$i: $pc[$i] -> $ofs[$i][1],$ofs[$i][0]\n"; + $b[$d + $ofs[$i][1]][$d + $ofs[$i][0]] = $pc[$i]; + } + $b[$d][$d] = '_'; + for my $y (0 .. 2 * $d) { + for my $x (0 .. 2 * $d) { + print $b[2 * $d - $y][$x] || ' '; + print ' '; + } + print "\n"; + } + last; + } +} diff --git a/jni/pachi/tools/sgf-analyse.pl b/jni/pachi/tools/sgf-analyse.pl new file mode 100644 index 0000000..bded7f0 --- /dev/null +++ b/jni/pachi/tools/sgf-analyse.pl @@ -0,0 +1,65 @@ +#!/usr/bin/perl +# Simple script using Pachi for game analysis of SGF. +# Pachi will analyse each move in the SGF and suggest the winrate +# for either player and winrate it estimated for in-game followup. +# +# Usage: tools/sgf-analyse.pl COLOR SGF PACHIARGS... +# +# Note that this script assumes dynkomi=none and does not show +# dynamic komi information. (Would be trivial to add but it's tricky +# to interpret the data.) +# +# This script is dirty and insecure for untrusted input! +# +# Example: tools/sgf-analyse.pl W progame.sgf -t =2000 -d 0 dynkomi=none +# ...to get 2000 simulations per move, and winrates from white perspective. +# +# To plot the output in gnuplot: +# set yr [0:1] +# set ytics 0,0.25,1 +# plot "datafile.csv" using 1:5 with lines + +use warnings; +use strict; + +my $mcolor = shift @ARGV; +my $sgf = shift @ARGV; + +sub one { + my ($move) = @_; + + # Move of the other color - supposedly. + return if ($move % 2 == ($mcolor eq 'B' ? 0 : 1)); + + # Get pachi output from GTP stream that contains the SGF up to + # given move, with information about the originally made move + # included. + my $line = $move + 3; # board_size, clearboard, komi + my $rest = $line + 1; + open my $g, "tools/sgf2gtp.pl < \"$sgf\" | sed -e '$line s/play \\(.*\\) \\(.*\\)/1 echo \\1 \\2\\n2 genmove \\1\\n3 pachi-result/' -e '$rest,\$ d' | ./pachi @ARGV |" or die $!; + + # Parse the GTP output. + my ($color, $realmove, $genmove, $winrate) = @_; + while (<$g>) { + chomp; + if (/^=1 (.*) (.*)/) { + $color = $1; $realmove = uc $2; + } elsif (/^=2 (.*)/) { + $genmove = $1; + } elsif (/^=3 (.*) (.*) (.*) (.*)/) { + $winrate = $mcolor eq $color ? $3 : 1.0 - $3; + } + } + + # Pass value is not interesting since Pachi might want + # to clarify some groups yet. + return if $realmove eq 'PASS'; + + # Generate summary line. + print join(', ', $move, $color, $realmove, $genmove, $winrate) . "\n"; +} + +print "# $sgf @ARGV\n"; + +my $moves = `tools/sgf2gtp.pl < \"$sgf\" | wc -l` - 3; +one($_) for (1 .. $moves); diff --git a/jni/pachi/tools/sgf-ratemove.sh b/jni/pachi/tools/sgf-ratemove.sh new file mode 100644 index 0000000..8b0b457 --- /dev/null +++ b/jni/pachi/tools/sgf-ratemove.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Simple script using Pachi for rating all moves in a given situation. +# Pachi will analyse each followup move and suggest a winrate for it; +# higher is better. +# +# Usage: tools/sgf-ratemove.sh SGF MOVENUM PACHIARGS... +# +# Note that this script assumes dynkomi=none and does not show +# dynamic komi information. (Would be trivial to add but it's tricky +# to interpret the data.) +# +# This script is dirty and insecure for untrusted input! +# +# Example: tools/sgf-ratemove.sh progame.sgf 120 -t =500 -d 0 +# ...to get 500 simulations per each possible move in programe.sgf +# at move 120. +# +# If you want to know more details on what is Pachi thinking about the +# various moves, remove the `-d 0` part. To improve the accuracy of values +# (at the expense of run time), raise the value of 500 (try 2000 or 10000; +# 100000 will usually already take couple of hours). The values will be +# most useful in the middle game; in fuseki and most yose situations, +# expect a lot of noise. + +sgf=$1; shift +movenum=$1; shift +tools/sgf2gtp.pl -g -n $movenum <"$sgf" | + sed -e 's/genmove/0 pachi-evaluate/' | + ./pachi "$@" | + sed -ne '/^=0/,${s/^=0 //;p}' | + sort -r -n -t ' ' -k 2 diff --git a/jni/pachi/tools/sgf2gtp.pl b/jni/pachi/tools/sgf2gtp.pl new file mode 100644 index 0000000..c70995a --- /dev/null +++ b/jni/pachi/tools/sgf2gtp.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl -l +# +# sgf2gtp - Convert SGF game record to GTP command stream +# +# Usage: sgf2gtp [-g] [-n MOVENUM] [FILENAME] +# +# This is a naive Perl script that will convert SGF files to GTP +# format so that you can feed them to Pachi, insert genmove at +# the right places etc. Might not work on obscure SGF files, +# and of course there must be no variations. +# +# When called with a filename argument, it will create the output +# file with .gtp extension instead of .sgf. +# +# -g: Automatically append genmove command for the other color. +# -n MOVENUM: Output at most first MOVENUM moves. + +use warnings; + +my $genmove; +if ($ARGV[0] and $ARGV[0] eq '-g') { + shift @ARGV; + $genmove = 1; +} + +my $maxmoves; +if ($ARGV[0] and $ARGV[0] eq '-n') { + shift @ARGV; + $maxmoves = shift @ARGV; +} + +if ($ARGV[0]) { + open STDIN, "$ARGV[0]" or die "$ARGV[0]: $!"; + my $ofile = $ARGV[0]; $ofile =~ s/sgf$//i; $ofile .= 'gtp'; + open STDOUT, ">$ofile" or die "$ofile: $!"; +} + +local $/ = undef; my $sgf = <>; +my $size = ($sgf =~ /SZ\[(\d+)\]/)[0]; +$size ||= 19; +$sgf =~ s/\bC\[.*?\]//gs; # no comments +#$sgf =~ s/\).*//gs; # cut at end of principal branch + +print "boardsize " . $size; +print "clear_board"; +if ($sgf =~ s/\bKM\[([\d.]+)\]//gs) { + print "komi $1"; +} +if ($sgf =~ s/\bHA\[(\d+)\]//gs and $1 > 0) { + print "fixed_handicap $1"; +} + +my $abcd = "abcdefghijklmnopqrstuvwxyz"; +my $movenum = 0; +my $last_color = 'w'; + +my @m = split /;/, $sgf; +foreach (@m) { + $maxmoves and $movenum >= $maxmoves and last; + + if (/^([BW])\[\]/) { + $last_color = $1; + $movenum++; + print "play $1 pass"; + next; + } + unless (/^([BW])\[(\w\w)\]/) { + next; + } + + my ($color, $coord) = ($1, $2); + my ($x, $y) = split //, $coord; + ($x ge 'i') and $x++; + $y = $size - index($abcd, $y); + + $last_color = $color; + $movenum++; + print "play $color $x$y"; +} + +if ($genmove) { + print "genmove ".(uc $last_color eq 'W' ? 'B' : 'W'); +} diff --git a/jni/pachi/tools/sgf2gtp.py b/jni/pachi/tools/sgf2gtp.py new file mode 100644 index 0000000..0928706 --- /dev/null +++ b/jni/pachi/tools/sgf2gtp.py @@ -0,0 +1,152 @@ +#! /usr/bin/env python + +import sys +import argparse +import re + +from sgflib import SGFParser + +DEBUG = False + +parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, +description=""" +This script converts SGF files to GTP format so that you can feed them +to Pachi, insert genmove at the right places etc. Might not work on +obscure SGF files. + +When called with FILENAMES argument, it will create according output +files with .gtp extension instead of .sgf, unless --no-gtp option is +specified. + +Otherwise the games are read from standard input. Note that the stdin +in this case is read in at once, so for very large collections, it is +better to run this script separately for each sgf file. + +example: + cat *.sgf | %s -g -n 5 +"""%(sys.argv[0])) + +parser.add_argument('FILENAMES', help='List of sgf games to process.', nargs='*', default=[]) +parser.add_argument('-g', help='Automatically append genmove command for the other color.', action='store_true') +parser.add_argument('-n', help='Output at most first MOVENUM moves.', metavar='MOVENUM', type=int, default=10**10) +parser.add_argument('--stdout-only', help='Do not create the .gtp files from FILENAMES, print everything to stdout.', action='store_true') +args = vars(parser.parse_args()) + +class UnknownNode(Exception): + pass + +def get_atr(node, atr): + try: + return node.data[atr].data[0] + except KeyError: + return None + +def col2num(column, board_size): + a, o, z = map(ord, ['a', column, 'z']) + if a <= o <= z: + return a + board_size - o + raise Exception( "Wrong column character: '%s'"%(column,) ) + +def is_pass_move(coord, board_size): + # the pass move is represented either by [] ( = empty coord ) + # OR by [tt] (for boards <= 19 only) + return len(coord) == 0 or ( board_size <= 19 and coord == 'tt' ) + +def process_gametree(gametree, fout): + # cursor for tree traversal + c = gametree.cursor() + # first node is the header + header = c.node + + handicap = get_atr(header, 'HA') + board_size = int(get_atr(header, 'SZ')) + komi = get_atr(header, 'KM') + player_next, player_other = "B", "W" + + print >>fout, "boardsize", board_size + print >>fout, "clear_board" + if komi: + print >>fout, "komi", komi + if handicap and handicap != '0': + print >>fout, "fixed_handicap", handicap + player_next, player_other = player_other, player_next + + def print_game_step(coord): + if is_pass_move(coord, board_size): + print >>fout, "play", player_next, "pass" + else: + x, y = coord + # The reason for this incredibly weird thing is that + # the GTP protocol excludes `i` in the coordinates + # (do you see any purpose in this??) + if x >= 'i': + x = chr(ord(x)+1) + y = str(col2num(y, board_size)) + print >>fout, "play", player_next, x+y + + movenum = 0 + # walk the game tree forward + while 1: + # sgf2gtp.pl ignores n = 0 + if c.atEnd or (args['n'] and movenum >= args['n']): + break + c.next() + movenum += 1 + + coord = get_atr(c.node, player_next) + if coord != None: + print_game_step(coord) + else: + # MAYBE white started? + # or one of the players plays two time in a row + player_next, player_other = player_other, player_next + coord = get_atr(c.node, player_next) + if coord != None: + print_game_step(coord) + else: + # TODO handle weird sgf files better + raise UnknownNode + + player_next, player_other = player_other, player_next + + if args['g']: + print >>fout, "genmove", player_next + +def process_sgf_file(fin, fout): + sgfdata = fin.read() + col = SGFParser(sgfdata).parse() + + for gametree in col: + try: + process_gametree(gametree, fout) + except UnknownNode: + # Try next game tree in this file + if DEBUG: + print >>sys.stderr, "Unknown Node" + continue + +if __name__ == "__main__": + if not len(args['FILENAMES']): + process_sgf_file(sys.stdin, sys.stdout) + else: + for in_filename in args['FILENAMES']: + if args['stdout_only']: + fout = sys.stdout + else: + if re.search('sgf$', in_filename): + filename_base = in_filename[:-3] + else: + filename_base = in_filename + # Save the .gtp file + out_filename = filename_base + 'gtp' + + fout = open(out_filename, 'w') + + fin = open(in_filename, 'r') + + process_sgf_file(fin, fout) + + fin.close() + if not args['stdout_only']: + fout.close() + diff --git a/jni/pachi/tools/sgflib/.gitignore b/jni/pachi/tools/sgflib/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/jni/pachi/tools/sgflib/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/jni/pachi/tools/sgflib/__init__.py b/jni/pachi/tools/sgflib/__init__.py new file mode 100644 index 0000000..b9d128d --- /dev/null +++ b/jni/pachi/tools/sgflib/__init__.py @@ -0,0 +1 @@ +from sgflib import * diff --git a/jni/pachi/tools/sgflib/sgflib.py b/jni/pachi/tools/sgflib/sgflib.py new file mode 100644 index 0000000..ffe2d29 --- /dev/null +++ b/jni/pachi/tools/sgflib/sgflib.py @@ -0,0 +1,652 @@ +#!/usr/local/bin/python + +# sgflib.py (Smart Game Format Parser Library) +# Copyright (C) 2000 David John Goodger (dgoodger@bigfoot.com) +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# (lgpl.txt) along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# The license is currently available on the Internet at: +# http://www.gnu.org/copyleft/lesser.html + +""" +============================================= + Smart Game Format Parser Library: sgflib.py +============================================= +version 1.0 (2000-03-27) + +Homepage: [[http://gotools.sourceforge.net]] + +Copyright (C) 2000 David John Goodger ([[mailto:dgoodger@bigfoot.com]]; davidg +on NNGS, IGS, goclub.org). sgflib.py comes with ABSOLUTELY NO WARRANTY. This is +free software, and you are welcome to redistribute it and/or modify it under the +terms of the GNU Lesser General Public License; see the source code for details. + +Description +=========== +This library contains a parser and classes for SGF, the Smart Game Format. SGF +is a text only, tree based file format designed to store game records of board +games for two players, most commonly for the game of go. (See the official SGF +specification at [[http://www.POBoxes.com/sgf/]]). + +Given a string containing a complete SGF data instance, the 'SGFParser' class +will create a 'Collection' object consisting of one or more 'GameTree''s (one +'GameTree' per game in the SGF file), each containing a sequence of 'Node''s and +(potentially) two or more variation 'GameTree''s (branches). Each 'Node' +contains an ordered dictionary of 'Property' ID/value pairs (note that values +are lists, and can have multiple entries). + +Tree traversal methods are provided through the 'Cursor' class. + +The default representation (using 'str()' or 'print') of each class of SGF +objects is the Smart Game Format itself.""" + + +# Revision History: +# +# 1.0 (2000-03-27): First public release. +# - Ready for prime time. +# +# 0.1 (2000-01-16): +# - Initial idea & started coding. + + +import string, re +from typelib import List, Dictionary + + +# Parsing Exceptions + +class EndOfDataParseError(Exception): + """ Raised by 'SGFParser.parseVariations()', 'SGFParser.parseNode()'.""" + pass + +class GameTreeParseError(Exception): + """ Raised by 'SGFParser.parseGameTree()'.""" + pass + +class NodePropertyParseError(Exception): + """ Raised by 'SGFParser.parseNode()'.""" + pass + +class PropertyValueParseError(Exception): + """ Raised by 'SGFParser.parsePropertyValue()'.""" + pass + +# Tree Construction Exceptions + +class DirectAccessError(Exception): + """ Raised by 'Node.__setitem__()', 'Node.update()'.""" + pass + +class DuplicatePropertyError(Exception): + """ Raised by 'Node.addProperty()'.""" + pass + +# Tree Navigation Exceptions +class GameTreeNavigationError(Exception): + """ Raised by 'Cursor.next()'.""" + pass + +class GameTreeEndError(Exception): + """ Raised by 'Cursor.next()', 'Cursor.previous()'.""" + pass + + +# for type checking +INT_TYPE = type(0) # constant + +# miscellaneous constants +MAX_LINE_LEN = 76 # constant; for line breaks + + +class SGFParser: + """ + Parser for SGF data. Creates a tree structure based on the SGF standard + itself. 'SGFParser.parse()' will return a 'Collection' object for the entire + data. + + Instance Attributes: + - self.data : string -- The complete SGF data instance. + - self.datalen : integer -- Length of 'self.data'. + - self.index : integer -- Current parsing position in 'self.data'. + + Class Attributes: + - re* : re.RegexObject -- Regular expression text matching patterns. + - ctrltrans: string[256] -- Control character translation table for + string.translate(), used to remove all control characters from Property + values. May be overridden (preferably in instances).""" + + # text matching patterns + reGameTreeStart = re.compile(r'\s*\(') + reGameTreeEnd = re.compile(r'\s*\)') + reGameTreeNext = re.compile(r'\s*(;|\(|\))') + reNodeContents = re.compile(r'\s*([A-Za-z]+(?=\s*\[))') + rePropertyStart = re.compile(r'\s*\[') + rePropertyEnd = re.compile(r'\]') + reEscape = re.compile(r'\\') + reLineBreak = re.compile(r'\r\n?|\n\r?') # CR, LF, CR/LF, LF/CR + + + # character translation tables + # for control characters (except LF \012 & CR \015): convert to spaces + ctrltrans = string.maketrans("\000\001\002\003\004\005\006\007" + + "\010\011\013\014\016\017\020\021\022\023\024\025\026\027" + + "\030\031\032\033\034\035\036\037", " "*30) + + def __init__(self, data): + """ Initialize the instance attributes. See the class itself for info.""" + self.data = data + self.datalen = len(data) + self.index = 0 + + def parse(self): + """ Parses the SGF data stored in 'self.data', and returns a 'Collection'.""" + c = Collection() + while self.index < self.datalen: + g = self.parseOneGame() + if g: + c.append(g) + else: + break + return c + + def parseOneGame(self): + """ Parses one game from 'self.data'. Returns a 'GameTree' containing + one game, or 'None' if the end of 'self.data' has been reached.""" + if self.index < self.datalen: + match = self.reGameTreeStart.match(self.data, self.index) + if match: + self.index = match.end() + return self.parseGameTree() + return None + + def parseGameTree(self): + """ Called when "(" encountered, ends when a matching ")" encountered. + Parses and returns one 'GameTree' from 'self.data'. Raises + 'GameTreeParseError' if a problem is encountered.""" + g = GameTree() + while self.index < self.datalen: + match = self.reGameTreeNext.match(self.data, self.index) + if match: + self.index = match.end() + if match.group(1) == ";": # found start of node + if g.variations: + raise GameTreeParseError( + "A node was encountered after a variation.") + g.append(g.makeNode(self.parseNode())) + elif match.group(1) == "(": # found start of variation + g.variations = self.parseVariations() + else: # found end of GameTree ")" + return g + else: # error + raise GameTreeParseError + return g + + def parseVariations(self): + """ Called when "(" encountered inside a 'GameTree', ends when a + non-matching ")" encountered. Returns a list of variation + 'GameTree''s. Raises 'EndOfDataParseError' if the end of 'self.data' + is reached before the end of the enclosing 'GameTree'.""" + v = [] + while self.index < self.datalen: + # check for ")" at end of GameTree, but don't consume it + match = self.reGameTreeEnd.match(self.data, self.index) + if match: + return v + g = self.parseGameTree() + if g: + v.append(g) + # check for next variation, and consume "(" + match = self.reGameTreeStart.match(self.data, self.index) + if match: + self.index = match.end() + raise EndOfDataParseError + + def parseNode(self): + """ Called when ";" encountered (& is consumed). Parses and returns one + 'Node', which can be empty. Raises 'NodePropertyParseError' if no + property values are extracted. Raises 'EndOfDataParseError' if the + end of 'self.data' is reached before the end of the node (i.e., the + start of the next node, the start of a variation, or the end of the + enclosing game tree).""" + n = Node() + while self.index < self.datalen: + match = self.reNodeContents.match(self.data, self.index) + if match: + self.index = match.end() + pvlist = self.parsePropertyValue() + if pvlist: + n.addProperty(n.makeProperty(match.group(1), pvlist)) + else: + raise NodePropertyParseError + else: # reached end of Node + return n + raise EndOfDataParseError + + def parsePropertyValue(self): + """ Called when "[" encountered (but not consumed), ends when the next + property, node, or variation encountered. Parses and returns a list + of property values. Raises 'PropertyValueParseError' if there is a + problem.""" + pvlist = [] + while self.index < self.datalen: + match = self.rePropertyStart.match(self.data, self.index) + if match: + self.index = match.end() + v = "" # value + # scan for escaped characters (using '\'), unescape them (remove linebreaks) + mend = self.rePropertyEnd.search(self.data, self.index) + mesc = self.reEscape.search(self.data, self.index) + while mesc and mend and (mesc.end() < mend.end()): + # copy up to '\', but remove '\' + v = v + self.data[self.index:mesc.start()] + mbreak = self.reLineBreak.match(self.data, mesc.end()) + if mbreak: + self.index = mbreak.end() # remove linebreak + else: + v = v + self.data[mesc.end()] # copy escaped character + self.index = mesc.end() + 1 # move to point after escaped char + mend = self.rePropertyEnd.search(self.data, self.index) + mesc = self.reEscape.search(self.data, self.index) + if mend: + v = v + self.data[self.index:mend.start()] + self.index = mend.end() + pvlist.append(self._convertControlChars(v)) + else: + raise PropertyValueParseError + else: # reached end of Property + break + if len(pvlist) >= 1: + return pvlist + else: + raise PropertyValueParseError + + def _convertControlChars(self, text): + """ Converts control characters in 'text' to spaces, using the + 'self.ctrltrans' translation table. Override for variant + behaviour.""" + return string.translate(text, self.ctrltrans) + + +class RootNodeSGFParser(SGFParser): + """ For parsing only the first 'GameTree''s root Node of an SGF file.""" + + def parseNode(self): + """ Calls 'SGFParser.parseNode()', sets 'self.index' to point to the end + of the data (effectively ending the 'GameTree' and 'Collection'), + and returns the single (root) 'Node' parsed.""" + n = SGFParser.parseNode(self) # process one Node as usual + self.index = self.datalen # set end of data + return n # we're only interested in the root node + + +class Collection(List): + """ + An SGF collection: multiple 'GameTree''s. Instance atributes: + - self[.data] : list of 'GameTree' -- One 'GameTree' per game.""" + + def __str__(self): + """ SGF representation. Separates game trees with a blank line.""" + return string.join(map(str, self.data), "\n"*2) + + def cursor(self, gamenum=0): + """ Returns a 'Cursor' object for navigation of the given 'GameTree'.""" + return Cursor(self[gamenum]) + + +class GameTree(List): + """ + An SGF game tree: a game or variation. Instance attributes: + - self[.data] : list of 'Node' -- game tree 'trunk'. + - self.variations : list of 'GameTree' -- 0 or 2+ variations. + 'self.variations[0]' contains the main branch (sequence actually played).""" + + def __init__(self, nodelist=None, variations=None): + """ + Initialize the 'GameTree'. Arguments: + - nodelist : 'GameTree' or list of 'Node' -- Stored in 'self.data'. + - variations : list of 'GameTree' -- Stored in 'self.variations'.""" + List.__init__(self, nodelist) + self.variations = variations or [] + + def __str__(self): + """ SGF representation, with proper line breaks between nodes.""" + if len(self): + s = "(" + str(self[0]) # append the first Node automatically + l = len(string.split(s, "\n")[-1]) # accounts for line breaks within Nodes + for n in map(str, self[1:]): + if l + len(string.split(n, "\n")[0]) > MAX_LINE_LEN: + s = s + "\n" + l = 0 + s = s + n + l = len(string.split(s, "\n")[-1]) + return s + string.join(map(str, [""] + self.variations), "\n") + ")" + else: + return "" # empty GameTree illegal; "()" illegal + + def mainline(self): + """ Returns the main line of the game (variation A) as a 'GameTree'.""" + if self.variations: + return GameTree(self.data + self.variations[0].mainline()) + else: + return self + + def makeNode(self, plist): + """ + Create a new 'Node' containing the properties contained in 'plist'. + Override/extend to create 'Node' subclass instances (move, setup). + Argument: + - plist : 'Node' or list of 'Property'""" + return Node(plist) + + def cursor(self): + """ Returns a 'Cursor' object for navigation of this 'GameTree'.""" + return Cursor(self) + + def propertySearch(self, pid, getall=0): + """ + Searches this 'GameTree' for nodes containing matching properties. + Returns a 'GameTree' containing the matched node(s). Arguments: + - pid : string -- ID of properties to search for. + - getall : boolean -- Set to true (1) to return all 'Node''s that + match, or to false (0) to return only the first match.""" + matches = [] + for n in self: + if n.has_key(pid): + matches.append(n) + if not getall: + break + else: # getall or not matches: + for v in self.variations: + matches = matches + v.propertySearch(pid, getall) + if not getall and matches: + break + return GameTree(matches) + + +class Node(Dictionary): + """ + An SGF node. Instance Attributes: + - self[.data] : ordered dictionary -- '{Property.id:Property}' mapping. + (Ordered dictionary: allows offset-indexed retrieval). Properties *must* + be added using 'self.addProperty()'. + + Example: Let 'n' be a 'Node' parsed from ';B[aa]BL[250]C[comment]': + - 'str(n["BL"])' => '"BL[250]"' + - 'str(n[0])' => '"B[aa]"' + - 'map(str, n)' => '["B[aa]","BL[250]","C[comment]"]'""" + + def __init__(self, plist=[]): + """ + Initializer. Argument: + - plist: Node or list of 'Property'.""" + Dictionary.__init__(self) + self.order = [] + for p in plist: + self.addProperty(p) + + def __getitem__(self, key): + """ On 'self[key]', 'x in self', 'for x in self'. Implements all + indexing-related operations. Allows both key- and offset-indexed + retrieval. Membership and iteration ('in', 'for') repeatedly index + from 0 until 'IndexError'.""" + if type(key) is INT_TYPE: + return self.order[key] + else: + return self.data[key] + + def __setitem__(self, key, x): + """ On 'self[key]=x'. Allows assignment to existing items only. Raises + 'DirectAccessError' on new item assignment.""" + if self.has_key(key): + self.order[self.order.index(self[key])] = x + Dictionary.__setitem__(self, key, x) + else: + raise DirectAccessError( + "Properties may not be added directly; use addProperty() instead.") + + def __delitem__(self, key): + """ On 'del self[key]'. Updates 'self.order' to maintain consistency.""" + self.order.remove(self[key]) + Dictionary.__delitem__(self, key) + + def __getslice__(self, low, high): + """ On 'self[low:high]'.""" + return self.order[low:high] + + def __str__(self): + """ SGF representation, with proper line breaks between properties.""" + if len(self): + s = ";" + str(self[0]) + l = len(string.split(s, "\n")[-1]) # accounts for line breaks within Properties + for p in map(str, self[1:]): + if l + len(string.split(p, "\n")[0]) > MAX_LINE_LEN: + s = s + "\n" + l = 0 + s = s + p + l = len(string.split(s, "\n")[-1]) + return s + else: + return ";" + + def update(self, dict): + """ 'Dictionary' method not applicable to 'Node'. Raises + 'DirectAccessError'.""" + raise DirectAccessError( + "The update() method is not supported by Node; use addProperty() instead.") + + def addProperty(self, property): + """ + Adds a 'Property' to this 'Node'. Checks for duplicate properties + (illegal), and maintains the property order. Argument: + - property : 'Property'""" + if self.has_key(property.id): + raise DuplicatePropertyError + else: + self.data[property.id] = property + self.order.append(property) + + def makeProperty(self, id, valuelist): + """ + Create a new 'Property'. Override/extend to create 'Property' + subclass instances (move, setup, game-info, etc.). Arguments: + - id : string + - valuelist : 'Property' or list of values""" + return Property(id, valuelist) + + +class Property(List): + """ + An SGF property: a set of label and value(s). Instance attributes: + - self[.data] : list -- property values. + - self.id : string -- SGF standard property label. + - self.name : string -- actual label used in the SGF data. For example, the + property 'CoPyright[...]' has name 'CoPyright' and id 'CP'.""" + + def __init__(self, id, values, name=None): + """ + Initialize the 'Property'. Arguments: + - id : string + - name : string (optional) -- If not given, 'self.name' + - nodelist : 'GameTree' or list of 'Node' -- Stored in 'self.data'. + - variations : list of 'GameTree' -- Stored in 'self.variations'.""" + List.__init__(self, values) # XXX will _convert work here? + self.id = id + self.name = name or id + + def __str__(self): + return self.name + "[" + string.join(map(_escapeText, self), "][") + "]" + + +class Cursor: + """ + 'GameTree' navigation tool. Instance attributes: + - self.game : 'GameTree' -- The root 'GameTree'. + - self.gametree : 'GameTree' -- The current 'GameTree'. + - self.node : 'Node' -- The current Node. + - self.nodenum : integer -- The offset of 'self.node' from the root of + 'self.game'. The nodenum of the root node is 0. + - self.index : integer -- The offset of 'self.node' within 'self.gametree'. + - self.stack : list of 'GameTree' -- A record of 'GameTree''s traversed. + - self.children : list of 'Node' -- All child nodes of the current node. + - self.atEnd : boolean -- Flags if we are at the end of a branch. + - self.atStart : boolean -- Flags if we are at the start of the game.""" + + def __init__(self, gametree): + """ Initialize root 'GameTree' and instance variables.""" + self.game = gametree # root GameTree + self.reset() + + def reset(self): + """ Set 'Cursor' to point to the start of the root 'GameTree', 'self.game'.""" + self.gametree = self.game + self.nodenum = 0 + self.index = 0 + self.stack = [] + self.node = self.gametree[self.index] + self._setChildren() + self._setFlags() + + def next(self, varnum=0): + """ + Moves the 'Cursor' to & returns the next 'Node'. Raises + 'GameTreeEndError' if the end of a branch is exceeded. Raises + 'GameTreeNavigationError' if a non-existent variation is accessed. + Argument: + - varnum : integer, default 0 -- Variation number. Non-zero only + valid at a branching, where variations exist.""" + if self.index + 1 < len(self.gametree): # more main line? + if varnum != 0: + raise GameTreeNavigationError("Nonexistent variation.") + self.index = self.index + 1 + elif self.gametree.variations: # variations exist? + if varnum < len(self.gametree.variations): + self.stack.append(self.gametree) + self.gametree = self.gametree.variations[varnum] + self.index = 0 + else: + raise GameTreeNavigationError("Nonexistent variation.") + else: + raise GameTreeEndError + self.node = self.gametree[self.index] + self.nodenum = self.nodenum + 1 + self._setChildren() + self._setFlags() + return self.node + + def previous(self): + """ Moves the 'Cursor' to & returns the previous 'Node'. Raises + 'GameTreeEndError' if the start of a branch is exceeded.""" + if self.index - 1 >= 0: # more main line? + self.index = self.index - 1 + elif self.stack: # were we in a variation? + self.gametree = self.stack.pop() + self.index = len(self.gametree) - 1 + else: + raise GameTreeEndError + self.node = self.gametree[self.index] + self.nodenum = self.nodenum - 1 + self._setChildren() + self._setFlags() + return self.node + + def _setChildren(self): + """ Sets up 'self.children'.""" + if self.index + 1 < len(self.gametree): + self.children = [self.gametree[self.index+1]] + else: + self.children = map(lambda list: list[0], self.gametree.variations) + + def _setFlags(self): + """ Sets up the flags 'self.atEnd' and 'self.atStart'.""" + self.atEnd = not self.gametree.variations and (self.index + 1 == len(self.gametree)) + self.atStart = not self.stack and (self.index == 0) + + +reCharsToEscape = re.compile(r'\]|\\') # characters that need to be \escaped + +def _escapeText(text): + """ Adds backslash-escapes to property value characters that need them.""" + output = "" + index = 0 + match = reCharsToEscape.search(text, index) + while match: + output = output + text[index:match.start()] + '\\' + text[match.start()] + index = match.end() + match = reCharsToEscape.search(text, index) + output = output + text[index:] + return output + + +def selfTest1(onConsole=0): + """ Canned data test case""" + sgfdata = r""" (;GM [1]US[someone]CoPyright[\ + Permission to reproduce this game is given.]GN[a-b]EV[None]RE[B+Resign] +PW[a]WR[2k*]PB[b]BR[4k*]PC[somewhere]DT[2000-01-16]SZ[19]TM[300]KM[4.5] +HA[3]AB[pd][dp][dd];W[pp];B[nq];W[oq]C[ x started observation. +](;B[qc]C[ [b\]: \\ hi x! ;-) \\];W[kc])(;B[hc];W[oe])) """ + print "\n\n********** Self-Test 1 **********\n" + print "Input data:\n" + print sgfdata + print "\n\nParsed data: " + col = SGFParser(sgfdata).parse() + print "done\n" + cstr = str(col) + print cstr, "\n" + print "Mainline:\n" + m = col[0].mainline() + print m, "\n" + ##print "as GameTree:\n" + ##print GameTree(m), "\n" + print "Tree traversal (forward):\n" + c = col.cursor() + while 1: + print "nodenum: %s; index: %s; children: %s; node: %s" % (c.nodenum, c.index, len(c.children), c.node) + if c.atEnd: break + c.next() + print "\nTree traversal (backward):\n" + while 1: + print "nodenum: %s; index: %s; children: %s; node: %s" % (c.nodenum, c.index, len(c.children), c.node) + if c.atStart: break + c.previous() + print "\nSearch for property 'B':" + print col[0].propertySearch("B", 1) + print "\nSearch for property 'C':" + print col[0].propertySearch("C", 1) + pass + +def selfTest2(onConsole=0): + """ Macintosh-based SGF file test""" + import macfs + print "\n\n********** Self-Test 2 (Mac) **********\n" + thefile = macfs.PromptGetFile("Please choose an SGF file:") + if not thefile[1]: + return + srcpath = thefile[0].as_pathname() + src = open(srcpath, 'r') + sgfdata = src.read() + print "Input data:\n" + print sgfdata + print "\n\nParsed data:" + col = SGFParser(sgfdata).parse() + print "done\n" + print str(col) + + +if __name__ == '__main__': + print __doc__ # show module's documentation string + selfTest1() + import os + if os.name == 'mac': + selfTest2() diff --git a/jni/pachi/tools/sgflib/typelib.py b/jni/pachi/tools/sgflib/typelib.py new file mode 100644 index 0000000..311b24f --- /dev/null +++ b/jni/pachi/tools/sgflib/typelib.py @@ -0,0 +1,546 @@ +#!/usr/local/bin/python + +# typelib.py (Type Class Library) +# Copyright (c) 2000 David John Goodger +# +# This software is provided "as-is", without any express or implied warranty. +# In no event will the authors be held liable for any damages arising from the +# use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software in a +# product, an acknowledgment in the product documentation would be appreciated +# but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. + +""" +================================ + Type Class Library: typelib.py +================================ +version 1.0 (2000-03-27) + +Homepage: [[http://gotools.sourceforge.net/]] (see sgflib.py) + +Copyright (C) 2000 David John Goodger ([[mailto:dgoodger@bigfoot.com]]). +typelib.py comes with ABSOLUTELY NO WARRANTY. This is free software, and you are +welcome to redistribute it and/or modify it under certain conditions; see the +source code for details. + +Description +=========== +This library implements abstract superclasses to emulate Python's built-in data +types. This is useful when you want a class which acts like a built-in type, but +with added/modified behaviour (methods) and/or data (attributes). + +Implemented types are: 'String', 'Tuple', 'List', 'Dictionary', 'Integer', +'Long', 'Float', 'Complex' (along with their abstract superclasses). + +All methods, including special overloading methods, are implemented for each +type-emulation class. Instance data is stored internally in the 'data' attribute +(i.e., 'self.data'). The type the class is emulating is stored in the class +attribute 'self.TYPE' (as given by the built-in 'type(class)'). The +'SuperClass.__init__()' method uses two class-specific methods to instantiate +objects: '_reset()' and '_convert()'. + +See "sgflib.py" (at module's homepage, see above) for examples of use. The Node +class is of particular interest: a modified 'Dictionary' which is ordered and +allows for offset-indexed retrieval.""" + + +# Revision History +# +# 1.0 (2000-03-27): First public release. +# - Implemented Integer, Long, Float, and Complex. +# - Cleaned up a few loose ends. +# - Completed docstring documentatation. +# +# 0.1 (2000-01-27): +# - Implemented String, Tuple, List, and Dictionary emulation. +# +# To do: +# - Implement Function? File? (Have to come up with a good reason first ;-) + + +class SuperType: + """ Superclass of all type classes. Implements methods common to all types. + Concrete (as opposed to abstract) subclasses must define a class + attribute 'self.TYPE' ('=type(Class)'), and methods '_reset(self)' and + '_convert(self, data)'.""" + + def __init__(self, data=None): + """ + On 'Class()', initialize 'self.data'. Argument: + - 'data' : optional, default 'None' -- + - If the type of 'data' is identical to the Class' 'TYPE', + 'data' will be shared (relevant for mutable types only). + - If 'data' is given (and not false), it will be converted by + the Class-specific method 'self._convert(data)'. Incompatible + data types will raise an exception. + - If 'data' is 'None', false, or not given, a Class-specific method + 'self._reset()' is called to initialize an empty instance.""" + if data: + if type(data) is self.TYPE: + self.data = data + else: + self.data = self._convert(data) + else: + self._reset() + + def __str__(self): + """ On 'str(self)' and 'print self'. Returns string representation.""" + return str(self.data) + + def __cmp__(self, x): + """ On 'self>x', 'self==x', 'cmp(self,x)', etc. Catches all + comparisons: returns -1, 0, or 1 for less, equal, or greater.""" + return cmp(self.data, x) + + def __rcmp__(self, x): + """ On 'x>self', 'x==self', 'cmp(x,self)', etc. Catches all + comparisons: returns -1, 0, or 1 for less, equal, or greater.""" + return cmp(x, self.data) + + def __hash__(self): + """ On 'dictionary[self]', 'hash(self)'. Returns a unique and unchanging + integer hash-key.""" + return hash(self.data) + + +class AddMulMixin: + """ Addition & multiplication for numbers, concatenation & repetition for + sequences.""" + + def __add__(self, other): + """ On 'self+other'. Numeric addition, or sequence concatenation.""" + return self.data + other + + def __radd__(self, other): + """ On 'other+self'. Numeric addition, or sequence concatenation.""" + return other + self.data + + def __mul__(self, other): + """ On 'self*other'. Numeric multiplication, or sequence repetition.""" + return self.data * other + + def __rmul__(self, other): + """ On 'other*self'. Numeric multiplication, or sequence repetition.""" + return other * self.data + + +class MutableMixin: + """ Assignment to and deletion of collection component.""" + + def __setitem__(self, key, x): + """ On 'self[key]=x'.""" + self.data[key] = x + + def __delitem__(self, key): + """ On 'del self[key]'.""" + del self.data[key] + + +class ModMixin: + """ Modulo remainder and string formatting.""" + + def __mod__(self, other): + """ On 'self%other'.""" + return self.data % other + + def __rmod__(self, other): + """ On 'other%self'.""" + return other % self.data + + +class Number(SuperType, AddMulMixin, ModMixin): + """ Superclass for numeric emulation types.""" + + def __sub__(self, other): + """ On 'self-other'.""" + return self.data - other + + def __rsub__(self, other): + """ On 'other-self'.""" + return other - self.data + + def __div__(self, other): + """ On 'self/other'.""" + return self.data / other + + def __rdiv__(self, other): + """ On 'other/self'.""" + return other / self.data + + def __divmod__(self, other): + """ On 'divmod(self,other)'.""" + return divmod(self.data, other) + + def __rdivmod__(self, other): + """ On 'divmod(other,self)'.""" + return divmod(other, self.data) + + def __pow__(self, other, mod=None): + """ On 'pow(self,other[,mod])', 'self**other'.""" + if mod is None: + return self.data ** other + else: + return pow(self.data, other, mod) + + def __rpow__(self, other): + """ On 'pow(other,self)', 'other**self'.""" + return other ** self.data + + def __neg__(self): + """ On '-self'.""" + return -self.data + + def __pos__(self): + """ On '+self'.""" + return +self.data + + def __abs__(self): + """ On 'abs(self)'.""" + return abs(self.data) + + def __int__(self): + """ On 'int(self)'.""" + return int(self.data) + + def __long__(self): + """ On 'long(self)'.""" + return long(self.data) + + def __float__(self): + """ On 'float(self)'.""" + return float(self.data) + + def __complex__(self): + """ On 'complex(self)'.""" + return complex(self.data) + + def __nonzero__(self): + """ On truth-value (or uses '__len__()' if defined).""" + return self.data != 0 + + def __coerce__(self, other): + """ On mixed-type expression, 'coerce()'. Returns tuple of '(self, other)' + converted to a common type.""" + return coerce(self.data, other) + + +class Integer(Number): + """ Emulates a Python integer.""" + + TYPE = type(1) + + def _reset(self): + """ Initialize an integer.""" + self.data = 0 + + def _convert(self, data): + """ Convert data into an integer.""" + return int(data) + + def __lshift__(self, other): + """ On 'self<>other'.""" + return self.data >> other + + def __rrshift__(self, other): + """ On 'other>>self'.""" + return other >> self.data + + def __and__(self, other): + """ On 'self&other'.""" + return self.data & other + + def __rand__(self, other): + """ On 'other&self'.""" + return other & self.data + + def __or__(self, other): + """ On 'self|other'.""" + return self.data | other + + def __ror__(self, other): + """ On 'other|self'.""" + return other | self.data + + def __xor__(self, other): + """ On 'self^other'.""" + return self.data ^ other + + def __rxor__(self, other): + """ On 'other%self'.""" + return other % self.data + + def __invert__(self): + """ On '~self'.""" + return ~self.data + + def __oct__(self): + """ On 'oct(self)'. Returns octal string representation.""" + return oct(self.data) + + def __hex__(self): + """ On 'hex(self)'. Returns hexidecimal string representation.""" + return hex(self.data) + + +class Long(Integer): + """ Emulates a Python long integer.""" + + TYPE = type(1L) + + def _reset(self): + """ Initialize an integer.""" + self.data = 0L + + def _convert(self, data): + """ Convert data into an integer.""" + return long(data) + + +class Float(Number): + """ Emulates a Python floating-point number.""" + + TYPE = type(0.1) + + def _reset(self): + """ Initialize a float.""" + self.data = 0.0 + + def _convert(self, data): + """ Convert data into a float.""" + return float(data) + + +class Complex(Number): + """ Emulates a Python complex number.""" + + TYPE = type(0+0j) + + def _reset(self): + """ Initialize an integer.""" + self.data = 0+0j + + def _convert(self, data): + """ Convert data into an integer.""" + return complex(data) + + def __getattr__(self, name): + """ On 'self.real' & 'self.imag'.""" + if name == "real": + return self.data.real + elif name == "imag": + return self.data.imag + else: + raise AttributeError(name) + + def conjugate(self): + """ On 'self.conjugate()'.""" + return self.data.conjugate() + + +class Container(SuperType): + """ Superclass for countable, indexable collection types ('Sequence', 'Mapping').""" + + def __len__(self): + """ On 'len(self)', truth-value tests. Returns sequence or mapping + collection size. Zero means false.""" + return len(self.data) + + def __getitem__(self, key): + """ On 'self[key]', 'x in self', 'for x in self'. Implements all + indexing-related operations. Membership and iteration ('in', 'for') + repeatedly index from 0 until 'IndexError'.""" + return self.data[key] + + +class Sequence(Container, AddMulMixin): + """ Superclass for classes which emulate sequences ('List', 'Tuple', 'String').""" + + def __getslice__(self, low, high): + """ On 'self[low:high]'.""" + return self.data[low:high] + + +class String(Sequence, ModMixin): + """ Emulates a Python string.""" + + TYPE = type("") + + def _reset(self): + """ Initialize an empty string.""" + self.data = "" + + def _convert(self, data): + """ Convert data into a string.""" + return str(data) + + +class Tuple(Sequence): + """ Emulates a Python tuple.""" + + TYPE = type(()) + + def _reset(self): + """ Initialize an empty tuple.""" + self.data = () + + def _convert(self, data): + """ Non-tuples cannot be converted. Raise an exception.""" + raise TypeError("Non-tuples cannot be converted to a tuple.") + + +class MutableSequence(Sequence, MutableMixin): + """ Superclass for classes which emulate mutable (modifyable in-place) + sequences ('List').""" + + def __setslice__(self, low, high, seq): + """ On 'self[low:high]=seq'.""" + self.data[low:high] = seq + + def __delslice__(self, low, high): + """ On 'del self[low:high]'.""" + del self.data[low:high] + + def append(self, x): + """ Inserts object 'x' at the end of 'self.data' in-place.""" + self.data.append(x) + + def count(self, x): + """ Returns the number of occurrences of 'x' in 'self.data'.""" + return self.data.count(x) + + def extend(self, x): + """ Concatenates sequence 'x' to the end of 'self' in-place + (like 'self=self+x').""" + self.data.extend(x) + + def index(self, x): + """ Returns the offset of the first occurrence of object 'x' in + 'self.data'; raises an exception if not found.""" + return self.data.index(x) + + def insert(self, i, x): + """ Inserts object 'x' into 'self.data' at offset 'i' + (like 'self[i:i]=[x]').""" + self.data.insert(i, x) + + def pop(self, i=-1): + """ Returns and deletes the last item of 'self.data' (or item + 'self.data[i]' if 'i' given).""" + return self.data.pop(i) + + def remove(self, x): + """ Deletes the first occurrence of object 'x' from 'self.data'; + raise an exception if not found.""" + self.data.remove(x) + + def reverse(self): + """ Reverses items in 'self.data' in-place.""" + self.data.reverse() + + def sort(self, func=None): + """ + Sorts 'self.data' in-place. Argument: + - func : optional, default 'None' -- + - If 'func' not given, sorting will be in ascending + order. + - If 'func' given, it will determine the sort order. + 'func' must be a two-argument comparison function + which returns -1, 0, or 1, to mean before, same, + or after ordering.""" + if func: + self.data.sort(func) + else: + self.data.sort() + + +class List(MutableSequence): + """ Emulates a Python list. When instantiating an object with data + ('List(data)'), you can force a copy with 'List(list(data))'.""" + + TYPE = type([]) + + def _reset(self): + """ Initialize an empty list.""" + self.data = [] + + def _convert(self, data): + """ Convert data into a list.""" + return list(data) + + +class Mapping(Container): + """ Superclass for classes which emulate mappings/hashes ('Dictionary').""" + + def has_key(self, key): + """ Returns 1 (true) if 'self.data' has a key 'key', or 0 otherwise.""" + return self.data.has_key(key) + + def keys(self): + """ Returns a new list holding all keys from 'self.data'.""" + return self.data.keys() + + def values(self): + """ Returns a new list holding all values from 'self.data'.""" + return self.data.values() + + def items(self): + """ Returns a new list of tuple pairs '(key, value)', one for each entry + in 'self.data'.""" + return self.data.items() + + def clear(self): + """ Removes all items from 'self.data'.""" + self.data.clear() + + def get(self, key, default=None): + """ Similar to 'self[key]', but returns 'default' (or 'None') instead of + raising an exception when 'key' is not found in 'self.data'.""" + return self.data.get(key, default) + + def copy(self): + """ Returns a shallow (top-level) copy of 'self.data'.""" + return self.data.copy() + + def update(self, dict): + """ Merges 'dict' into 'self.data' + (i.e., 'for (k,v) in dict.items(): self.data[k]=v').""" + self.data.update(dict) + + +class Dictionary(Mapping, MutableMixin): + """ Emulates a Python dictionary, a mutable mapping. When instantiating an + object with data ('Dictionary(data)'), you can force a (shallow) copy + with 'Dictionary(data.copy())'.""" + + TYPE = type({}) + + def _reset(self): + """ Initialize an empty dictionary.""" + self.data = {} + + def _convert(self, data): + """ Non-dictionaries cannot be converted. Raise an exception.""" + raise TypeError("Non-dictionaries cannot be converted to a dictionary.") + + +if __name__ == "__main__": + print __doc__ # show module's documentation string diff --git a/jni/pachi/tools/spirit.gtp b/jni/pachi/tools/spirit.gtp new file mode 100644 index 0000000..58a8570 --- /dev/null +++ b/jni/pachi/tools/spirit.gtp @@ -0,0 +1,37 @@ +# Spirit-gman KGS 2007-03-07 19:14 +boardsize 9 +komi 0.5 +clear_board +play b d6 +play w d3 +play b d4 +play w c4 +# !! +play b e3 +play w e2 +# !! +play b c3 +play w d2 +play b c5 +play w b4 +# !! +play b e7 +play w g5 +# !! do you see e4 reply? +play b f2 +play w e4 +play b f7 +play w b5 +play b d5 +play w f3 +play b b6 +play w h7 +play b b3 +play w b2 +play b g6 +play w h6 +# this should be better a4 +play b a3 +play w a2 +# !! are we seeing the a2 atari threat? +play b h5 diff --git a/jni/pachi/tools/twogtp.py b/jni/pachi/tools/twogtp.py new file mode 100644 index 0000000..0a04ebe --- /dev/null +++ b/jni/pachi/tools/twogtp.py @@ -0,0 +1,703 @@ +#! /usr/bin/env python + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# This program is distributed with GNU Go, a Go program. # +# # +# Write gnugo@gnu.org or see http://www.gnu.org/software/gnugo/ # +# for more information. # +# # +# Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 and 2007 # +# by the Free Software Foundation. # +# # +# This program is free software; you can redistribute it and/or # +# modify it under the terms of the GNU General Public License # +# as published by the Free Software Foundation - version 3, # +# or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be # +# useful, but WITHOUT ANY WARRANTY; without even the implied # +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # +# PURPOSE. See the GNU General Public License in file COPYING # +# for more details. # +# # +# You should have received a copy of the GNU General Public # +# License along with this program; if not, write to the Free # +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # +# Boston, MA 02111, USA. # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +from getopt import * +import popen2 +import sys +import string +import re + + +debug = 0 + + +def coords_to_sgf(size, board_coords): + global debug + + board_coords = string.lower(board_coords) + if board_coords == "pass": + return "" + if debug: + print "Coords: <" + board_coords + ">" + letter = board_coords[0] + digits = board_coords[1:] + if letter > "i": + sgffirst = chr(ord(letter) - 1) + else: + sgffirst = letter + sgfsecond = chr(ord("a") + int(size) - int(digits)) + return sgffirst + sgfsecond + + + +class GTP_connection: + + # + # Class members: + # outfile File to write to + # infile File to read from + + def __init__(self, command): + try: + infile, outfile = popen2.popen2(command) + except: + print "popen2 failed" + sys.exit(1) + self.infile = infile + self.outfile = outfile + + def exec_cmd(self, cmd): + global debug + + if debug: + sys.stderr.write("GTP command: " + cmd + "\n") + self.outfile.write(cmd + "\n\n") + self.outfile.flush() + result = "" + line = self.infile.readline() + while line != "\n": + result = result + line + line = self.infile.readline() + if debug: + sys.stderr.write("Reply: " + line + "\n") + + # Remove trailing newline from the result + if result[-1] == "\n": + result = result[:-1] + + if len(result) == 0: + return "ERROR: len = 0" + if (result[0] == "?"): + return "ERROR: GTP Command failed: " + result[2:] + if (result[0] == "="): + return result[2:] + return "ERROR: Unrecognized answer: " + result + + +class GTP_player: + + # Class members: + # connection GTP_connection + + def __init__(self, command): + self.connection = GTP_connection(command) + protocol_version = self.connection.exec_cmd("protocol_version") + if protocol_version[:5] != "ERROR": + self.protocol_version = protocol_version + else: + self.protocol_version = "1" + + def is_known_command(self, command): + return self.connection.exec_cmd("known_command " + command) == "true" + + def genmove(self, color): + if color[0] in ["b", "B"]: + command = "black" + elif color[0] in ["w", "W"]: + command = "white" + if self.protocol_version == "1": + command = "genmove_" + command + else: + command = "genmove " + command + + return self.connection.exec_cmd(command) + + def black(self, move): + if self.protocol_version == "1": + self.connection.exec_cmd("black " + move) + else: + self.connection.exec_cmd("play black " + move) + + def white(self, move): + if self.protocol_version == "1": + self.connection.exec_cmd("white " + move) + else: + self.connection.exec_cmd("play white " + move) + + def komi(self, komi): + self.connection.exec_cmd("komi " + komi) + + def boardsize(self, size): + self.connection.exec_cmd("boardsize " + size) + if self.protocol_version != "1": + self.connection.exec_cmd("clear_board") + + def handicap(self, handicap, handicap_type): + if handicap_type == "fixed": + result = self.connection.exec_cmd("fixed_handicap %d" % (handicap)) + else: + result = self.connection.exec_cmd("place_free_handicap %d" + % (handicap)) + + return string.split(result, " ") + + def loadsgf(self, endgamefile, move_number): + self.connection.exec_cmd(string.join(["loadsgf", endgamefile, + str(move_number)])) + + def list_stones(self, color): + return string.split(self.connection.exec_cmd("list_stones " + color), " ") + + def quit(self): + return self.connection.exec_cmd("quit") + + def showboard(self): + board = self.connection.exec_cmd("showboard") + if board and (board[0] == "\n"): + board = board[1:] + return board + + def get_random_seed(self): + result = self.connection.exec_cmd("get_random_seed") + if result[:5] == "ERROR": + return "unknown" + return result + + def set_random_seed(self, seed): + self.connection.exec_cmd("set_random_seed " + seed) + + def get_program_name(self): + return self.connection.exec_cmd("name") + " " + \ + self.connection.exec_cmd("version") + + def final_score(self): + return self.connection.exec_cmd("final_score") + + def score(self): + return self.final_score(self) + + def cputime(self): + if (self.is_known_command("cputime")): + return self.connection.exec_cmd("cputime") + else: + return "0" + + +class GTP_game: + + # Class members: + # whiteplayer GTP_player + # blackplayer GTP_player + # size int + # komi float + # handicap int + # handicap_type string + # handicap_stones int + # moves list of string + # resultw + # resultb + + def __init__(self, whitecommand, blackcommand, size, komi, handicap, + handicap_type, endgamefile): + self.whiteplayer = GTP_player(whitecommand) + self.blackplayer = GTP_player(blackcommand) + self.size = size + self.komi = komi + self.handicap = handicap + self.handicap_type = handicap_type + self.endgamefile = endgamefile + self.sgffilestart = "" + if endgamefile != "": + self.init_endgame_contest_game() + else: + self.sgffilestart = "" + + def init_endgame_contest_game(self): + infile = open(self.endgamefile) + if not infile: + print "Couldn't read " + self.endgamefile + sys.exit(2) + sgflines = infile.readlines() + infile.close + size = re.compile("SZ\[[0-9]+\]") + move = re.compile(";[BW]\[[a-z]{0,2}\]") + sgf_start = [] + for line in sgflines: + match = size.search(line) + if match: + self.size = match.group()[3:-1] + match = move.search(line) + while match: + sgf_start.append("A" + match.group()[1:]) + line = line[match.end():] + match = move.search(line) + self.endgame_start = len(sgf_start) - endgame_start_at + self.sgffilestart = ";" + string.join( + sgf_start[:self.endgame_start-1], "") + "\n" + if self.endgame_start % 2 == 0: + self.first_to_play = "W" + else: + self.first_to_play = "B" + + def get_position_from_engine(self, engine): + black_stones = engine.list_stones("black") + white_stones = engine.list_stones("white") + self.sgffilestart = ";" + if len(black_stones) > 0: + self.sgffilestart += "AB" + for stone in black_stones: + self.sgffilestart += "[%s]" % coords_to_sgf(self.size, stone) + self.sgffilestart += "\n" + if len(white_stones) > 0: + self.sgffilestart += "AW" + for stone in white_stones: + self.sgffilestart += "[%s]" % coords_to_sgf(self.size, stone) + self.sgffilestart += "\n" + + def writesgf(self, sgffilename): + "Write the game to an SGF file after a game" + + size = self.size + outfile = open(sgffilename, "w") + if not outfile: + print "Couldn't create " + sgffilename + return + black_name = self.blackplayer.get_program_name() + white_name = self.whiteplayer.get_program_name() + black_seed = self.blackplayer.get_random_seed() + white_seed = self.whiteplayer.get_random_seed() + handicap = self.handicap + komi = self.komi + result = self.resultw + + outfile.write("(;GM[1]FF[4]RU[Japanese]SZ[%s]HA[%s]KM[%s]RE[%s]\n" % + (size, handicap, komi, result)) + outfile.write("PW[%s (random seed %s)]PB[%s (random seed %s)]\n" % + (white_name, white_seed, black_name, black_seed)) + outfile.write(self.sgffilestart) + + if handicap > 1: + outfile.write("AB"); + for stone in self.handicap_stones: + outfile.write("[%s]" %(coords_to_sgf(size, stone))) + outfile.write("PL[W]\n") + + to_play = self.first_to_play + + for move in self.moves: + sgfmove = coords_to_sgf(size, move) + outfile.write(";%s[%s]\n" % (to_play, sgfmove)) + if to_play == "B": + to_play = "W" + else: + to_play = "B" + outfile.write(")\n") + outfile.close + + def set_handicap(self, handicap): + self.handicap = handicap + + def swap_players(self): + temp = self.whiteplayer + self.whiteplayer = self.blackplayer + self.blackplayer = temp + + def play(self, sgffile): + "Play a game" + global verbose + + if verbose >= 1: + print "Setting boardsize and komi for black\n" + self.blackplayer.boardsize(self.size) + self.blackplayer.komi(self.komi) + + if verbose >= 1: + print "Setting boardsize and komi for white\n" + self.whiteplayer.boardsize(self.size) + self.whiteplayer.komi(self.komi) + + self.handicap_stones = [] + + if self.endgamefile == "": + if self.handicap < 2: + self.first_to_play = "B" + else: + self.handicap_stones = self.blackplayer.handicap(self.handicap, self.handicap_type) + for stone in self.handicap_stones: + self.whiteplayer.black(stone) + self.first_to_play = "W" + else: + self.blackplayer.loadsgf(self.endgamefile, self.endgame_start) + self.blackplayer.set_random_seed("0") + self.whiteplayer.loadsgf(self.endgamefile, self.endgame_start) + self.whiteplayer.set_random_seed("0") + if self.blackplayer.is_known_command("list_stones"): + self.get_position_from_engine(self.blackplayer) + elif self.whiteplayer.is_known_command("list_stones"): + self.get_position_from_engine(self.whiteplayer) + + to_play = self.first_to_play + + self.moves = [] + passes = 0 + won_by_resignation = "" + while passes < 2: + if to_play == "B": + move = self.blackplayer.genmove("black") + + if move[:5] == "ERROR": + # FIXME: write_sgf + sys.exit(1) + + if move[:6] == "resign": + if verbose >= 1: + print "Black resigns" + won_by_resignation = "W+Resign" + break + else: + self.moves.append(move) + if string.lower(move[:4]) == "pass": + passes = passes + 1 + if verbose >= 1: + print "Black passes" + else: + passes = 0 + self.whiteplayer.black(move) + if verbose >= 1: + print "Black plays " + move + to_play = "W" + else: + move = self.whiteplayer.genmove("white") + if move[:5] == "ERROR": + # FIXME: write_sgf + sys.exit(1) + + if move[:6] == "resign": + if verbose >= 1: + print "White resigns" + won_by_resignation = "B+Resign" + break + else: + self.moves.append(move) + if string.lower(move[:4]) == "pass": + passes = passes + 1 + if verbose >= 1: + print "White passes" + else: + passes = 0 + self.blackplayer.white(move) + if verbose >= 1: + print "White plays " + move + to_play = "B" + + if verbose >= 2: + print self.whiteplayer.showboard() + "\n" + + if won_by_resignation == "": + self.resultw = self.whiteplayer.final_score() + self.resultb = self.blackplayer.final_score() + else: + self.resultw = won_by_resignation; + self.resultb = won_by_resignation; + # if self.resultb == self.resultw: + # print "Result: ", self.resultw + # else: + # print "Result according to W: ", self.resultw + # print "Result according to B: ", self.resultb + # FIXME: $self->writesgf($sgffile) if defined $sgffile; + if sgffile != "": + self.writesgf(sgffile) + + def result(self): + return (self.resultw, self.resultb) + + def cputime(self): + cputime = {} + cputime["white"] = self.whiteplayer.cputime() + cputime["black"] = self.blackplayer.cputime() + return cputime + + def quit(self): + self.blackplayer.quit() + self.whiteplayer.quit() + + +class GTP_match: + + # Class members: + # black + # white + # size + # komi + # handicap + # handicap_type + + def __init__(self, whitecommand, blackcommand, size, komi, handicap, + handicap_type, streak_length, endgamefilelist): + self.white = whitecommand + self.black = blackcommand + self.size = size + self.komi = komi + self.handicap = handicap + self.handicap_type = handicap_type + self.streak_length = streak_length + self.endgamefilelist = endgamefilelist + + def endgame_contest(self, sgfbase): + results = [] + i = 1 + for endgamefile in self.endgamefilelist: + game1 = GTP_game(self.white, self.black, self.size, self.komi, + 0, "", endgamefile) + game2 = GTP_game(self.black, self.white, self.size, self.komi, + 0, "", endgamefile) + if verbose: + print "Replaying", endgamefile + print "Black:", self.black + print "White:", self.white + game1.play("") + result1 = game1.result()[0] + if result1 != "0": + plain_result1 = re.search(r"([BW]\+)([0-9]*\.[0-9]*)", result1) + result1_float = float(plain_result1.group(2)) + else: + plain_result1 = re.search(r"(0)", "0") + result1_float = 0.0 + if result1[0] == "B": + result1_float *= -1 + if verbose: + print "Result:", result1 + print "Replaying", endgamefile + print "Black:", self.white + print "White:", self.black + game2.play("") + result2 = game2.result()[1] + if verbose: + print "Result:", result2 + if result2 != "0": + plain_result2 = re.search(r"([BW]\+)([0-9]*\.[0-9]*)", result2) + result2_float = float(plain_result2.group(2)) + else: + plain_result2 = re.search(r"(0)", "0") + result2_float = 0.0 + + if result2[0] == "B": + result2_float *= -1 + results.append(result1_float - result2_float) + if (result1 != result2): + print endgamefile+ ":", plain_result1.group(), \ + plain_result2.group(), "Difference:", + print result1_float - result2_float + else: + print endgamefile+": Same result:", plain_result1.group() + sgffilename = "%s%03d" % (sgfbase, i) + game1.writesgf(sgffilename + "_1.sgf") + game2.writesgf(sgffilename + "_2.sgf") + game1.quit() + game2.quit() + i += 1 + return results + + def play(self, games, sgfbase): + last_color = "" + last_streak = 0 + game = GTP_game(self.white, self.black, + self.size, self.komi, self.handicap, + self.handicap_type, "") + results = [] + for i in range(games): + sgffilename = "%s%03d.sgf" % (sgfbase, i + 1) + game.play(sgffilename) + result = game.result() + if result[0] == result[1]: + print "Game %d: %s" % (i + 1, result[0]) + else: + print "Game %d: %s %s" % (i + 1, result[0], result[1]) + + if result[0][0] == last_color: + last_streak += 1 + elif result[0][0] != "0": + last_color = result[0][0] + last_streak = 1 + + if last_streak == self.streak_length: + if last_color == "W": + self.handicap += 1 + if self.handicap == 1: + self.handicap = 2 + print "White wins too often. Increasing handicap to %d" \ + % (self.handicap) + else: + if self.handicap > 0: + self.handicap -= 1 + if self.handicap == 1: + self.handicap = 0 + print "Black wins too often. Decreasing handicap to %d" \ + % (self.handicap) + else: + self.handicap = 2 + game.swap_players() + print "Black looks stronger than white. Swapping colors and setting handicap to 2" + game.set_handicap(self.handicap) + last_color = "" + last_streak = 0 + results.append(result) + cputime = game.cputime() + game.quit() + return results, cputime + + +# ================================================================ +# Main program +# + + +# Default values +# + +white = "" +black = "" +komi = "" +size = "19" +handicap = 0 +handicap_type = "fixed" +streak_length = -1 +endgame_start_at = 0 + +games = 1 +sgfbase = "twogtp" + +verbose = 0 + +helpstring = """ + +Run with: + +twogtp --white \' --mode gtp [program options]\' \\ + --black \' --mode gtp [program options]\' \\ + [twogtp options] + +Possible twogtp options: + + --verbose 1 (to list moves) or --verbose 2 (to draw board) + --komi + --handicap + --free-handicap + --adjust-handicap (change handicap by 1 after wins + in a row) + --size (default 19) + --games (default 1) + --sgfbase (create sgf files with sgfbase as basename) + --endgame (endgame contest - add filenames of + games to be replayed after last option) +""" + +def usage(): + print helpstring + sys.exit(1) + +try: + (opts, params) = getopt(sys.argv[1:], "", + ["black=", + "white=", + "verbose=", + "komi=", + "boardsize=", + "size=", + "handicap=", + "free-handicap=", + "adjust-handicap=", + "games=", + "sgfbase=", + "endgame=", + ]) +except: + usage(); + +for opt, value in opts: + if opt == "--black": + black = value + elif opt == "--white": + white = value + elif opt == "--verbose": + verbose = int(value) + elif opt == "--komi": + komi = value + elif opt == "--boardsize" or opt == "--size": + size = value + elif opt == "--handicap": + handicap = int(value) + handicap_type = "fixed" + elif opt == "--free-handicap": + handicap = int(value) + handicap_type = "free" + elif opt == "--adjust-handicap": + streak_length = int(value) + elif opt == "--games": + games = int(value) + elif opt == "--sgfbase": + sgfbase = value + elif opt == "--endgame": + endgame_start_at = int(value) + +if endgame_start_at != 0: + endgame_filelist = params +else: + endgame_filelist = [] + if params != []: + usage() + + +if black == "" or white == "": + usage() + +if komi == "": + if handicap > 1 and streak_length == -1: + komi = "0.5" + else: + komi = "5.5" + +match = GTP_match(white, black, size, komi, handicap, handicap_type, + streak_length, endgame_filelist) +if endgame_filelist != []: + results = match.endgame_contest(sgfbase) + win_black = 0 + win_white = 0 + for res in results: + print res + if res > 0.0: + win_white += 1 + elif res < 0.0: + win_black += 1 + print "%d games, %d wins for black, %d wins for white." \ + % (len(results), win_black, win_white) + +else: + results, cputimes = match.play(games, sgfbase) + + i = 0 + for resw, resb in results: + i = i + 1 + if resw == resb: + print "Game %d: %s" % (i, resw) + else: + print "Game %d: %s %s" % (i, resb, resw) + if (cputimes["white"] != "0"): + print "White: %ss CPU time" % cputimes["white"] + if (cputimes["black"] != "0"): + print "Black: %ss CPU time" % cputimes["black"] diff --git a/jni/pachi/uct/Makefile b/jni/pachi/uct/Makefile new file mode 100644 index 0000000..f4ea8e5 --- /dev/null +++ b/jni/pachi/uct/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I.. +OBJS=dynkomi.o tree.o uct.o prior.o search.o slave.o walk.o plugins.o + +all: uct.a +uct.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../Makefile.lib diff --git a/jni/pachi/uct/dynkomi.c b/jni/pachi/uct/dynkomi.c new file mode 100644 index 0000000..b7d5fa0 --- /dev/null +++ b/jni/pachi/uct/dynkomi.c @@ -0,0 +1,601 @@ +#define DEBUG +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "tactics/util.h" +#include "uct/dynkomi.h" +#include "uct/internal.h" +#include "uct/tree.h" + + +static void +generic_done(struct uct_dynkomi *d) +{ + if (d->data) free(d->data); + free(d); +} + + +/* NONE dynkomi strategy - never fiddle with komi values. */ + +struct uct_dynkomi * +uct_dynkomi_init_none(struct uct *u, char *arg, struct board *b) +{ + struct uct_dynkomi *d = calloc2(1, sizeof(*d)); + d->uct = u; + d->permove = NULL; + d->persim = NULL; + d->done = generic_done; + d->data = NULL; + + if (arg) { + fprintf(stderr, "uct: Dynkomi method none accepts no arguments\n"); + exit(1); + } + + return d; +} + + +/* LINEAR dynkomi strategy - Linearly Decreasing Handicap Compensation. */ +/* At move 0, we impose extra komi of handicap_count*handicap_value, then + * we linearly decrease this extra komi throughout the game down to 0 + * at @moves moves. Towards the end of the game the linear compensation + * becomes zero but we increase the extra komi when winning big. This reduces + * the number of point-wasting moves and makes the game more enjoyable for humans. */ + +struct dynkomi_linear { + int handicap_value[S_MAX]; + int moves[S_MAX]; + bool rootbased; + /* Increase the extra komi if my win ratio > green_zone but always + * keep extra_komi <= komi_ratchet. komi_ratchet starts infinite but decreases + * when we give too much extra komi and this puts us back < orange_zone. + * This is meant only to increase the territory margin when playing against + * weaker opponents. We never take negative komi when losing. The ratchet helps + * avoiding oscillations and keeping us above orange_zone. + * To disable the adaptive phase, set green_zone=2. */ + floating_t komi_ratchet; + floating_t green_zone; + floating_t orange_zone; + floating_t drop_step; +}; + +static floating_t +linear_permove(struct uct_dynkomi *d, struct board *b, struct tree *tree) +{ + struct dynkomi_linear *l = d->data; + enum stone color = d->uct->pondering ? tree->root_color : stone_other(tree->root_color); + int lmoves = l->moves[color]; + floating_t extra_komi; + + if (b->moves < lmoves) { + floating_t base_komi = board_effective_handicap(b, l->handicap_value[color]); + extra_komi = base_komi * (lmoves - b->moves) / lmoves; + return extra_komi; + } else { + extra_komi = floor(tree->extra_komi); + } + + /* Do not take decisions on unstable value. */ + if (tree->root->u.playouts < GJ_MINGAMES) return extra_komi; + + floating_t my_value = tree_node_get_value(tree, 1, tree->root->u.value); + /* We normalize komi as in komi_by_value(), > 0 when winning. */ + extra_komi = komi_by_color(extra_komi, color); + if (extra_komi < 0 && DEBUGL(3)) + fprintf(stderr, "XXX: extra_komi %.3f < 0 (color %s tree ek %.3f)\n", extra_komi, stone2str(color), tree->extra_komi); + // assert(extra_komi >= 0); + floating_t orig_komi = extra_komi; + + if (my_value < 0.5 && l->komi_ratchet > 0 && l->komi_ratchet != INFINITY) { + if (DEBUGL(0)) + fprintf(stderr, "losing %f extra komi %.1f ratchet %.1f -> 0\n", + my_value, extra_komi, l->komi_ratchet); + /* Disable dynkomi completely, too dangerous in this game. */ + extra_komi = l->komi_ratchet = 0; + + } else if (my_value < l->orange_zone && extra_komi > 0) { + extra_komi = l->komi_ratchet = fmax(extra_komi - l->drop_step, 0.0); + if (extra_komi != orig_komi && DEBUGL(3)) + fprintf(stderr, "dropping to %f, extra komi %.1f -> ratchet %.1f\n", + my_value, orig_komi, extra_komi); + + } else if (my_value > l->green_zone && extra_komi + 1 <= l->komi_ratchet) { + extra_komi += 1; + if (extra_komi != orig_komi && DEBUGL(3)) + fprintf(stderr, "winning %f extra_komi %.1f -> %.1f, ratchet %.1f\n", + my_value, orig_komi, extra_komi, l->komi_ratchet); + } + return komi_by_color(extra_komi, color); +} + +static floating_t +linear_persim(struct uct_dynkomi *d, struct board *b, struct tree *tree, struct tree_node *node) +{ + struct dynkomi_linear *l = d->data; + if (l->rootbased) + return tree->extra_komi; + /* We don't reuse computed value from tree->extra_komi, + * since we want to use value correct for this node depth. + * This also means the values will stay correct after + * node promotion. */ + return linear_permove(d, b, tree); +} + +struct uct_dynkomi * +uct_dynkomi_init_linear(struct uct *u, char *arg, struct board *b) +{ + struct uct_dynkomi *d = calloc2(1, sizeof(*d)); + d->uct = u; + d->permove = linear_permove; + d->persim = linear_persim; + d->done = generic_done; + + struct dynkomi_linear *l = calloc2(1, sizeof(*l)); + d->data = l; + + /* Force white to feel behind and try harder, but not to the + * point of resigning immediately in high handicap games. + * By move 100 white should still be behind but should have + * caught up enough to avoid resigning. */ + int moves = board_large(b) ? 100 : 50; + if (!board_small(b)) { + l->moves[S_BLACK] = moves; + l->moves[S_WHITE] = moves; + } + + /* The real value of one stone is twice the komi so about 15 points. + * But use a lower value to avoid being too pessimistic as black + * or too optimistic as white. */ + l->handicap_value[S_BLACK] = 8; + l->handicap_value[S_WHITE] = 1; + + l->komi_ratchet = INFINITY; + l->green_zone = 0.85; + l->orange_zone = 0.8; + l->drop_step = 4.0; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "moves") && optval) { + /* Dynamic komi in handicap game; linearly + * decreases to basic settings until move + * #optval. moves=blackmoves%whitemoves */ + for (int i = S_BLACK; *optval && i <= S_WHITE; i++) { + l->moves[i] = atoi(optval); + optval += strcspn(optval, "%"); + if (*optval) optval++; + } + } else if (!strcasecmp(optname, "handicap_value") && optval) { + /* Point value of single handicap stone, + * for dynkomi computation. */ + for (int i = S_BLACK; *optval && i <= S_WHITE; i++) { + l->handicap_value[i] = atoi(optval); + optval += strcspn(optval, "%"); + if (*optval) optval++; + } + } else if (!strcasecmp(optname, "rootbased")) { + /* If set, the extra komi applied will be + * the same for all simulations within a move, + * instead of being same for all simulations + * within the tree node. */ + l->rootbased = !optval || atoi(optval); + } else if (!strcasecmp(optname, "green_zone") && optval) { + /* Increase komi when win ratio is above green_zone */ + l->green_zone = atof(optval); + } else if (!strcasecmp(optname, "orange_zone") && optval) { + /* Decrease komi when > 0 and win ratio is below orange_zone */ + l->orange_zone = atof(optval); + } else if (!strcasecmp(optname, "drop_step") && optval) { + /* Decrease komi by drop_step points */ + l->drop_step = atof(optval); + } else { + fprintf(stderr, "uct: Invalid dynkomi argument %s or missing value\n", optname); + exit(1); + } + } + } + + return d; +} + + +/* ADAPTIVE dynkomi strategy - Adaptive Situational Compensation */ +/* We adapt the komi based on current situation: + * (i) score-based: We maintain the average score outcome of our + * games and adjust the komi by a fractional step towards the expected + * score; + * (ii) value-based: While winrate is above given threshold, adjust + * the komi by a fixed step in the appropriate direction. + * These adjustments can be + * (a) Move-stepped, new extra komi value is always set only at the + * beginning of the tree search for next move; + * (b) Continuous, new extra komi value is periodically re-determined + * and adjusted throughout a single tree search. */ + +struct dynkomi_adaptive { + /* Do not take measured average score into regard for + * first @lead_moves - the variance is just too much. + * (Instead, we consider the handicap-based komi provided + * by linear dynkomi.) */ + int lead_moves; + /* Maximum komi to pretend the opponent to give. */ + floating_t max_losing_komi; + /* Game portion at which losing komi is not allowed anymore. */ + floating_t losing_komi_stop; + /* Turn off dynkomi at the (perceived) closing of the game + * (last few moves). */ + bool no_komi_at_game_end; + /* Alternative game portion determination. */ + bool adapt_aport; + floating_t (*indicator)(struct uct_dynkomi *d, struct board *b, struct tree *tree, enum stone color); + + /* Value-based adaptation. */ + floating_t zone_red, zone_green; + int score_step; + floating_t score_step_byavg; // use portion of average score as increment + bool use_komi_ratchet; + bool losing_komi_ratchet; // ratchet even losing komi + int komi_ratchet_maxage; + // runtime, not configuration: + int komi_ratchet_age; + floating_t komi_ratchet; + + /* Score-based adaptation. */ + floating_t (*adapter)(struct uct_dynkomi *d, struct board *b); + floating_t adapt_base; // [0,1) + /* Sigmoid adaptation rate parameter; see below for details. */ + floating_t adapt_phase; // [0,1] + floating_t adapt_rate; // [1,infty) + /* Linear adaptation rate parameter. */ + int adapt_moves; + floating_t adapt_dir; // [-1,1] +}; +#define TRUSTWORTHY_KOMI_PLAYOUTS 200 + +static floating_t +board_game_portion(struct dynkomi_adaptive *a, struct board *b) +{ + if (!a->adapt_aport) { + int total_moves = b->moves + 2 * board_estimated_moves_left(b); + return (floating_t) b->moves / total_moves; + } else { + int brsize = board_size(b) - 2; + return 1.0 - (floating_t) b->flen / (brsize * brsize); + } +} + +static floating_t +adapter_sigmoid(struct uct_dynkomi *d, struct board *b) +{ + struct dynkomi_adaptive *a = d->data; + /* Figure out how much to adjust the komi based on the game + * stage. The adaptation rate is 0 at the beginning, + * at game stage a->adapt_phase crosses though 0.5 and + * approaches 1 at the game end; the slope is controlled + * by a->adapt_rate. */ + floating_t game_portion = board_game_portion(a, b); + floating_t l = game_portion - a->adapt_phase; + return 1.0 / (1.0 + exp(-a->adapt_rate * l)); +} + +static floating_t +adapter_linear(struct uct_dynkomi *d, struct board *b) +{ + struct dynkomi_adaptive *a = d->data; + /* Figure out how much to adjust the komi based on the game + * stage. We just linearly increase/decrease the adaptation + * rate for first N moves. */ + if (b->moves > a->adapt_moves) + return 0; + if (a->adapt_dir < 0) + return 1 - (- a->adapt_dir) * b->moves / a->adapt_moves; + else + return a->adapt_dir * b->moves / a->adapt_moves; +} + +static floating_t +komi_by_score(struct uct_dynkomi *d, struct board *b, struct tree *tree, enum stone color) +{ + struct dynkomi_adaptive *a = d->data; + if (d->score.playouts < TRUSTWORTHY_KOMI_PLAYOUTS) + return tree->extra_komi; + + struct move_stats score = d->score; + /* Almost-reset tree->score to gather fresh stats. */ + d->score.playouts = 1; + + /* Look at average score and push extra_komi in that direction. */ + floating_t p = a->adapter(d, b); + p = a->adapt_base + p * (1 - a->adapt_base); + if (p > 0.9) p = 0.9; // don't get too eager! + floating_t extra_komi = tree->extra_komi + p * score.value; + if (DEBUGL(3)) + fprintf(stderr, "mC += %f * %f\n", p, score.value); + return extra_komi; +} + +static floating_t +komi_by_value(struct uct_dynkomi *d, struct board *b, struct tree *tree, enum stone color) +{ + struct dynkomi_adaptive *a = d->data; + if (d->value.playouts < TRUSTWORTHY_KOMI_PLAYOUTS) + return tree->extra_komi; + + struct move_stats value = d->value; + /* Almost-reset tree->value to gather fresh stats. */ + d->value.playouts = 1; + /* Correct color POV. */ + if (color == S_WHITE) + value.value = 1 - value.value; + + /* We have three "value zones": + * red zone | yellow zone | green zone + * ~45% ~60% + * red zone: reduce komi + * yellow zone: do not touch komi + * green zone: enlage komi. + * + * Also, at some point komi will be tuned in such way + * that it will be in green zone but increasing it will + * be unfeasible. Thus, we have a _ratchet_ - we will + * remember the last komi that has put us into the + * red zone, and not use it or go over it. We use the + * ratchet only when giving extra komi, we always want + * to try to reduce extra komi we take. + * + * TODO: Make the ratchet expire after a while. */ + + /* We use komi_by_color() first to normalize komi + * additions/subtractions, then apply it again on + * return value to restore original komi parity. */ + /* Positive extra_komi means that we are _giving_ + * komi (winning), negative extra_komi is _taking_ + * komi (losing). */ + floating_t extra_komi = komi_by_color(tree->extra_komi, color); + int score_step_red = -a->score_step; + int score_step_green = a->score_step; + + if (a->score_step_byavg != 0) { + struct move_stats score = d->score; + /* Almost-reset tree->score to gather fresh stats. */ + d->score.playouts = 1; + /* Correct color POV. */ + if (color == S_WHITE) + score.value = - score.value; + if (score.value > 0) + score_step_green = round(score.value * a->score_step_byavg); + else + score_step_red = round(-score.value * a->score_step_byavg); + if (score_step_green < 0 || score_step_red > 0) { + /* The steps are in bad direction - keep still. */ + return komi_by_color(extra_komi, color); + } + } + + /* Wear out the ratchet. */ + if (a->use_komi_ratchet && a->komi_ratchet_maxage > 0) { + a->komi_ratchet_age += value.playouts; + if (a->komi_ratchet_age > a->komi_ratchet_maxage) { + a->komi_ratchet = 1000; + a->komi_ratchet_age = 0; + } + } + + if (value.value < a->zone_red) { + /* Red zone. Take extra komi. */ + if (DEBUGL(3)) + fprintf(stderr, "[red] %f, step %d | komi ratchet %f age %d/%d -> %f\n", + value.value, score_step_red, a->komi_ratchet, a->komi_ratchet_age, a->komi_ratchet_maxage, extra_komi); + if (a->losing_komi_ratchet || extra_komi > 0) { + a->komi_ratchet = extra_komi; + a->komi_ratchet_age = 0; + } + extra_komi += score_step_red; + return komi_by_color(extra_komi, color); + + } else if (value.value < a->zone_green) { + /* Yellow zone, do nothing. */ + return komi_by_color(extra_komi, color); + + } else { + /* Green zone. Give extra komi. */ + if (DEBUGL(3)) + fprintf(stderr, "[green] %f, step %d | komi ratchet %f age %d/%d\n", + value.value, score_step_green, a->komi_ratchet, a->komi_ratchet_age, a->komi_ratchet_maxage); + extra_komi += score_step_green; + if (a->use_komi_ratchet && extra_komi >= a->komi_ratchet) + extra_komi = a->komi_ratchet - 1; + return komi_by_color(extra_komi, color); + } +} + +static floating_t +bounded_komi(struct dynkomi_adaptive *a, struct board *b, + enum stone color, floating_t komi, floating_t max_losing_komi) +{ + /* At the end of game, disallow losing komi. */ + if (komi_by_color(komi, color) < 0 + && board_game_portion(a, b) > a->losing_komi_stop) + return 0; + + /* Get lower bound on komi we take so that we don't underperform + * too much. */ + floating_t min_komi = komi_by_color(- max_losing_komi, color); + + if (komi_by_color(komi - min_komi, color) > 0) + return komi; + else + return min_komi; +} + +static floating_t +adaptive_permove(struct uct_dynkomi *d, struct board *b, struct tree *tree) +{ + struct dynkomi_adaptive *a = d->data; + enum stone color = stone_other(tree->root_color); + + /* We do not use extra komi at the game end - we are not + * to fool ourselves at this point. */ + if (a->no_komi_at_game_end && board_estimated_moves_left(b) <= MIN_MOVES_LEFT) { + tree->use_extra_komi = false; + return 0; + } + + if (DEBUGL(4)) + fprintf(stderr, "m %d/%d ekomi %f permove %f/%d\n", + b->moves, a->lead_moves, tree->extra_komi, + d->score.value, d->score.playouts); + + if (b->moves <= a->lead_moves) + return bounded_komi(a, b, color, + board_effective_handicap(b, 7 /* XXX */), + a->max_losing_komi); + + floating_t komi = a->indicator(d, b, tree, color); + if (DEBUGL(4)) + fprintf(stderr, "dynkomi: %f -> %f\n", tree->extra_komi, komi); + return bounded_komi(a, b, color, komi, a->max_losing_komi); +} + +static floating_t +adaptive_persim(struct uct_dynkomi *d, struct board *b, struct tree *tree, struct tree_node *node) +{ + return tree->extra_komi; +} + +struct uct_dynkomi * +uct_dynkomi_init_adaptive(struct uct *u, char *arg, struct board *b) +{ + struct uct_dynkomi *d = calloc2(1, sizeof(*d)); + d->uct = u; + d->permove = adaptive_permove; + d->persim = adaptive_persim; + d->done = generic_done; + + struct dynkomi_adaptive *a = calloc2(1, sizeof(*a)); + d->data = a; + + a->lead_moves = board_large(b) ? 20 : 4; // XXX + a->max_losing_komi = 30; + a->losing_komi_stop = 1.0f; + a->no_komi_at_game_end = true; + a->indicator = komi_by_value; + + a->adapter = adapter_sigmoid; + a->adapt_rate = -18; + a->adapt_phase = 0.65; + a->adapt_moves = 200; + a->adapt_dir = -0.5; + + a->zone_red = 0.45; + a->zone_green = 0.50; + a->score_step = 1; + a->use_komi_ratchet = true; + a->komi_ratchet_maxage = 0; + a->komi_ratchet = 1000; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "lead_moves") && optval) { + /* Do not adjust komi adaptively for first + * N moves. */ + a->lead_moves = atoi(optval); + } else if (!strcasecmp(optname, "max_losing_komi") && optval) { + a->max_losing_komi = atof(optval); + } else if (!strcasecmp(optname, "losing_komi_stop") && optval) { + a->losing_komi_stop = atof(optval); + } else if (!strcasecmp(optname, "no_komi_at_game_end")) { + a->no_komi_at_game_end = !optval || atoi(optval); + } else if (!strcasecmp(optname, "indicator")) { + /* Adaptatation indicator - how to decide + * the adaptation rate and direction. */ + if (!strcasecmp(optval, "value")) { + /* Winrate w/ komi so far. */ + a->indicator = komi_by_value; + } else if (!strcasecmp(optval, "score")) { + /* Expected score w/ current komi. */ + a->indicator = komi_by_score; + } else { + fprintf(stderr, "UCT: Invalid indicator %s\n", optval); + exit(1); + } + + /* value indicator settings */ + } else if (!strcasecmp(optname, "zone_red") && optval) { + a->zone_red = atof(optval); + } else if (!strcasecmp(optname, "zone_green") && optval) { + a->zone_green = atof(optval); + } else if (!strcasecmp(optname, "score_step") && optval) { + a->score_step = atoi(optval); + } else if (!strcasecmp(optname, "score_step_byavg") && optval) { + a->score_step_byavg = atof(optval); + } else if (!strcasecmp(optname, "use_komi_ratchet")) { + a->use_komi_ratchet = !optval || atoi(optval); + } else if (!strcasecmp(optname, "losing_komi_ratchet")) { + a->losing_komi_ratchet = !optval || atoi(optval); + } else if (!strcasecmp(optname, "komi_ratchet_age") && optval) { + a->komi_ratchet_maxage = atoi(optval); + + /* score indicator settings */ + } else if (!strcasecmp(optname, "adapter") && optval) { + /* Adaptatation method. */ + if (!strcasecmp(optval, "sigmoid")) { + a->adapter = adapter_sigmoid; + } else if (!strcasecmp(optval, "linear")) { + a->adapter = adapter_linear; + } else { + fprintf(stderr, "UCT: Invalid adapter %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "adapt_base") && optval) { + /* Adaptation base rate; see above. */ + a->adapt_base = atof(optval); + } else if (!strcasecmp(optname, "adapt_rate") && optval) { + /* Adaptation slope; see above. */ + a->adapt_rate = atof(optval); + } else if (!strcasecmp(optname, "adapt_phase") && optval) { + /* Adaptation phase shift; see above. */ + a->adapt_phase = atof(optval); + } else if (!strcasecmp(optname, "adapt_moves") && optval) { + /* Adaptation move amount; see above. */ + a->adapt_moves = atoi(optval); + } else if (!strcasecmp(optname, "adapt_aport")) { + a->adapt_aport = !optval || atoi(optval); + } else if (!strcasecmp(optname, "adapt_dir") && optval) { + /* Adaptation direction vector; see above. */ + a->adapt_dir = atof(optval); + + } else { + fprintf(stderr, "uct: Invalid dynkomi argument %s or missing value\n", optname); + exit(1); + } + } + } + + return d; +} diff --git a/jni/pachi/uct/dynkomi.h b/jni/pachi/uct/dynkomi.h new file mode 100644 index 0000000..7cff32f --- /dev/null +++ b/jni/pachi/uct/dynkomi.h @@ -0,0 +1,63 @@ +#ifndef PACHI_UCT_DYNKOMI_H +#define PACHI_UCT_DYNKOMI_H + +/* Dynamic computation of artificial komi values to stabilize the MCTS. */ + +#include "move.h" +#include "uct/internal.h" +#include "uct/tree.h" + +/* Motivation: Monte Carlo Tree Search tends to produce unstable and + * unreasonable results when playing in situation of extreme advantage + * or * disadvantage, due to poor move selection becauce of low + * signal-to-noise * ratio; notably, this occurs when playing in high + * handicap game, burdening the computer with further disadvantage + * against the strong human opponent. */ + +/* Here, we try to solve the problem by adding arbitrarily computed + * komi values to the score. The used algorithm is transparent to the + * rest of UCT implementation. */ + +struct board; +struct tree; +struct tree_node; +struct uct; +struct uct_dynkomi; + +/* Compute effective komi value for given color: Positive value + * means giving komi, negative value means taking komi. */ +#define komi_by_color(komi, color) ((color) == S_BLACK ? (komi) : -(komi)) + +/* Determine base dynamic komi for this genmove run. The returned + * value is stored in tree->extra_komi and by itself used just for + * user information. */ +typedef floating_t (*uctd_permove)(struct uct_dynkomi *d, struct board *b, struct tree *tree); +/* Determine actual dynamic komi for this simulation (run on board @b + * from node @node). In some cases, this function will just return + * tree->extra_komi, in other cases it might want to adjust the komi + * according to the actual move depth. */ +typedef floating_t (*uctd_persim)(struct uct_dynkomi *d, struct board *b, struct tree *tree, struct tree_node *node); +/* Destroy the uct_dynkomi structure. */ +typedef void (*uctd_done)(struct uct_dynkomi *d); + +struct uct_dynkomi { + struct uct *uct; + uctd_permove permove; + uctd_persim persim; + uctd_done done; + void *data; + + /* Game state for dynkomi use: */ + /* Information on average score at the simulation end (black's + * perspective) since last dynkomi adjustment. */ + struct move_stats score; + /* Information on average winrate of simulations since last + * dynkomi adjustment. */ + struct move_stats value; +}; + +struct uct_dynkomi *uct_dynkomi_init_none(struct uct *u, char *arg, struct board *b); +struct uct_dynkomi *uct_dynkomi_init_linear(struct uct *u, char *arg, struct board *b); +struct uct_dynkomi *uct_dynkomi_init_adaptive(struct uct *u, char *arg, struct board *b); + +#endif diff --git a/jni/pachi/uct/internal.h b/jni/pachi/uct/internal.h new file mode 100644 index 0000000..f608156 --- /dev/null +++ b/jni/pachi/uct/internal.h @@ -0,0 +1,173 @@ +#ifndef PACHI_UCT_INTERNAL_H +#define PACHI_UCT_INTERNAL_H + +/* Internal UCT structures */ + +#include "debug.h" +#include "move.h" +#include "ownermap.h" +#include "pattern.h" +#include "patternsp.h" +#include "patternprob.h" +#include "playout.h" +#include "stats.h" + +struct tree; +struct tree_node; +struct uct_policy; +struct uct_prior; +struct uct_dynkomi; +struct uct_pluginset; +struct joseki_dict; + +/* How big proportion of ownermap counts must be of one color to consider + * the point sure. */ +#define GJ_THRES 0.8 +/* How many games to consider at minimum before judging groups. */ +#define GJ_MINGAMES 500 + +/* Internal engine state. */ +struct uct { + int debug_level; + enum uct_reporting { + UR_TEXT, + UR_JSON, + UR_JSON_BIG, + } reporting; + int reportfreq; + + int games, gamelen; + floating_t resign_threshold, sure_win_threshold; + double best2_ratio, bestr_ratio; + floating_t max_maintime_ratio; + bool pass_all_alive; /* Current value */ + bool allow_losing_pass; + bool territory_scoring; + int expand_p; + bool playout_amaf; + bool amaf_prior; + int playout_amaf_cutoff; + int dumpthres; + int force_seed; + bool no_tbook; + bool fast_alloc; + unsigned long max_tree_size; + unsigned long max_pruned_size; + unsigned long pruning_threshold; + int mercymin; + int significant_threshold; + + int threads; + enum uct_thread_model { + TM_TREE, /* Tree parallelization w/o virtual loss. */ + TM_TREEVL, /* Tree parallelization with virtual loss. */ + } thread_model; + int virtual_loss; + bool pondering_opt; /* User wants pondering */ + bool pondering; /* Actually pondering now */ + bool slave; /* Act as slave in distributed engine. */ + int max_slaves; /* Optional, -1 if not set */ + int slave_index; /* 0..max_slaves-1, or -1 if not set */ + enum stone my_color; + + int fuseki_end; + int yose_start; + + int dynkomi_mask; + int dynkomi_interval; + struct uct_dynkomi *dynkomi; + floating_t initial_extra_komi; + + floating_t val_scale; + int val_points; + bool val_extra; + bool val_byavg; + bool val_bytemp; + floating_t val_bytemp_min; + + int random_policy_chance; + bool local_tree; + int tenuki_d; + floating_t local_tree_aging; +#define LTREE_PLAYOUTS_MULTIPLIER 100 + floating_t local_tree_depth_decay; + bool local_tree_allseq; + bool local_tree_neival; + enum { + LTE_ROOT, + LTE_EACH, + LTE_TOTAL, + } local_tree_eval; + bool local_tree_rootchoose; + + char *banner; + + struct uct_policy *policy; + struct uct_policy *random_policy; + struct playout_policy *playout; + struct uct_prior *prior; + struct uct_pluginset *plugins; + struct joseki_dict *jdict; + + struct pattern_setup pat; + /* Various modules (prior, policy, ...) set this if they want pattern + * database to be loaded. */ + bool want_pat; + + /* Used within frame of single genmove. */ + struct board_ownermap ownermap; + /* Used for coordination among slaves of the distributed engine. */ + int stats_hbits; + int shared_nodes; + int shared_levels; + double stats_delay; /* stored in seconds */ + int played_own; + int played_all; /* games played by all slaves */ + + /* Game state - maintained by setup_state(), reset_state(). */ + struct tree *t; +}; + +#define UDEBUGL(n) DEBUGL_(u->debug_level, n) + +bool uct_pass_is_safe(struct uct *u, struct board *b, enum stone color, bool pass_all_alive); + +void uct_prepare_move(struct uct *u, struct board *b, enum stone color); +void uct_genmove_setup(struct uct *u, struct board *b, enum stone color); +void uct_pondering_stop(struct uct *u); + + +/* This is the state used for descending the tree; we use this wrapper + * structure in order to be able to easily descend in multiple trees + * in parallel (e.g. main tree and local tree) or compute cummulative + * "path value" throughout the tree descent. */ +struct uct_descent { + /* Active tree nodes: */ + struct tree_node *node; /* Main tree. */ + struct tree_node *lnode; /* Local tree. */ + /* Value of main tree node (with all value factors, but unbiased + * - without exploration factor), from black's perspective. */ + struct move_stats value; +}; + + +typedef struct tree_node *(*uctp_choose)(struct uct_policy *p, struct tree_node *node, struct board *b, enum stone color, coord_t exclude); +typedef floating_t (*uctp_evaluate)(struct uct_policy *p, struct tree *tree, struct uct_descent *descent, int parity); +typedef void (*uctp_descend)(struct uct_policy *p, struct tree *tree, struct uct_descent *descent, int parity, bool allow_pass); +typedef void (*uctp_winner)(struct uct_policy *p, struct tree *tree, struct uct_descent *descent); +typedef void (*uctp_prior)(struct uct_policy *p, struct tree *tree, struct tree_node *node, struct board *b, enum stone color, int parity); +typedef void (*uctp_update)(struct uct_policy *p, struct tree *tree, struct tree_node *node, enum stone node_color, enum stone player_color, struct playout_amafmap *amaf, struct board *final_board, floating_t result); + +struct uct_policy { + struct uct *uct; + uctp_choose choose; + uctp_winner winner; + uctp_evaluate evaluate; + uctp_descend descend; + uctp_update update; + uctp_prior prior; + bool wants_amaf; + void *data; +}; + +#endif diff --git a/jni/pachi/uct/plugin.h b/jni/pachi/uct/plugin.h new file mode 100644 index 0000000..953554d --- /dev/null +++ b/jni/pachi/uct/plugin.h @@ -0,0 +1,66 @@ +#ifndef PACHI_UCT_PLUGIN_H +#define PACHI_UCT_PLUGIN_H + +/* This is the public API for external Pachi UCT plugins. */ +/* Unlike the rest of Pachi, this file is available for anyone for + * unrestricted use and distribution. The plugin interface is not + * restricted by the terms of GPL and plugins may have any arbitrary + * licence conditions and do not need to be GPL or open-source at all. */ + +/* Contrast this with , which is internal Pachi API + * for calling all loaded modules. */ + +/* In order to compile your plugin, pass cc an -I parameter pointing + * at the root Pachi sources directory and include this file - it should + * pull in everything else neccessary. */ + +/* No API stability guarantees can be made at this point. The board + * structure and UCT tree in particular _are_ likely to change again. */ + +#include // we use assertions a lot, even in .h files + +#include "stone.h" // stones and their colors +#include "move.h" // coordinates and (coordinate, stone) pairs ("moves") +#include "board.h" // the goban structure and basic interface +#include "uct/prior.h" // the UCT tree prior values assignment +#include "uct/tree.h" // the UCT minimax tree and node structures + + +/* This function is called at the time a new game is started (more precisely, + * when clear_board GTP command is received; board size is already known and + * the board structure is initialized, but komi is not known yet), with + * argument string passed on Pachi's commandline. The pointer the function + * returns is assumed to be the plugin's context and will be passed to all + * subsequent functions called within the game. + * + * The seed is a random seed in the range of 0..65335. If the plugin will use + * fast_random() from Pachi's , this should be ignored; otherwise, + * if the plugin is using a random generator, it should be seeded with this + * value so that Pachi would play the same game with the same random seed. + * + * When the game finishes and new game is started, the current context is + * deinitialized and the init function is called again. The game is monotonic; + * no moves are undone when made (in case of undo, the game is cancelled and + * re-played from beginning). */ +void *pachi_plugin_init(char *args, struct board *b, int seed); + +/* This function is called when priors are to be assigned to all leaves + * of a given node. Usually, the leaves have been freshly expanded (but in + * theory, this may be also a delayed bias). Eqex is a recommendation on how + * many simulations should the prior information be worth. + * + * The function should evaluate the board situation when in tree node @node + * and save evaluation of various coordinates to @map. The @map structure + * contains a lot of useful information; map->b is the board in the @node + * situation, map->to_play is who is to play, map->consider is bool array + * describing which coordinates are worth evaluating at all and map->distances + * are pre-computed Common Fate Graph distances from the last move. To record + * priors, use add_prior_value(). See for details and the + * for the default prior evaluation functions. */ +void pachi_plugin_prior(void *data, struct tree_node *node, struct prior_map *map, int eqex); + +/* This function is called when the game has ended and the context needs + * to be deinitialized. */ +void pachi_plugin_done(void *data); + +#endif diff --git a/jni/pachi/uct/plugin/example.c b/jni/pachi/uct/plugin/example.c new file mode 100644 index 0000000..571a366 --- /dev/null +++ b/jni/pachi/uct/plugin/example.c @@ -0,0 +1,150 @@ +/* This is an example Pachi UCT plugin. */ +/* This file is released under the same licence conditions as + * . */ + +/* We will add positive priors (1.0) for moves that play in-between + * of two different groups of the same color; that is, moves that connect + * two groups or the same color or separate two groups of the same color. + * This is not a very good prior actually, since it leads to a lot of + * useless moves. (Maybe doing this in simulations would be more interesting?) + * But it is a simple enough example. :-) */ + +/* Compile the plugin like this: + * gcc -Wall -O3 -march=native -Ipachi_source_root -shared -fPIC -o example.so example.c + * Then, load it in Pachi by passing plugin=example.so as a parameter. + * You can also pass it parameters: plugin=example.so:p1=v1:p2=v2. + * The module supports these parameters: + * eqex Number of prior'd simulations, overrides Pachi default + * selfatari If specified (selfatari or selfatari=1), test for selfatari + * before giving the prior + */ + +#include +#include + +/* The basic plugin interface. */ +#include "uct/plugin.h" +/* The tactical reading tools, for selfatari testing. */ +#include "tactics/selfatari.h" + +/* Our context structure. */ +struct context { + int eqex; + bool selfatari; +}; + + +void +pachi_plugin_prior(void *data, struct tree_node *node, struct prior_map *map, int eqex) +{ + struct context *ctx = data; + struct board *b = map->b; + if (ctx->eqex >= 0) + eqex = ctx->eqex; // override Pachi default + + /* foreach_free_point defines a variable @c corresponding + * to our current coordinate. */ + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + + /* We will look at the current point's 4-neighborhood; + * we are to set a prior if we spot two different + * groups of the same color. */ + + /* First, a shortcut: We keep track of numbers of neighboring + * stones. The | is not a typo. */ + if ((neighbor_count_at(b, c, S_BLACK) | neighbor_count_at(b, c, S_WHITE)) <= 1) + continue; + + /* Keep track of seen groups for each color; at each + * point, we will look only at groups with the same color. */ + int groups[S_MAX] = {0, 0, 0, 0}; + + /* foreach_neighbor goes through all direct neighbors + * of a given coordinate defines also its own variable @c + * corresponding to the current coordinate. */ + foreach_neighbor(b, c, { + enum stone s = board_at(b, c); // color of stone + group_t g = group_at(b, c); // id of a group + if (!g) continue; // no group at this coord + + if (!groups[s]) { + /* First time we see a group of this color. */ + groups[s] = g; + continue; + } + if (groups[s] == g) { + /* We have already seen this group. */ + continue; + } + /* We have already seen another group of this color! + * We can connect or split. */ + goto set_prior; + }); + /* If we reach this point, we have not seen any two groups + * to connect. */ + continue; + +set_prior: + /* Check if our move here is not self-atari if the option + * is enabled. */ + if (ctx->selfatari && is_bad_selfatari(b, map->to_play, c)) + continue; + + /* Finally record the prior; value is 0.0 (avoid) to 1.0 + * (strongly favor). eqex is the number of simulations + * the value is worth. */ + add_prior_value(map, c, 1.0, eqex); + } foreach_free_point_end; +} + +void * +pachi_plugin_init(char *arg, struct board *b, int seed) +{ + struct context *ctx = calloc(1, sizeof(*ctx)); + + /* Initialize ctx defaults here. */ + ctx->eqex = -1; + + /* This is the canonical Pachi arguments parser. You do not strictly + * need to decypher it, you can just use it as a boilerplate. */ + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "eqex") && optval) { + /* eqex takes a required integer argument */ + ctx->eqex = atoi(optval); + + } else if (!strcasecmp(optname, "selfatari")) { + /* selfatari takes an optional integer + * argument */ + ctx->selfatari = !optval || atoi(optval); + + } else { + fprintf(stderr, "example plugin: Invalid argument %s or missing value\n", optname); + exit(1); + } + } + } + + /* Initialize the rest of ctx (depending on arguments) here. */ + + return ctx; +} + +void +pachi_plugin_done(void *data) +{ + struct context *ctx = data; + /* No big magic. */ + free(ctx); +} diff --git a/jni/pachi/uct/plugin/wolf.c b/jni/pachi/uct/plugin/wolf.c new file mode 100644 index 0000000..a94843f --- /dev/null +++ b/jni/pachi/uct/plugin/wolf.c @@ -0,0 +1,386 @@ +/* This is a Pachi UCT plugin for Thomas Wolf's move evaluation API. */ +/* This file is released under the same licence conditions as + * . */ + +/* We will add positive priors (1.0) for moves that play in-between + * of two different groups of the same color; that is, moves that connect + * two groups or the same color or separate two groups of the same color. + * This is not a very good prior actually, since it leads to a lot of + * useless moves. (Maybe doing this in simulations would be more interesting?) + * But it is a simple enough example. :-) */ + +/* Compile the plugin like this: + * gcc -Wall -O3 -march=native -Ipachi_source_root -shared -fPIC -o wolf.so wolf.c + * Then, load it in Pachi by passing plugin=wolf.so as a parameter. + * You can also pass it parameters: plugin=wolf.so:p1=v1:p2=v2. + * The plugin supports these parameters: + * file Filename of the real module used + * eqex Number of prior'd simulations, overrides Pachi default + * threshold Threshold value when to stop iterating influence/strength values. + * overrelax Overrelaxation parameter. Should probably not be changed from 1.0 + * iterations Upper bound of the number of iters for each empty point or chain. + */ + + +/* This is the Thomas Wolf's API we implement: + +The library currently provides 5 functions: + + SETPARAM + EVALFUN1 + EVALFUN2 + FINDMOVE1 + FINDMOVE2 + +of which SETPARAM, EVALFUN2, FINDMOVE2 are the ones likely to be used +in other programs. + +- - - - + +SETPARAM + +This procedure has to be called before any EVALFUN call but can also be +called again later. It sets parameters for the computation of the +influence function. + +The first parameter is a threshold value when to stop iterating +influence/strength values. Characteristics: The smaller the value the +more accurate the influence value but the slower the computation due +to an increase in iterations. But because accuracy is limited by +systematic errors, too small values do not bring improvement, +especially not in unstable situations. + +The second is an overrelaxation parameter. Should probably not be +changed from 1.0 . + +The third is an upper bound of the number of iterations for each empty +point or chain. Again, the more the better but also the slower. Again, +improvement is overshadowed by systematic errors, especially in +unstable situations. Typical values have the range 3..65000. + +- - - - + +EVALFUN1, EVALFUN2 + +Both are one and the same static evaluation function, only with +different interface. + +Input: + +For both functions the first parameter is a string +which provides the necessary input by specifying the board position, +who moves next, specifying whether the evaluation function should +be applied ad hoc (task=3) or incrementally (task=4) and which +optionally allows to specify unconditionally alive chains. The +syntax of the input for EVALFUN1 and EVALFUN2 is the following: + +- Only for ad hoc mode: + A single digit specifying the size of the board: + 1 --> 19x19, 2 --> 17x17, 3 --> 15x15, 4 --> 13x13, + 5 --> 11x11, 6 --> 9x9, 7 --> 7x7, 8 --> 5x5 +- Only for ad hoc mode: + All stones on the board. + Because there are more than 256 fields and sending non-alphanumeric + characters may give problems we need 2 coordinates for each stone + anyway. In these coordinates (1,1) is in the lower left corner + and the 1st increases to the right and the 2nd increases vertically + to the top. + Black stone coordinates are encoded by chr(64+x), so 'A' would + be 1, 'B' be 2,... and white stone coordinates by chr(96+x), so + 'a' is 1, 'b' is 2, ... +- If there is a field forbidden by ko then the first coordinate + is a capital letter and the second a lower case letter. +- Next is a single character '%' indicating the end of the sequence of fields. +- Next is a single character specifying who moves first and form of solution: + 'b' Black moves first <*> + 'w' White moves first <*> +- The task: a single digit that specifies the task. + Available currently: +3 ... evaluation of all chains and empty fields within a region, adhoc version. + If the region is the whole board no further data follow. + If the region is bounded then the following data consist of points (x,y). + Each point is encoded by 2 byte, either as chr(64+x) chr(64+y), i.e. as + upper case letters or as chr(96+x) chr(96+y), i.e. lower case letters. + Whether upper or lower case does not depend on the colour (the colour of the + stones has already been specified above) but is defined as follows: + - The first point (occupied or not) marks the inside of the region to be + evaluated. For that one can use upper or lower case letters. + - Each one of any further points marks a chain that shall be assumed to be + alive, i.e. such a chain will be a boundary to the region to be evaluated. + If a field marking a chain is encoded in upper case letters by + chr(64+x) chr(64+y) then the chain is assumed to be statically alive which + is a property that will be respected in future calls of task 4 below. If + the chain marking field is encoded in lower case letters then it treated + as alive for now in this computation of the evaluation function but not + necessarily in future calls of task 4. + +4 ... evaluation of all chains and empty fields within a region, incremental version. + Input is exactly exactly like task 3. The difference to task 3 is that this + position has already been evaluated in the previous call with task 3 or 4. + Therefore, no boardsize, no stones and no ko-field are specified. That means + the complete input is: + - The first character is '%' + - followed by 'b' or 'w' to mark the colour moving next, <*>. + - a digit for the task, so 4, + - optional, like in task 3: fields marking alive chains, + - a '%' character to mark the end of the optional list of alive chains + - a point (x,y) encoded by 2 byte chr(64+x) chr(64+y) that marks + the coordinates of the next move of the colour given in <*>. + +Output: + +EVALFUN1 has a simple interface, providing with its 2nd parameter a +string that gives for each board intersection +- the influence value of Black (in the range 0 .. 1.0) if the + intersection is empty +- the strength value of the chain occupying that intersection + (in the range 0.0 = dead .. 1.0 = uncnditionally alive) if the + intersection is not empty. +This interface is simple but slow because this string has to be +generated and to be decoded to use any information. + +EVALFUN2 gives direct access to the relevant data. +- For an intersection with coordinates (x,y) (1<=x<=boardsize from + left to right, 1<=y<=boardsize from bottom to top) the 2-dim + array PChainNo(x,y) + - is 0 if the intersection is empty, + - gives the chain number of the chain that occupies (x,y). +- For an empty intersection with coordinates (x,y) (1<=x<=boardsize from + left to right, 1<=y<=boardsize from bottom to top) the record/structure + PPD(i,j) stores relevant data and boc is the influence value of Black. +- For an intersection (x,y) occupied by a chain with number n the + record/structure PCD(n) stores the relevant data of this chain + and the svl component gives a strength value in the interval + 0.0 (= unconditionally dead) to 1.0 (= unconditionally alive). + +- - - - + +FINDMOVE1, FINDMOVE2 + +Before calling FINDMOVE1/2 the evaluation function EVALFUN1 or +EVALFUN2 for the current board position has to be executed. + +Both, FINDMOVE1 and FINDMOVE2, are one and the same function that +provides a ranking of legal moves, only with different +interface. Currently FINDMOVE performs each legal move, updates the +static evaluation and adds up probabilities over the whole board to +arrive at a rough score for each move. Thus, depending on the number +of legal moves this function is up to 360 times slower than EVALFUN in +update mode. + +- The first parameter is specifying which sides moves next: White=-1, Black=1. +- The 2nd parameter of FINDMOVE1 is an output string giving for each legal + intersection a measure of value of doing the next move there. + The first move is the supposedly best move. All other moves are + listed line by line of the board. +- For FINDMOVE2 the 2nd and 3rd parameter are the x- and y-coordinate + of the best move and the 4th parameter is a 'value' of that move. + The 5th parameter is a 2-dim array which for each intersection + gives a 'value' of moving there. To identify whether the intersection is + empty the last parameter is a 2-dim array giving a value 0 if it is + empty (otherwise the number of the chain that occupies the intersection). + +- - - - + + */ + +#include +#include +#include + +/* The basic plugin interface. */ +#include "uct/plugin.h" + +/* The API types: */ +#define MAXBOARDSIZE 19 +typedef char byte_board[MAXBOARDSIZE+2][MAXBOARDSIZE+2]; // The array indices are 1-based! +typedef floating_t influ_board[MAXBOARDSIZE][MAXBOARDSIZE]; + +/* Our context structure. */ +struct context { + int eqex; + + void *dlh; + void (*SETPARAM)(double sv, double omega, uint16_t mi); + void (*EVALFUN1)(char *javp, char *InfluField); + void (*FINDMOVE2)(int fa, char *mi, char *mj, floating_t *mxscore, influ_board *SB, byte_board **PChainNo); +}; + + +char +bsize2digit(int size) +{ + switch (size) { + case 19: return '1'; + case 17: return '2'; + case 15: return '3'; + case 13: return '4'; + case 11: return '5'; + case 9: return '6'; + case 7: return '7'; + case 5: return '8'; + default: + fprintf(stderr, "wolf plugin: Unsupported board size: %d\n", size); + exit(1); + } +} + +char +coord2digit(enum stone color, int coord) +{ + assert(color == S_BLACK || color == S_WHITE); + return (color == S_BLACK ? 64 + coord : 96 + coord); +} + +void +pachi_plugin_prior(void *data, struct tree_node *node, struct prior_map *map, int eqex) +{ + struct context *ctx = data; + struct board *b = map->b; + if (ctx->eqex >= 0) + eqex = ctx->eqex; // override Pachi default + + /* First, create a string representation of current board. */ +#define BIGSTR 10000 + char bin[BIGSTR]; + char bout[BIGSTR]; + char *bip = bin; + *bip++ = bsize2digit(board_size(b) - 2); + foreach_point(b) { + enum stone s = board_at(b, c); + if (s == S_NONE || s == S_OFFBOARD) continue; + *bip++ = coord2digit(s, coord_x(c, b)); + *bip++ = coord2digit(s, coord_y(c, b)); + } foreach_point_end; + if (!is_pass(b->ko.coord)) { + *bip++ = coord2digit(S_BLACK, coord_x(b->ko.coord, b)); + *bip++ = coord2digit(S_WHITE, coord_y(b->ko.coord, b)); + } + *bip++ = '%'; + *bip++ = map->to_play == S_BLACK ? 'b' : 'w'; + *bip++ = '3'; + *bip = 0; + + /* Seed the evaluation of the situation. */ + // fprintf(stderr, "board desc: %s\n", bin); + ctx->EVALFUN1(bin, bout); + /* We do not care about bout. */ + + /* Retrieve values of moves. */ + char bestx, besty; + floating_t bestval; + influ_board values; + byte_board *chaininfo; + ctx->FINDMOVE2(map->to_play == S_BLACK ? 1 : -1, &bestx, &besty, &bestval, &values, &chaininfo); + // fprintf(stderr, "best is (%d,%d)%s %f\n", bestx, besty, coord2sstr(coord_xy(b, bestx, besty), b), bestval); + + /* In the first pass, determine best and worst value. (Best value + * reported by FINDMOVE2 is wrong.) In the second pass, we set the + * priors by normalization based on the determined values. */ + floating_t best = -1000, worst = 1000; + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + floating_t value = values[coord_x(c, b) - 1][coord_y(c, b) - 1]; + if (map->to_play == S_WHITE) value = -value; + if (value > best) best = value; + else if (value < worst) worst = value; + } foreach_point_end; + + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + + /* Take the value and normalize it somehow. */ + /* Right now, we just do this by linear rescaling from + * [worst, best] to [0,1]. */ + floating_t value = values[coord_x(c, b) - 1][coord_y(c, b) - 1]; + if (map->to_play == S_WHITE) value = -value; + value = (value - worst) / (best - worst); + // fprintf(stderr, "\t[%s %s] %f/%f\n", stone2str(map->to_play), coord2sstr(c, b), value, best); + + add_prior_value(map, c, (value - worst) / best, eqex); + } foreach_free_point_end; +} + + +void * +pachi_plugin_init(char *arg, struct board *b, int seed) +{ + struct context *ctx = calloc(1, sizeof(*ctx)); + + /* Initialize ctx defaults here. */ + char *file = NULL; + floating_t overrelax = 1.0, threshold = 0.001; + int iterations = 13; + ctx->eqex = -1; + + /* This is the canonical Pachi arguments parser. You do not strictly + * need to decypher it, you can just use it as a boilerplate. */ + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "eqex") && optval) { + /* eqex takes a required integer argument */ + ctx->eqex = atoi(optval); + + } else if (!strcasecmp(optname, "file") && optval) { + file = strdup(optval); + + } else if (!strcasecmp(optname, "threshold") && optval) { + threshold = atof(optval); + + } else if (!strcasecmp(optname, "overrelax") && optval) { + overrelax = atof(optval); + + } else if (!strcasecmp(optname, "iterations") && optval) { + iterations = atoi(optval); + + } else { + fprintf(stderr, "wolf plugin: Invalid argument %s or missing value\n", optname); + exit(1); + } + } + } + + /* Initialize the rest of ctx (depending on arguments) here. */ + if (!file) { + fprintf(stderr, "wolf plugin: file argument not specified\n"); + exit(1); + } + ctx->dlh = dlopen(file, RTLD_NOW); + if (!ctx->dlh) { + fprintf(stderr, "Cannot load file %s: %s\n", file, dlerror()); + exit(EXIT_FAILURE); + } +#define loadsym(s_) do {\ + ctx->s_ = dlsym(ctx->dlh, #s_); \ + if (!ctx->s_) { \ + fprintf(stderr, "Cannot find %s in module: %s\n", #s_, dlerror()); \ + exit(EXIT_FAILURE); \ + } \ +} while (0) + loadsym(SETPARAM); + loadsym(EVALFUN1); + loadsym(FINDMOVE2); + + ctx->SETPARAM(threshold, overrelax, iterations); + + return ctx; +} + +void +pachi_plugin_done(void *data) +{ + struct context *ctx = data; + dlclose(ctx->dlh); + free(ctx); +} diff --git a/jni/pachi/uct/plugins.c b/jni/pachi/uct/plugins.c new file mode 100644 index 0000000..79bd55d --- /dev/null +++ b/jni/pachi/uct/plugins.c @@ -0,0 +1,126 @@ +#include +#ifndef WIN32 +#include +#endif +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "move.h" +#include "random.h" +#include "uct/plugins.h" +#include "uct/prior.h" +#include "uct/tree.h" + +/* Plugin interface for UCT. External plugins may hook callbacks on various + * events and e.g. bias the tree. */ + + +/* Keep the API typedefs in sync with . */ + +struct plugin { + char *path; + char *args; + void *dlh; + void *data; + + void *(*init)(char *args, struct board *b, int seed); + void (*prior)(void *data, struct tree_node *node, struct prior_map *map, int eqex); + void (*done)(void *data); +}; + +struct uct_pluginset { + struct plugin *plugins; + int n_plugins; + struct board *b; +}; + + +#ifdef WIN32 + +/* We do not support plugins on Windows. Minimal dummy stubs. */ + +struct uct_pluginset * +pluginset_init(struct board *b) +{ + return NULL; +} +void +pluginset_done(struct uct_pluginset *ps) +{ + assert(!ps); +} +void +plugin_load(struct uct_pluginset *ps, char *path, char *args) +{ + assert(!ps); +} +void +plugin_prior(struct uct_pluginset *ps, struct tree_node *node, struct prior_map *map, int eqex) +{ + assert(!ps); +} + +#else + +struct uct_pluginset * +pluginset_init(struct board *b) +{ + struct uct_pluginset *ps = calloc(1, sizeof(*ps)); + ps->b = b; + return ps; +} + +void +pluginset_done(struct uct_pluginset *ps) +{ + for (int i = 0; i < ps->n_plugins; i++) { + struct plugin *p = &ps->plugins[i]; + p->done(p->data); + dlclose(p->dlh); + free(p->path); + free(p->args); + } + free(ps); +} + + +void +plugin_load(struct uct_pluginset *ps, char *path, char *args) +{ + ps->plugins = realloc(ps->plugins, ++ps->n_plugins * sizeof(ps->plugins[0])); + struct plugin *p = &ps->plugins[ps->n_plugins - 1]; + p->path = strdup(path); + p->args = args ? strdup(args) : args; + + p->dlh = dlopen(path, RTLD_NOW); + if (!p->dlh) { + fprintf(stderr, "Cannot load plugin %s: %s\n", path, dlerror()); + exit(EXIT_FAILURE); + } +#define loadsym(s_) do {\ + p->s_ = dlsym(p->dlh, "pachi_plugin_" #s_); \ + if (!p->s_) { \ + fprintf(stderr, "Cannot find pachi_plugin_%s in plugin %s: %s\n", #s_, path, dlerror()); \ + exit(EXIT_FAILURE); \ + } \ +} while (0) + loadsym(init); + loadsym(prior); + loadsym(done); + + p->data = p->init(p->args, ps->b, fast_random(65536)); +} + +void +plugin_prior(struct uct_pluginset *ps, struct tree_node *node, struct prior_map *map, int eqex) +{ + for (int i = 0; i < ps->n_plugins; i++) { + struct plugin *p = &ps->plugins[i]; + p->prior(p->data, node, map, eqex); + } +} + +#endif diff --git a/jni/pachi/uct/plugins.h b/jni/pachi/uct/plugins.h new file mode 100644 index 0000000..b559677 --- /dev/null +++ b/jni/pachi/uct/plugins.h @@ -0,0 +1,20 @@ +#ifndef PACHI_UCT_PLUGINS_H +#define PACHI_UCT_PLUGINS_H + +struct tree_node; +struct board; +struct prior_map; + + +/* The pluginset structure of current UCT context. */ +struct uct_pluginset; +struct uct_pluginset *pluginset_init(struct board *b); +void pluginset_done(struct uct_pluginset *ps); + +/* Load a new plugin with DLL at path, passed arguments in args. */ +void plugin_load(struct uct_pluginset *ps, char *path, char *args); + +/* Query plugins for priors of a node's leaves. */ +void plugin_prior(struct uct_pluginset *ps, struct tree_node *node, struct prior_map *map, int eqex); + +#endif diff --git a/jni/pachi/uct/policy/Makefile b/jni/pachi/uct/policy/Makefile new file mode 100644 index 0000000..845e48c --- /dev/null +++ b/jni/pachi/uct/policy/Makefile @@ -0,0 +1,12 @@ +INCLUDES=-I../.. +OBJS=generic.o ucb1.o ucb1amaf.o + +all: uctpolicy.a +uctpolicy.a: $(OBJS) + +clean: + rm -f *.o *.a +clean-profiled: + rm -f *.gcda *.gcno + +-include ../../Makefile.lib diff --git a/jni/pachi/uct/policy/generic.c b/jni/pachi/uct/policy/generic.c new file mode 100644 index 0000000..2e5df6f --- /dev/null +++ b/jni/pachi/uct/policy/generic.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "move.h" +#include "tactics/util.h" +#include "random.h" +#include "uct/internal.h" +#include "uct/tree.h" +#include "uct/policy/generic.h" + +struct tree_node * +uctp_generic_choose(struct uct_policy *p, struct tree_node *node, struct board *b, enum stone color, coord_t exclude) +{ + struct tree_node *nbest = node->children; + if (!nbest) return NULL; + struct tree_node *nbest2 = nbest->sibling; + + /* This function is called while the tree is updated by other threads. + * We rely on node->children being set only after the node has been fully expanded. */ + for (struct tree_node *ni = nbest2; ni; ni = ni->sibling) { + // we compare playouts and choose the best-explored + // child; comparing values is more brittle + if (node_coord(ni) == exclude || ni->hints & TREE_HINT_INVALID) + continue; + if (ni->u.playouts > nbest->u.playouts) { + nbest2 = nbest; + nbest = ni; + } else if (ni->u.playouts > nbest2->u.playouts) { + nbest2 = ni; + } + } + /* Play pass only if we can afford scoring. Call expensive uct_pass_is_safe() only if + * pass is indeed the best move. */ + if (is_pass(node_coord(nbest)) && !uct_pass_is_safe(p->uct, b, color, p->uct->pass_all_alive)) + return nbest2; + return nbest; +} + +/* Return the node with best value instead of best explored. We must use the heuristic + * value (using prior and possibly rave), because the raw value is meaningless for + * nodes evaluated rarely. + * This function is called while the tree is updated by other threads */ +void +uctp_generic_winner(struct uct_policy *p, struct tree *tree, struct uct_descent *descent) +{ + if (!p->evaluate) + return; + bool allow_pass = false; /* At worst forces some extra playouts at the end */ + int parity = tree_node_parity(tree, descent->node); + + uctd_try_node_children(tree, descent, allow_pass, parity, p->uct->tenuki_d, di, urgency) { + urgency = p->evaluate(p, tree, &di, parity); + } uctd_set_best_child(di, urgency); + + uctd_get_best_child(descent); +} diff --git a/jni/pachi/uct/policy/generic.h b/jni/pachi/uct/policy/generic.h new file mode 100644 index 0000000..e7a5c8b --- /dev/null +++ b/jni/pachi/uct/policy/generic.h @@ -0,0 +1,79 @@ +#ifndef PACHI_UCT_POLICY_GENERIC_H +#define PACHI_UCT_POLICY_GENERIC_H + +/* Some default policy routines and templates. */ + +#include "board.h" +#include "stone.h" +#include "uct/internal.h" + +struct board; +struct tree_node; + +struct tree_node *uctp_generic_choose(struct uct_policy *p, struct tree_node *node, struct board *b, enum stone color, coord_t exclude); +void uctp_generic_winner(struct uct_policy *p, struct tree *tree, struct uct_descent *descent); + + +/* Some generic stitching for tree descent. */ + +#if 0 +#define uctd_debug(fmt...) fprintf(stderr, fmt); +#else +#define uctd_debug(fmt...) +#endif + +#define uctd_try_node_children(tree, descent, allow_pass, parity, tenuki_d, di, urgency) \ + /* Information abound best children. */ \ + /* XXX: We assume board <=25x25. */ \ + struct uct_descent dbest[BOARD_MAX_MOVES + 1] = { { .node = descent->node->children, .lnode = NULL } }; int dbests = 1; \ + floating_t best_urgency = -9999; \ + /* Descent children iterator. */ \ + struct uct_descent dci = { .node = descent->node->children, .lnode = descent->lnode ? descent->lnode->children : NULL }; \ + \ + for (; dci.node; dci.node = dci.node->sibling) { \ + floating_t urgency; \ + /* Do not consider passing early. */ \ + if (unlikely((!allow_pass && is_pass(node_coord(dci.node))) || (dci.node->hints & TREE_HINT_INVALID))) \ + continue; \ + /* Position dci.lnode to point at or right after the local + * node corresponding to dci.node. */ \ + while (dci.lnode && node_coord(dci.lnode) < node_coord(dci.node)) \ + dci.lnode = dci.lnode->sibling; \ + /* Set up descent-further iterator. This is the public-accessible + * one, and usually is similar to dci. However, in case of local + * trees, we may keep next-candidate pointer in dci while storing + * actual-specimen in di. */ \ + struct uct_descent di = dci; \ + if (dci.lnode) { \ + /* Set lnode to local tree node corresponding + * to node (dci.lnode, pass-lnode or NULL). */ \ + di.lnode = tree_lnode_for_node(tree, dci.node, dci.lnode, tenuki_d); \ + } + + /* ...your urgency computation code goes here... */ + +#define uctd_set_best_child(di, urgency) \ + uctd_debug("(%s) %f\n", coord2sstr(node_coord(di.node), tree->board), urgency); \ + if (urgency - best_urgency > __FLT_EPSILON__) { /* urgency > best_urgency */ \ + uctd_debug("new best\n"); \ + best_urgency = urgency; dbests = 0; \ + } \ + if (urgency - best_urgency > -__FLT_EPSILON__) { /* urgency >= best_urgency */ \ + uctd_debug("another best\n"); \ + /* We want to always choose something else than a pass \ + * in case of a tie. pass causes degenerative behaviour. */ \ + if (dbests == 1 && is_pass(node_coord(dbest[0].node))) { \ + dbests--; \ + } \ + struct uct_descent db = di; \ + /* Make sure lnode information is meaningful. */ \ + if (db.lnode && is_pass(node_coord(db.lnode))) \ + db.lnode = NULL; \ + dbest[dbests++] = db; \ + } \ + } + +#define uctd_get_best_child(descent) *(descent) = dbest[fast_random(dbests)]; + + +#endif diff --git a/jni/pachi/uct/policy/ucb1.c b/jni/pachi/uct/policy/ucb1.c new file mode 100644 index 0000000..bd439ed --- /dev/null +++ b/jni/pachi/uct/policy/ucb1.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "move.h" +#include "random.h" +#include "uct/internal.h" +#include "uct/tree.h" +#include "uct/policy/generic.h" + +/* This implements the basic UCB1 policy. */ + +struct ucb1_policy { + /* This is what the Modification of UCT with Patterns in Monte Carlo Go + * paper calls 'p'. Original UCB has this on 2, but this seems to + * produce way too wide searches; reduce this to get deeper and + * narrower readouts - try 0.2. */ + floating_t explore_p; + /* First Play Urgency - if set to less than infinity (the MoGo paper + * above reports 1.0 as the best), new branches are explored only + * if none of the existing ones has higher urgency than fpu. */ + floating_t fpu; +}; + + +void +ucb1_descend(struct uct_policy *p, struct tree *tree, struct uct_descent *descent, int parity, bool allow_pass) +{ + /* We want to count in the prior stats here after all. Otherwise, + * nodes with positive prior will get explored _LESS_ since the + * urgency will be always higher; even with normal FPU because + * of the explore coefficient. */ + + struct ucb1_policy *b = p->data; + floating_t xpl = log(descent->node->u.playouts + descent->node->prior.playouts); + + uctd_try_node_children(tree, descent, allow_pass, parity, p->uct->tenuki_d, di, urgency) { + struct tree_node *ni = di.node; + int uct_playouts = ni->u.playouts + ni->prior.playouts; + + /* XXX: We don't take local-tree information into account. */ + + if (uct_playouts) { + urgency = (ni->u.playouts * tree_node_get_value(tree, parity, ni->u.value) + + ni->prior.playouts * tree_node_get_value(tree, parity, ni->prior.value)) + / uct_playouts; + urgency += b->explore_p * sqrt(xpl / uct_playouts); + } else { + urgency = b->fpu; + } + } uctd_set_best_child(di, urgency); + + uctd_get_best_child(descent); +} + +void +ucb1_update(struct uct_policy *p, struct tree *tree, struct tree_node *node, enum stone node_color, enum stone player_color, struct playout_amafmap *map, struct board *final_board, floating_t result) +{ + /* It is enough to iterate by a single chain; we will + * update all the preceding positions properly since + * they had to all occur in all branches, only in + * different order. */ + enum stone winner_color = result > 0.5 ? S_BLACK : S_WHITE; + for (; node; node = node->parent) { + stats_add_result(&node->u, result, 1); + if (!is_pass(node_coord(node))) { + stats_add_result(&node->winner_owner, board_at(final_board, node_coord(node)) == winner_color ? 1.0 : 0.0, 1); + stats_add_result(&node->black_owner, board_at(final_board, node_coord(node)) == S_BLACK ? 1.0 : 0.0, 1); + } + } +} + + +struct uct_policy * +policy_ucb1_init(struct uct *u, char *arg) +{ + struct uct_policy *p = calloc2(1, sizeof(*p)); + struct ucb1_policy *b = calloc2(1, sizeof(*b)); + p->uct = u; + p->data = b; + p->descend = ucb1_descend; + p->choose = uctp_generic_choose; + p->update = ucb1_update; + + b->explore_p = 0.2; + b->fpu = 1.1; //INFINITY; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "explore_p") && optval) { + b->explore_p = atof(optval); + } else if (!strcasecmp(optname, "fpu") && optval) { + b->fpu = atof(optval); + } else { + fprintf(stderr, "ucb1: Invalid policy argument %s or missing value\n", + optname); + exit(1); + } + } + } + + return p; +} diff --git a/jni/pachi/uct/policy/ucb1amaf.c b/jni/pachi/uct/policy/ucb1amaf.c new file mode 100644 index 0000000..c8630a7 --- /dev/null +++ b/jni/pachi/uct/policy/ucb1amaf.c @@ -0,0 +1,407 @@ +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "debug.h" +#include "move.h" +#include "random.h" +#include "tactics/util.h" +#include "uct/internal.h" +#include "uct/tree.h" +#include "uct/policy/generic.h" + +/* This implements the UCB1 policy with an extra AMAF heuristics. */ + +struct ucb1_policy_amaf { + /* This is what the Modification of UCT with Patterns in Monte Carlo Go + * paper calls 'p'. Original UCB has this on 2, but this seems to + * produce way too wide searches; reduce this to get deeper and + * narrower readouts - try 0.2. */ + floating_t explore_p; + /* In distributed mode, encourage different slaves to work on different + * parts of the tree by adding virtual wins to different nodes. */ + int virtual_win; + int root_virtual_win; + int vwin_min_playouts; + /* First Play Urgency - if set to less than infinity (the MoGo paper + * above reports 1.0 as the best), new branches are explored only + * if none of the existing ones has higher urgency than fpu. */ + floating_t fpu; + unsigned int equiv_rave; + bool sylvain_rave; + /* Give more weight to moves played earlier. */ + int distance_rave; + /* Give 0 or negative rave bonus to ko threats before taking the ko. + 1=normal bonus, 0=no bonus, -1=invert rave bonus, -2=double penalty... */ + int threat_rave; + /* Coefficient of local tree values embedded in RAVE. */ + floating_t ltree_rave; + /* Coefficient of criticality embedded in RAVE. */ + floating_t crit_rave; + int crit_min_playouts; + floating_t crit_plthres_coef; + bool crit_negative; + bool crit_negflip; + bool crit_amaf; + bool crit_lvalue; +}; + + +static inline floating_t fast_sqrt(unsigned int x) +{ + static const floating_t table[] = { + 0, 1, 1.41421356237309504880, 1.73205080756887729352, + 2.00000000000000000000, 2.23606797749978969640, + 2.44948974278317809819, 2.64575131106459059050, + 2.82842712474619009760, 3.00000000000000000000, + 3.16227766016837933199, 3.31662479035539984911, + 3.46410161513775458705, 3.60555127546398929311, + 3.74165738677394138558, 3.87298334620741688517, + 4.00000000000000000000, 4.12310562561766054982, + 4.24264068711928514640, 4.35889894354067355223, + 4.47213595499957939281, 4.58257569495584000658, + 4.69041575982342955456, 4.79583152331271954159, + 4.89897948556635619639, 5.00000000000000000000, + 5.09901951359278483002, 5.19615242270663188058, + 5.29150262212918118100, 5.38516480713450403125, + 5.47722557505166113456, 5.56776436283002192211, + 5.65685424949238019520, 5.74456264653802865985, + 5.83095189484530047087, 5.91607978309961604256, + 6.00000000000000000000, 6.08276253029821968899, + 6.16441400296897645025, 6.24499799839839820584, + 6.32455532033675866399, 6.40312423743284868648, + 6.48074069840786023096, 6.55743852430200065234, + 6.63324958071079969822, 6.70820393249936908922, + 6.78232998312526813906, 6.85565460040104412493, + 6.92820323027550917410, 7.00000000000000000000, + 7.07106781186547524400, 7.14142842854284999799, + 7.21110255092797858623, 7.28010988928051827109, + 7.34846922834953429459, 7.41619848709566294871, + 7.48331477354788277116, 7.54983443527074969723, + 7.61577310586390828566, 7.68114574786860817576, + 7.74596669241483377035, 7.81024967590665439412, + 7.87400787401181101968, 7.93725393319377177150, + }; + if (x < sizeof(table) / sizeof(*table)) { + return table[x]; + } else { + return sqrt(x); + } +} + +#define URAVE_DEBUG if (0) +static inline floating_t +ucb1rave_evaluate(struct uct_policy *p, struct tree *tree, struct uct_descent *descent, int parity) +{ + struct ucb1_policy_amaf *b = p->data; + struct tree_node *node = descent->node; + struct tree_node *lnode = descent->lnode; + + struct move_stats n = node->u, r = node->amaf; + if (p->uct->amaf_prior) { + stats_merge(&r, &node->prior); + } else { + stats_merge(&n, &node->prior); + } + + /* Local tree heuristics. */ + assert(!lnode || lnode->parent); + if (p->uct->local_tree && b->ltree_rave > 0 && lnode + && (p->uct->local_tree_rootchoose || lnode->parent->parent)) { + struct move_stats l = lnode->u; + l.playouts = ((floating_t) l.playouts) * b->ltree_rave / LTREE_PLAYOUTS_MULTIPLIER; + URAVE_DEBUG fprintf(stderr, "[ltree] adding [%s] %f%%%d to [%s] RAVE %f%%%d\n", + coord2sstr(node_coord(lnode), tree->board), l.value, l.playouts, + coord2sstr(node_coord(node), tree->board), r.value, r.playouts); + stats_merge(&r, &l); + } + + /* Criticality heuristics. */ + if (b->crit_rave > 0 && (b->crit_plthres_coef > 0 + ? node->u.playouts > tree->root->u.playouts * b->crit_plthres_coef + : node->u.playouts > b->crit_min_playouts)) { + floating_t crit = tree_node_criticality(tree, node); + if (b->crit_negative || crit > 0) { + floating_t val = 1.0f; + if (b->crit_negflip && crit < 0) { + val = 0; + crit = -crit; + } + struct move_stats c = { + .value = tree_node_get_value(tree, parity, val), + .playouts = crit * r.playouts * b->crit_rave + }; + URAVE_DEBUG fprintf(stderr, "[crit] adding %f%%%d to [%s] RAVE %f%%%d\n", + c.value, c.playouts, + coord2sstr(node_coord(node), tree->board), r.value, r.playouts); + stats_merge(&r, &c); + } + } + + + floating_t value = 0; + if (n.playouts) { + if (r.playouts) { + /* At the beginning, beta is at 1 and RAVE is used. + * At b->equiv_rate, beta is at 1/3 and gets steeper on. */ + floating_t beta; + if (b->sylvain_rave) { + beta = (floating_t) r.playouts / (r.playouts + n.playouts + + (floating_t) n.playouts * r.playouts / b->equiv_rave); + } else { + /* XXX: This can be cached in descend; but we don't use this by default. */ + beta = sqrt(b->equiv_rave / (3 * node->parent->u.playouts + b->equiv_rave)); + } + + value = beta * r.value + (1.f - beta) * n.value; + URAVE_DEBUG fprintf(stderr, "\t%s value = %f * %f + (1 - %f) * %f (prior %f)\n", + coord2sstr(node_coord(node), tree->board), beta, r.value, beta, n.value, node->prior.value); + } else { + value = n.value; + URAVE_DEBUG fprintf(stderr, "\t%s value = %f (prior %f)\n", + coord2sstr(node_coord(node), tree->board), n.value, node->prior.value); + } + } else if (r.playouts) { + value = r.value; + URAVE_DEBUG fprintf(stderr, "\t%s value = rave %f (prior %f)\n", + coord2sstr(node_coord(node), tree->board), r.value, node->prior.value); + } + descent->value.playouts = r.playouts + n.playouts; + descent->value.value = value; + return tree_node_get_value(tree, parity, value); +} + +void +ucb1rave_descend(struct uct_policy *p, struct tree *tree, struct uct_descent *descent, int parity, bool allow_pass) +{ + struct ucb1_policy_amaf *b = p->data; + floating_t nconf = 1.f; + if (b->explore_p > 0) + nconf = sqrt(log(descent->node->u.playouts + descent->node->prior.playouts)); + struct uct *u = p->uct; + int vwin = 0; + if (u->max_slaves > 0 && u->slave_index >= 0) + vwin = descent->node == tree->root ? b->root_virtual_win : b->virtual_win; + int child = 0; + + uctd_try_node_children(tree, descent, allow_pass, parity, u->tenuki_d, di, urgency) { + struct tree_node *ni = di.node; + urgency = ucb1rave_evaluate(p, tree, &di, parity); + + /* In distributed mode, encourage different slaves to work on different + * parts of the tree. We rely on the fact that children (if they exist) + * are the same and in the same order in all slaves. */ + if (vwin > 0 && ni->u.playouts > b->vwin_min_playouts && (child - u->slave_index) % u->max_slaves == 0) + urgency += vwin / (ni->u.playouts + vwin); + + if (ni->u.playouts > 0 && b->explore_p > 0) { + urgency += b->explore_p * nconf / fast_sqrt(ni->u.playouts); + + } else if (ni->u.playouts + ni->amaf.playouts + ni->prior.playouts == 0) { + /* assert(!u->even_eqex); */ + urgency = b->fpu; + } + } uctd_set_best_child(di, urgency); + + uctd_get_best_child(descent); +} + + +/* Return the length of the current ko (number of moves up to to the last ko capture), + * 0 if the sequence is empty or doesn't start with a ko capture. + * B captures a ko + * W plays a ko threat + * B answers ko threat + * W re-captures the ko <- return 4 + * B plays a ko threat + * W connects the ko */ +static inline int ko_length(bool *ko_capture_map, int map_length) +{ + if (map_length <= 0 || !ko_capture_map[0]) return 0; + int length = 1; + while (length + 2 < map_length && ko_capture_map[length + 2]) length += 3; + return length; +} + +void +ucb1amaf_update(struct uct_policy *p, struct tree *tree, struct tree_node *node, + enum stone node_color, enum stone player_color, + struct playout_amafmap *map, struct board *final_board, + floating_t result) +{ + struct ucb1_policy_amaf *b = p->data; + enum stone winner_color = result > 0.5 ? S_BLACK : S_WHITE; + + /* Record of the random playout - for each intersection coord, + * first_move[coord] is the index map->game of the first move + * at this coordinate, or INT_MAX if the move was not played. + * The parity gives the color of this move. + */ + int first_map[board_size2(final_board)+1]; + int *first_move = &first_map[1]; // +1 for pass + +#if 0 + struct board bb; bb.size = 9+2; + for (struct tree_node *ni = node; ni; ni = ni->parent) + fprintf(stderr, "%s ", coord2sstr(node_coord(ni), &bb)); + fprintf(stderr, "[color %d] update result %d (color %d)\n", + node_color, result, player_color); +#endif + + /* Initialize first_move */ + for (int i = pass; i < board_size2(final_board); i++) first_move[i] = INT_MAX; + int move; + assert(map->gamelen > 0); + for (move = map->gamelen - 1; move >= map->game_baselen; move--) + first_move[map->game[move]] = move; + + while (node) { + if (!b->crit_amaf && !is_pass(node_coord(node))) { + stats_add_result(&node->winner_owner, board_local_value(b->crit_lvalue, final_board, node_coord(node), winner_color), 1); + stats_add_result(&node->black_owner, board_local_value(b->crit_lvalue, final_board, node_coord(node), S_BLACK), 1); + } + stats_add_result(&node->u, result, 1); + + bool *ko_capture_map = &map->is_ko_capture[move+1]; + int max_threat_dist = b->threat_rave <= 0 ? ko_length(ko_capture_map, map->gamelen - (move+1)) : -1; + + /* This loop ignores symmetry considerations, but they should + * matter only at a point when AMAF doesn't help much. */ + assert(map->game_baselen >= 0); + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) { + if (is_pass(node_coord(ni))) continue; + + /* Use the child move only if it was first played by the same color. */ + int first = first_move[node_coord(ni)]; + if (first == INT_MAX) continue; + assert(first > move && first < map->gamelen); + int distance = first - (move + 1); + if (distance & 1) continue; + + int weight = 1; + floating_t res = result; + + /* Don't give amaf bonus to a ko threat before taking the ko. + * http://www.grappa.univ-lille3.fr/~coulom/Aja_PhD_Thesis.pdf + */ + if (distance <= max_threat_dist && distance % 6 == 4) { + weight = - b->threat_rave; + res = 1.0 - res; + } else if (b->distance_rave != 0) { + /* Give more weight to moves played earlier */ + weight += b->distance_rave * (map->gamelen - first) / (map->gamelen - move); + } + stats_add_result(&ni->amaf, res, weight); + + if (b->crit_amaf) { + stats_add_result(&ni->winner_owner, board_local_value(b->crit_lvalue, final_board, node_coord(ni), winner_color), 1); + stats_add_result(&ni->black_owner, board_local_value(b->crit_lvalue, final_board, node_coord(ni), S_BLACK), 1); + } +#if 0 + struct board bb; bb.size = 9+2; + fprintf(stderr, "* %s<%"PRIhash"> -> %s<%"PRIhash"> [%d/%f => %d/%f]\n", + coord2sstr(node_coord(node), &bb), node->hash, + coord2sstr(node_coord(ni), &bb), ni->hash, + player_color, result, move, res); +#endif + } + if (node->parent) { + assert(move >= 0 && map->game[move] == node_coord(node) && first_move[node_coord(node)] > move); + first_move[node_coord(node)] = move; + move--; + } + node = node->parent; + } +} + + +struct uct_policy * +policy_ucb1amaf_init(struct uct *u, char *arg, struct board *board) +{ + struct uct_policy *p = calloc2(1, sizeof(*p)); + struct ucb1_policy_amaf *b = calloc2(1, sizeof(*b)); + p->uct = u; + p->data = b; + p->choose = uctp_generic_choose; + p->winner = uctp_generic_winner; + p->evaluate = ucb1rave_evaluate; + p->descend = ucb1rave_descend; + p->update = ucb1amaf_update; + p->wants_amaf = true; + + b->explore_p = 0; + b->equiv_rave = board_large(board) ? 4000 : 3000; + b->fpu = INFINITY; + b->sylvain_rave = true; + b->distance_rave = 3; + b->threat_rave = 0; + b->ltree_rave = 0.75f; + + b->crit_rave = 1.1f; + b->crit_min_playouts = 2000; + b->crit_negative = 1; + b->crit_amaf = 0; + + b->virtual_win = 5; + b->root_virtual_win = 30; + b->vwin_min_playouts = 1000; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "explore_p")) { + b->explore_p = atof(optval); + } else if (!strcasecmp(optname, "fpu") && optval) { + b->fpu = atof(optval); + } else if (!strcasecmp(optname, "equiv_rave") && optval) { + b->equiv_rave = atof(optval); + } else if (!strcasecmp(optname, "sylvain_rave")) { + b->sylvain_rave = !optval || *optval == '1'; + } else if (!strcasecmp(optname, "distance_rave") && optval) { + b->distance_rave = atoi(optval); + } else if (!strcasecmp(optname, "threat_rave") && optval) { + b->threat_rave = atoi(optval); + } else if (!strcasecmp(optname, "ltree_rave") && optval) { + b->ltree_rave = atof(optval); + } else if (!strcasecmp(optname, "crit_rave") && optval) { + b->crit_rave = atof(optval); + } else if (!strcasecmp(optname, "crit_min_playouts") && optval) { + b->crit_min_playouts = atoi(optval); + } else if (!strcasecmp(optname, "crit_plthres_coef") && optval) { + b->crit_plthres_coef = atof(optval); + } else if (!strcasecmp(optname, "crit_negative")) { + b->crit_negative = !optval || *optval == '1'; + } else if (!strcasecmp(optname, "crit_negflip")) { + b->crit_negflip = !optval || *optval == '1'; + } else if (!strcasecmp(optname, "crit_amaf")) { + b->crit_amaf = !optval || *optval == '1'; + } else if (!strcasecmp(optname, "crit_lvalue")) { + b->crit_lvalue = !optval || *optval == '1'; + } else if (!strcasecmp(optname, "virtual_win") && optval) { + b->virtual_win = atoi(optval); + } else if (!strcasecmp(optname, "root_virtual_win") && optval) { + b->root_virtual_win = atoi(optval); + } else if (!strcasecmp(optname, "vwin_min_playouts") && optval) { + b->vwin_min_playouts = atoi(optval); + } else { + fprintf(stderr, "ucb1amaf: Invalid policy argument %s or missing value\n", + optname); + exit(1); + } + } + } + + return p; +} diff --git a/jni/pachi/uct/prior.c b/jni/pachi/uct/prior.c new file mode 100644 index 0000000..29c8c79 --- /dev/null +++ b/jni/pachi/uct/prior.c @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "joseki/base.h" +#include "move.h" +#include "random.h" +#include "tactics/ladder.h" +#include "tactics/util.h" +#include "uct/internal.h" +#include "uct/plugins.h" +#include "uct/prior.h" +#include "uct/tree.h" + +/* Applying heuristic values to the tree nodes, skewing the reading in + * most interesting directions. */ + +/* TODO: Introduce foreach_fpoint() to iterate only over non-occupied + * positions. */ + +struct uct_prior { + /* Equivalent experience for prior knowledge. MoGo paper recommends + * 50 playouts per source; in practice, esp. with RAVE, about 6 + * playouts per source seems best. */ + int eqex; + int even_eqex, policy_eqex, b19_eqex, eye_eqex, ko_eqex, plugin_eqex, joseki_eqex, pattern_eqex; + int cfgdn; int *cfgd_eqex; + bool prune_ladders; +}; + +void +uct_prior_even(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Q_{even} */ + /* This may be dubious for normal UCB1 but is essential for + * reading stability of RAVE, it appears. */ + add_prior_value(map, pass, 0.5, u->prior->even_eqex); + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + add_prior_value(map, c, 0.5, u->prior->even_eqex); + } foreach_free_point_end; +} + +void +uct_prior_eye(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Discourage playing into our own eyes. However, we cannot + * completely prohibit it: + * ####### + * ...XX.# + * XOOOXX# + * X.OOOO# + * .XXXX.# */ + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + if (!board_is_one_point_eye(map->b, c, map->to_play)) + continue; + add_prior_value(map, c, 0, u->prior->eye_eqex); + } foreach_free_point_end; +} + +void +uct_prior_ko(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Favor fighting ko, if we took it le 10 moves ago. */ + coord_t ko = map->b->last_ko.coord; + if (is_pass(ko) || map->b->moves - map->b->last_ko_age > 10 || !map->consider[ko]) + return; + // fprintf(stderr, "prior ko-fight @ %s %s\n", stone2str(map->to_play), coord2sstr(ko, map->b)); + add_prior_value(map, ko, 1, u->prior->ko_eqex); +} + +void +uct_prior_b19(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Q_{b19} */ + /* Specific hints for 19x19 board - priors for certain edge distances. */ + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + int d = coord_edge_distance(c, map->b); + if (d != 0 && d != 2) + continue; + /* The bonus applies only with no stones in immediate + * vincinity. */ + if (board_stone_radar(map->b, c, 2)) + continue; + /* First line: 0 */ + /* Third line: 1 */ + add_prior_value(map, c, d == 2, u->prior->b19_eqex); + } foreach_free_point_end; +} + +void +uct_prior_playout(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Q_{playout-policy} */ + if (u->playout->assess) + u->playout->assess(u->playout, map, u->prior->policy_eqex); +} + +void +uct_prior_cfgd(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Q_{common_fate_graph_distance} */ + /* Give bonus to moves local to the last move, where "local" means + * local in terms of groups, not just manhattan distance. */ + if (is_pass(map->b->last_move.coord) || is_resign(map->b->last_move.coord)) + return; + + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + if (map->distances[c] > u->prior->cfgdn) + continue; + assert(map->distances[c] != 0); + int bonus = u->prior->cfgd_eqex[map->distances[c]]; + add_prior_value(map, c, 1, bonus); + } foreach_free_point_end; +} + +void +uct_prior_joseki(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Q_{joseki} */ + if (!u->jdict) + return; + for (int i = 0; i < 4; i++) { + hash_t h = map->b->qhash[i] & joseki_hash_mask; + coord_t *cc = u->jdict->patterns[h].moves[map->to_play - 1]; + if (!cc) continue; + for (; !is_pass(*cc); cc++) { + if (coord_quadrant(*cc, map->b) != i) + continue; + add_prior_value(map, *cc, 1.0, u->prior->joseki_eqex); + } + } +} + +void +uct_prior_pattern(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + /* Q_{pattern} */ + if (!u->pat.pd) + return; + + struct board *b = map->b; + struct pattern pats[b->flen]; + floating_t probs[b->flen]; + pattern_rate_moves(&u->pat, b, map->to_play, pats, probs); + if (UDEBUGL(5)) { + fprintf(stderr, "Pattern prior at node %s\n", coord2sstr(node->coord, b)); + board_print(b, stderr); + } + + for (int f = 0; f < b->flen; f++) { + if (isnan(probs[f]) || probs[f] < 0.001) + continue; + assert(!is_pass(b->f[f])); + if (UDEBUGL(5)) { + char s[256]; pattern2str(s, &pats[f]); + fprintf(stderr, "\t%s: %.3f %s\n", coord2sstr(b->f[f], b), probs[f], s); + } + add_prior_value(map, b->f[f], 1.0, sqrt(probs[f]) * u->prior->pattern_eqex); + } +} + +void +uct_prior(struct uct *u, struct tree_node *node, struct prior_map *map) +{ + if (u->prior->prune_ladders && !board_playing_ko_threat(map->b)) { + foreach_free_point(map->b) { + if (!map->consider[c]) + continue; + group_t atari_neighbor = board_get_atari_neighbor(map->b, c, map->to_play); + if (atari_neighbor && is_ladder(map->b, c, atari_neighbor, true)) { + if (UDEBUGL(5)) + fprintf(stderr, "Pruning ladder move %s\n", coord2sstr(c, map->b)); + map->consider[c] = false; + } + } foreach_free_point_end; + } + + if (u->prior->even_eqex) + uct_prior_even(u, node, map); + if (u->prior->eye_eqex) + uct_prior_eye(u, node, map); + if (u->prior->ko_eqex) + uct_prior_ko(u, node, map); + if (u->prior->b19_eqex) + uct_prior_b19(u, node, map); + if (u->prior->policy_eqex) + uct_prior_playout(u, node, map); + if (u->prior->cfgd_eqex) + uct_prior_cfgd(u, node, map); + if (u->prior->joseki_eqex) + uct_prior_joseki(u, node, map); + if (u->prior->pattern_eqex) + uct_prior_pattern(u, node, map); + if (u->prior->plugin_eqex) + plugin_prior(u->plugins, node, map, u->prior->plugin_eqex); +} + +struct uct_prior * +uct_prior_init(char *arg, struct board *b, struct uct *u) +{ + struct uct_prior *p = calloc2(1, sizeof(struct uct_prior)); + + p->even_eqex = p->policy_eqex = p->b19_eqex = p->eye_eqex = p->ko_eqex = p->plugin_eqex = -100; + /* FIXME: Optimal pattern_eqex is about -1000 with small playout counts + * but only -400 on a cluster. We need a better way to set the default + * here. */ + p->pattern_eqex = -400; + p->joseki_eqex = -200; + p->cfgdn = -1; + + /* Even number! */ + p->eqex = board_large(b) ? 20 : 14; + + p->prune_ladders = true; + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ":"); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + if (!strcasecmp(optname, "eqex") && optval) { + p->eqex = atoi(optval); + + /* In the following settings, you can use negative + * numbers to give the hundredths of default eqex. + * E.g. -100 is default eqex, -50 is half of the + * default eqex, -200 is double the default eqex. */ + } else if (!strcasecmp(optname, "even") && optval) { + p->even_eqex = atoi(optval); + } else if (!strcasecmp(optname, "policy") && optval) { + p->policy_eqex = atoi(optval); + } else if (!strcasecmp(optname, "b19") && optval) { + p->b19_eqex = atoi(optval); + } else if (!strcasecmp(optname, "cfgd") && optval) { + /* cfgd=3%40%20%20 - 3 levels; immediate libs + * of last move => 40 wins, their neighbors + * 20 wins, 2nd-level neighbors 20 wins; + * neighbors are group-transitive. */ + p->cfgdn = atoi(optval); optval += strcspn(optval, "%"); + p->cfgd_eqex = calloc2(p->cfgdn + 1, sizeof(*p->cfgd_eqex)); + p->cfgd_eqex[0] = 0; + int i; + for (i = 1; *optval; i++, optval += strcspn(optval, "%")) { + optval++; + p->cfgd_eqex[i] = atoi(optval); + } + if (i != p->cfgdn + 1) { + fprintf(stderr, "uct: Missing prior cfdn level %d/%d\n", i, p->cfgdn); + exit(1); + } + + } else if (!strcasecmp(optname, "joseki") && optval) { + p->joseki_eqex = atoi(optval); + } else if (!strcasecmp(optname, "eye") && optval) { + p->eye_eqex = atoi(optval); + } else if (!strcasecmp(optname, "ko") && optval) { + p->ko_eqex = atoi(optval); + } else if (!strcasecmp(optname, "pattern") && optval) { + /* Pattern-based prior eqex. */ + /* Note that this prior is still going to be + * used only if you have downloaded or + * generated the pattern files! */ + p->pattern_eqex = atoi(optval); + } else if (!strcasecmp(optname, "plugin") && optval) { + /* Unlike others, this is just a *recommendation*. */ + p->plugin_eqex = atoi(optval); + } else if (!strcasecmp(optname, "prune_ladders")) { + p->prune_ladders = !optval || atoi(optval); + } else { + fprintf(stderr, "uct: Invalid prior argument %s or missing value\n", optname); + exit(1); + } + } + } + + if (p->even_eqex < 0) p->even_eqex = p->eqex * -p->even_eqex / 100; + if (p->policy_eqex < 0) p->policy_eqex = p->eqex * -p->policy_eqex / 100; + if (p->b19_eqex < 0) p->b19_eqex = p->eqex * -p->b19_eqex / 100; + if (p->eye_eqex < 0) p->eye_eqex = p->eqex * -p->eye_eqex / 100; + if (p->ko_eqex < 0) p->ko_eqex = p->eqex * -p->ko_eqex / 100; + if (p->joseki_eqex < 0) p->joseki_eqex = p->eqex * -p->joseki_eqex / 100; + if (p->pattern_eqex < 0) p->pattern_eqex = p->eqex * -p->pattern_eqex / 100; + if (p->plugin_eqex < 0) p->plugin_eqex = p->eqex * -p->plugin_eqex / 100; + + if (p->cfgdn < 0) { + static int large_bonuses[] = { 0, 55, 50, 15 }; + static int small_bonuses[] = { 0, 45, 40, 15 }; + p->cfgdn = 3; + p->cfgd_eqex = calloc2(p->cfgdn + 1, sizeof(*p->cfgd_eqex)); + memcpy(p->cfgd_eqex, board_large(b) ? large_bonuses : small_bonuses, sizeof(large_bonuses)); + } + if (p->cfgdn > TREE_NODE_D_MAX) { + fprintf(stderr, "uct: CFG distances only up to %d available\n", TREE_NODE_D_MAX); + exit(1); + } + + if (p->pattern_eqex) + u->want_pat = true; + + return p; +} + +void +uct_prior_done(struct uct_prior *p) +{ + assert(p->cfgd_eqex); + free(p->cfgd_eqex); + free(p); +} diff --git a/jni/pachi/uct/prior.h b/jni/pachi/uct/prior.h new file mode 100644 index 0000000..212c027 --- /dev/null +++ b/jni/pachi/uct/prior.h @@ -0,0 +1,46 @@ +#ifndef PACHI_UCT_PRIOR_H +#define PACHI_UCT_PRIOR_H + +#include "move.h" +#include "uct/tree.h" + +struct tree; +struct tree_node; +struct uct; +struct board; + +struct prior_map { + struct board *b; + enum stone to_play; + int parity; + /* [board_size2(b)] array, move_stats are the prior + * values to be assigned to individual moves; + * move_stats.value is not updated. */ + struct move_stats *prior; + /* [board_size2(b)] array, whether to compute + * prior for the given value. */ + bool *consider; + /* [board_size2(b)] array from cfg_distances() */ + int *distances; +}; + +/* @value is the value, @playouts is its weight. */ +static void add_prior_value(struct prior_map *map, coord_t c, floating_t value, int playouts); + +void uct_prior(struct uct *u, struct tree_node *node, struct prior_map *map); + +struct uct_prior; +struct uct_prior *uct_prior_init(char *arg, struct board *b, struct uct *u); +void uct_prior_done(struct uct_prior *p); + + +static inline void +add_prior_value(struct prior_map *map, coord_t c, floating_t value, int playouts) +{ + floating_t v = map->parity > 0 ? value : 1 - value; + /* We don't need atomicity: */ + struct move_stats s = { .playouts = playouts, .value = v }; + stats_merge(&map->prior[c], &s); +} + +#endif diff --git a/jni/pachi/uct/search.c b/jni/pachi/uct/search.c new file mode 100644 index 0000000..f29cd85 --- /dev/null +++ b/jni/pachi/uct/search.c @@ -0,0 +1,519 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#include "debug.h" +#include "distributed/distributed.h" +#include "move.h" +#include "random.h" +#include "timeinfo.h" +#include "uct/dynkomi.h" +#include "uct/internal.h" +#include "uct/search.h" +#include "uct/tree.h" +#include "uct/uct.h" +#include "uct/walk.h" + + +/* Default time settings for the UCT engine. In distributed mode, slaves are + * unlimited by default and all control is done on the master, either in time + * or with total number of playouts over all slaves. (It is also possible but + * not recommended to limit only the slaves; the master then decides the move + * when a majority of slaves have made their choice.) */ +static struct time_info default_ti; +static __attribute__((constructor)) void +default_ti_init(void) +{ + time_parse(&default_ti, "15"); +} + +static const struct time_info unlimited_ti = { + .period = TT_MOVE, + .dim = TD_GAMES, + .len = { .games = INT_MAX }, +}; + +/* When terminating UCT search early, the safety margin to add to the + * remaining playout number estimate when deciding whether the result can + * still change. */ +#define PLAYOUT_DELTA_SAFEMARGIN 1000 + +/* Minimal number of simulations to consider early break. */ +#define PLAYOUT_EARLY_BREAK_MIN 5000 + +/* Minimal time to consider early break (in seconds). */ +#define TIME_EARLY_BREAK_MIN 1.0 + + +/* Pachi threading structure: + * + * main thread + * | main(), GTP communication, ... + * | starts and stops the search managed by thread_manager + * | + * thread_manager + * | spawns and collects worker threads + * | + * worker0 + * worker1 + * ... + * workerK + * uct_playouts() loop, doing descend-playout until uct_halt + * + * Another way to look at it is by functions (lines denote thread boundaries): + * + * | uct_genmove() + * | uct_search() (uct_search_start() .. uct_search_stop()) + * | ----------------------- + * | spawn_thread_manager() + * | ----------------------- + * | spawn_worker() + * V uct_playouts() */ + +/* Set in thread manager in case the workers should stop. */ +volatile sig_atomic_t uct_halt = 0; +/* ID of the thread manager. */ +static pthread_t thread_manager; +bool thread_manager_running; + +static pthread_mutex_t finish_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t finish_cond = PTHREAD_COND_INITIALIZER; +static volatile int finish_thread; +static pthread_mutex_t finish_serializer = PTHREAD_MUTEX_INITIALIZER; + +static void * +spawn_worker(void *ctx_) +{ + struct uct_thread_ctx *ctx = ctx_; + /* Setup */ + fast_srandom(ctx->seed); + /* Run */ + ctx->games = uct_playouts(ctx->u, ctx->b, ctx->color, ctx->t, ctx->ti); + /* Finish */ + pthread_mutex_lock(&finish_serializer); + pthread_mutex_lock(&finish_mutex); + finish_thread = ctx->tid; + pthread_cond_signal(&finish_cond); + pthread_mutex_unlock(&finish_mutex); + return ctx; +} + +/* Thread manager, controlling worker threads. It must be called with + * finish_mutex lock held, but it will unlock it itself before exiting; + * this is necessary to be completely deadlock-free. */ +/* The finish_cond can be signalled for it to stop; in that case, + * the caller should set finish_thread = -1. */ +/* After it is started, it will update mctx->t to point at some tree + * used for the actual search, on return + * it will set mctx->games to the number of performed simulations. */ +static void * +spawn_thread_manager(void *ctx_) +{ + /* In thread_manager, we use only some of the ctx fields. */ + struct uct_thread_ctx *mctx = ctx_; + struct uct *u = mctx->u; + struct tree *t = mctx->t; + fast_srandom(mctx->seed); + + int played_games = 0; + pthread_t threads[u->threads]; + int joined = 0; + + uct_halt = 0; + + /* Garbage collect the tree by preference when pondering. */ + if (u->pondering && t->nodes && t->nodes_size >= t->pruning_threshold) { + t->root = tree_garbage_collect(t, t->root); + } + + /* Spawn threads... */ + for (int ti = 0; ti < u->threads; ti++) { + struct uct_thread_ctx *ctx = malloc2(sizeof(*ctx)); + ctx->u = u; ctx->b = mctx->b; ctx->color = mctx->color; + mctx->t = ctx->t = t; + ctx->tid = ti; ctx->seed = fast_random(65536) + ti; + ctx->ti = mctx->ti; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setstacksize(&a, 1048576); + pthread_create(&threads[ti], &a, spawn_worker, ctx); + if (UDEBUGL(3)) + fprintf(stderr, "Spawned worker %d\n", ti); + } + + /* ...and collect them back: */ + while (joined < u->threads) { + /* Wait for some thread to finish... */ + pthread_cond_wait(&finish_cond, &finish_mutex); + if (finish_thread < 0) { + /* Stop-by-caller. Tell the workers to wrap up + * and unblock them from terminating. */ + uct_halt = 1; + /* We need to make sure the workers do not complete + * the termination sequence before we get officially + * stopped - their wake and the stop wake could get + * coalesced. */ + pthread_mutex_unlock(&finish_serializer); + continue; + } + /* ...and gather its remnants. */ + struct uct_thread_ctx *ctx; + pthread_join(threads[finish_thread], (void **) &ctx); + played_games += ctx->games; + joined++; + free(ctx); + if (UDEBUGL(3)) + fprintf(stderr, "Joined worker %d\n", finish_thread); + pthread_mutex_unlock(&finish_serializer); + } + + pthread_mutex_unlock(&finish_mutex); + + mctx->games = played_games; + return mctx; +} + + +/*** THREAD MANAGER end */ + +/*** Search infrastructure: */ + + +int +uct_search_games(struct uct_search_state *s) +{ + return s->ctx->t->root->u.playouts; +} + +void +uct_search_start(struct uct *u, struct board *b, enum stone color, + struct tree *t, struct time_info *ti, + struct uct_search_state *s) +{ + /* Set up search state. */ + s->base_playouts = s->last_dynkomi = s->last_print = t->root->u.playouts; + s->print_interval = u->reportfreq * u->threads; + s->fullmem = false; + + if (ti) { + if (ti->period == TT_NULL) { + if (u->slave) { + *ti = unlimited_ti; + } else { + *ti = default_ti; + time_start_timer(ti); + } + } + time_stop_conditions(ti, b, u->fuseki_end, u->yose_start, u->max_maintime_ratio, &s->stop); + } + + /* Fire up the tree search thread manager, which will in turn + * spawn the searching threads. */ + assert(u->threads > 0); + assert(!thread_manager_running); + static struct uct_thread_ctx mctx; + mctx = (struct uct_thread_ctx) { .u = u, .b = b, .color = color, .t = t, .seed = fast_random(65536), .ti = ti }; + s->ctx = &mctx; + pthread_mutex_lock(&finish_serializer); + pthread_mutex_lock(&finish_mutex); + pthread_create(&thread_manager, NULL, spawn_thread_manager, s->ctx); + thread_manager_running = true; +} + +struct uct_thread_ctx * +uct_search_stop(void) +{ + assert(thread_manager_running); + + /* Signal thread manager to stop the workers. */ + pthread_mutex_lock(&finish_mutex); + finish_thread = -1; + pthread_cond_signal(&finish_cond); + pthread_mutex_unlock(&finish_mutex); + + /* Collect the thread manager. */ + struct uct_thread_ctx *pctx; + thread_manager_running = false; + pthread_join(thread_manager, (void **) &pctx); + return pctx; +} + + +void +uct_search_progress(struct uct *u, struct board *b, enum stone color, + struct tree *t, struct time_info *ti, + struct uct_search_state *s, int i) +{ + struct uct_thread_ctx *ctx = s->ctx; + + /* Adjust dynkomi? */ + int di = u->dynkomi_interval * u->threads; + if (ctx->t->use_extra_komi && u->dynkomi->permove + && !u->pondering && di + && i > s->last_dynkomi + di) { + s->last_dynkomi += di; + floating_t old_dynkomi = ctx->t->extra_komi; + ctx->t->extra_komi = u->dynkomi->permove(u->dynkomi, b, ctx->t); + if (UDEBUGL(3) && old_dynkomi != ctx->t->extra_komi) + fprintf(stderr, "dynkomi adjusted (%f -> %f)\n", + old_dynkomi, ctx->t->extra_komi); + } + + /* Print progress? */ + if (i - s->last_print > s->print_interval) { + s->last_print += s->print_interval; // keep the numbers tidy + uct_progress_status(u, ctx->t, color, s->last_print, NULL); + } + + if (!s->fullmem && ctx->t->nodes_size > u->max_tree_size) { + if (UDEBUGL(2)) + fprintf(stderr, "memory limit hit (%lu > %lu)\n", + ctx->t->nodes_size, u->max_tree_size); + s->fullmem = true; + } +} + + +/* Determine whether we should terminate the search early. */ +static bool +uct_search_stop_early(struct uct *u, struct tree *t, struct board *b, + struct time_info *ti, struct time_stop *stop, + struct tree_node *best, struct tree_node *best2, + int played, bool fullmem) +{ + /* If the memory is full, stop immediately. Since the tree + * cannot grow anymore, some non-well-expanded nodes will + * quickly take over with extremely high ratio since the + * counters are not properly simulated (just as if we use + * non-UCT MonteCarlo). */ + /* (XXX: A proper solution would be to prune the tree + * on the spot.) */ + if (fullmem) + return true; + + /* Think at least 100ms to avoid a random move. This is particularly + * important in distributed mode, where this function is called frequently. */ + double elapsed = 0.0; + if (ti->dim == TD_WALLTIME) { + elapsed = time_now() - ti->len.t.timer_start; + if (elapsed < TREE_BUSYWAIT_INTERVAL) return false; + } + + /* Break early if we estimate the second-best move cannot + * catch up in assigned time anymore. We use all our time + * if we are in byoyomi with single stone remaining in our + * period, however - it's better to pre-ponder. */ + bool time_indulgent = (!ti->len.t.main_time && ti->len.t.byoyomi_stones == 1); + if (best2 && ti->dim == TD_WALLTIME + && played >= PLAYOUT_EARLY_BREAK_MIN && !time_indulgent) { + double remaining = stop->worst.time - elapsed; + double pps = ((double)played) / elapsed; + double estplayouts = remaining * pps + PLAYOUT_DELTA_SAFEMARGIN; + if (best->u.playouts > best2->u.playouts + estplayouts) { + if (UDEBUGL(2)) + fprintf(stderr, "Early stop, result cannot change: " + "best %d, best2 %d, estimated %f simulations to go (%d/%f=%f pps)\n", + best->u.playouts, best2->u.playouts, estplayouts, played, elapsed, pps); + return true; + } + } + + /* Early break in won situation. */ + if (best->u.playouts >= PLAYOUT_EARLY_BREAK_MIN + && (ti->dim != TD_WALLTIME || elapsed > TIME_EARLY_BREAK_MIN) + && tree_node_get_value(t, 1, best->u.value) >= u->sure_win_threshold) { + return true; + } + + return false; +} + +/* Determine whether we should terminate the search later than expected. */ +static bool +uct_search_keep_looking(struct uct *u, struct tree *t, struct board *b, + struct time_info *ti, struct time_stop *stop, + struct tree_node *best, struct tree_node *best2, + struct tree_node *bestr, struct tree_node *winner, int i) +{ + if (!best) { + if (UDEBUGL(2)) + fprintf(stderr, "Did not find best move, still trying...\n"); + return true; + } + + /* Do not waste time if we are winning. Spend up to worst time if + * we are unsure, but only desired time if we are sure of winning. */ + floating_t beta = 2 * (tree_node_get_value(t, 1, best->u.value) - 0.5); + if (ti->dim == TD_WALLTIME && beta > 0) { + double good_enough = stop->desired.time * beta + stop->worst.time * (1 - beta); + double elapsed = time_now() - ti->len.t.timer_start; + if (elapsed > good_enough) return false; + } + + if (u->best2_ratio > 0) { + /* Check best/best2 simulations ratio. If the + * two best moves give very similar results, + * keep simulating. */ + if (best2 && best2->u.playouts + && (double)best->u.playouts / best2->u.playouts < u->best2_ratio) { + if (UDEBUGL(2)) + fprintf(stderr, "Best2 ratio %f < threshold %f\n", + (double)best->u.playouts / best2->u.playouts, + u->best2_ratio); + return true; + } + } + + if (u->bestr_ratio > 0) { + /* Check best, best_best value difference. If the best move + * and its best child do not give similar enough results, + * keep simulating. */ + if (bestr && bestr->u.playouts + && fabs((double)best->u.value - bestr->u.value) > u->bestr_ratio) { + if (UDEBUGL(2)) + fprintf(stderr, "Bestr delta %f > threshold %f\n", + fabs((double)best->u.value - bestr->u.value), + u->bestr_ratio); + return true; + } + } + + if (winner && winner != best) { + /* Keep simulating if best explored + * does not have also highest value. */ + if (UDEBUGL(2)) + fprintf(stderr, "[%d] best %3s [%d] %f != winner %3s [%d] %f\n", i, + coord2sstr(node_coord(best), t->board), + best->u.playouts, tree_node_get_value(t, 1, best->u.value), + coord2sstr(node_coord(winner), t->board), + winner->u.playouts, tree_node_get_value(t, 1, winner->u.value)); + return true; + } + + /* No reason to keep simulating, bye. */ + return false; +} + +bool +uct_search_check_stop(struct uct *u, struct board *b, enum stone color, + struct tree *t, struct time_info *ti, + struct uct_search_state *s, int i) +{ + struct uct_thread_ctx *ctx = s->ctx; + + /* Never consider stopping if we played too few simulations. + * Maybe we risk losing on time when playing in super-extreme + * time pressure but the tree is going to be just too messed + * up otherwise - we might even play invalid suicides or pass + * when we mustn't. */ + assert(!(ti->dim == TD_GAMES && ti->len.games < GJ_MINGAMES)); + if (i < GJ_MINGAMES) + return false; + + struct tree_node *best = NULL; + struct tree_node *best2 = NULL; // Second-best move. + struct tree_node *bestr = NULL; // best's best child. + struct tree_node *winner = NULL; + + best = u->policy->choose(u->policy, ctx->t->root, b, color, resign); + if (best) best2 = u->policy->choose(u->policy, ctx->t->root, b, color, node_coord(best)); + + /* Possibly stop search early if it's no use to try on. */ + int played = u->played_all + i - s->base_playouts; + if (best && uct_search_stop_early(u, ctx->t, b, ti, &s->stop, best, best2, played, s->fullmem)) + return true; + + /* Check against time settings. */ + bool desired_done; + if (ti->dim == TD_WALLTIME) { + double elapsed = time_now() - ti->len.t.timer_start; + if (elapsed > s->stop.worst.time) return true; + desired_done = elapsed > s->stop.desired.time; + + } else { assert(ti->dim == TD_GAMES); + if (i > s->stop.worst.playouts) return true; + desired_done = i > s->stop.desired.playouts; + } + + /* We want to stop simulating, but are willing to keep trying + * if we aren't completely sure about the winner yet. */ + if (desired_done) { + if (u->policy->winner && u->policy->evaluate) { + struct uct_descent descent = { .node = ctx->t->root }; + u->policy->winner(u->policy, ctx->t, &descent); + winner = descent.node; + } + if (best) + bestr = u->policy->choose(u->policy, best, b, stone_other(color), resign); + if (!uct_search_keep_looking(u, ctx->t, b, ti, &s->stop, best, best2, bestr, winner, i)) + return true; + } + + /* TODO: Early break if best->variance goes under threshold + * and we already have enough playouts (possibly thanks to tbook + * or to pondering)? */ + return false; +} + + +struct tree_node * +uct_search_result(struct uct *u, struct board *b, enum stone color, + bool pass_all_alive, int played_games, int base_playouts, + coord_t *best_coord) +{ + /* Choose the best move from the tree. */ + struct tree_node *best = u->policy->choose(u->policy, u->t->root, b, color, resign); + if (!best) { + *best_coord = pass; + return NULL; + } + *best_coord = node_coord(best); + if (UDEBUGL(1)) + fprintf(stderr, "*** WINNER is %s (%d,%d) with score %1.4f (%d/%d:%d/%d games), extra komi %f\n", + coord2sstr(node_coord(best), b), coord_x(node_coord(best), b), coord_y(node_coord(best), b), + tree_node_get_value(u->t, 1, best->u.value), best->u.playouts, + u->t->root->u.playouts, u->t->root->u.playouts - base_playouts, played_games, + u->t->extra_komi); + + /* Do not resign if we're so short of time that evaluation of best + * move is completely unreliable, we might be winning actually. + * In this case best is almost random but still better than resign. + * Also do not resign if we are getting bad results while actually + * giving away extra komi points (dynkomi). */ + if (tree_node_get_value(u->t, 1, best->u.value) < u->resign_threshold + && !is_pass(node_coord(best)) && best->u.playouts > GJ_MINGAMES + && (!u->t->use_extra_komi || komi_by_color(u->t->extra_komi, color) < 0.5)) { + *best_coord = resign; + return NULL; + } + + /* If the opponent just passed and we win counting, always + * pass as well. For option stones_only, we pass only when there + * there is nothing else to do, to show how to maximize score. */ + if (b->moves > 1 && is_pass(b->last_move.coord) && b->rules != RULES_STONES_ONLY) { + if (uct_pass_is_safe(u, b, color, pass_all_alive)) { + if (UDEBUGL(0)) + fprintf(stderr, "\n", + board_official_score(b, NULL) / 2); + *best_coord = pass; + best = u->t->root->children; // pass is the first child + assert(is_pass(node_coord(best))); + return best; + } else { + if (UDEBUGL(3)) + fprintf(stderr, "Refusing to pass, unsafe; pass_all_alive %d, ownermap #playouts %d, raw score %f\n", + pass_all_alive, u->ownermap.playouts, + board_official_score(b, NULL) / 2); + } + } + + return best; +} diff --git a/jni/pachi/uct/search.h b/jni/pachi/uct/search.h new file mode 100644 index 0000000..6ee8178 --- /dev/null +++ b/jni/pachi/uct/search.h @@ -0,0 +1,77 @@ +#ifndef PACHI_UCT_SEARCH_H +#define PACHI_UCT_SEARCH_H + +/* MCTS Search infrastructure. We juggle the search threads and + * control search duration. */ + +/* uct.c provides the GTP interface and engine setup. */ +/* walk.c controls repeated walking of the MCTS tree within + * the search threads. */ + +#include // sig_atomic_t + +#include "debug.h" +#include "move.h" +#include "ownermap.h" +#include "playout.h" +#include "timeinfo.h" +#include "uct/internal.h" + +struct tree; +struct tree_node; + +/* Internal UCT structures */ + +/* How often to inspect the tree from the main thread to check for playout + * stop, progress reports, etc. (in seconds) */ +#define TREE_BUSYWAIT_INTERVAL 0.1 /* 100ms */ + + +/* Thread manager state */ +extern volatile sig_atomic_t uct_halt; +extern bool thread_manager_running; + +/* Search thread context */ +struct uct_thread_ctx { + int tid; + struct uct *u; + struct board *b; + enum stone color; + struct tree *t; + unsigned long seed; + int games; + struct time_info *ti; +}; + + +/* Progress information of the on-going MCTS search - when did we + * last adjusted dynkomi, printed out stuff, etc. */ +struct uct_search_state { + /* Number of games simulated for this simulation before + * we started the search. (We have simulated them earlier.) */ + int base_playouts; + /* Number of last dynkomi adjustment. */ + int last_dynkomi; + /* Number of last game with progress print. */ + int last_print; + /* Number of simulations to wait before next print. */ + int print_interval; + /* Printed notification about full memory? */ + bool fullmem; + + struct time_stop stop; + struct uct_thread_ctx *ctx; +}; + +int uct_search_games(struct uct_search_state *s); + +void uct_search_start(struct uct *u, struct board *b, enum stone color, struct tree *t, struct time_info *ti, struct uct_search_state *s); +struct uct_thread_ctx *uct_search_stop(void); + +void uct_search_progress(struct uct *u, struct board *b, enum stone color, struct tree *t, struct time_info *ti, struct uct_search_state *s, int i); + +bool uct_search_check_stop(struct uct *u, struct board *b, enum stone color, struct tree *t, struct time_info *ti, struct uct_search_state *s, int i); + +struct tree_node *uct_search_result(struct uct *u, struct board *b, enum stone color, bool pass_all_alive, int played_games, int base_playouts, coord_t *best_coord); + +#endif diff --git a/jni/pachi/uct/slave.c b/jni/pachi/uct/slave.c new file mode 100644 index 0000000..023a0ee --- /dev/null +++ b/jni/pachi/uct/slave.c @@ -0,0 +1,554 @@ +/* This is the slave specific part of the distributed engine. + * See introduction at top of distributed/distributed.c. + * The slave maintains a hash table of nodes received from the + * master. When receiving stats the hash table gives a pointer to the + * tree node to update. When sending stats we remember in the tree + * what was previously sent so that only the incremental part has to + * be sent. The incremental part is smaller and can be compressed. + * The compression is not yet done in this version. */ + +/* Similarly the master only sends stats increments. + * They include only contributions from other slaves. */ + +/* The keys for the hash table are coordinate paths from + * a root child to a given node. See distributed/distributed.h + * for the encoding of a path to a 64 bit integer. */ + +/* To allow the master to select the best move, slaves also send + * absolute playout counts for the best top level nodes (children + * of the root node), including contributions from other slaves. */ + +/* Pass me arguments like a=b,c=d,... + * Slave specific arguments (see uct.c for the other uct arguments + * and distributed.c for the port arguments) : + * slave required to indicate slave mode + * max_nodes=MAX_NODES default 80K + * stats_hbits=STATS_HBITS default 24. 2^stats_bits = hash table size + */ + +#include +#include +#include +#include +#include + +#define MAX_VERBOSE_LOGS 1000 +#define DEBUG + +#include "debug.h" +#include "board.h" +#include "fbook.h" +#include "gtp.h" +#include "move.h" +#include "timeinfo.h" +#include "uct/internal.h" +#include "uct/search.h" +#include "uct/slave.h" +#include "uct/tree.h" + + +/* UCT infrastructure for a distributed engine slave. */ + +/* For debugging only. */ +static struct hash_counts h_counts; +static long parent_not_found = 0; +static long parent_leaf = 0; +static long node_not_found = 0; + +/* Hash table entry mapping path to node. */ +struct tree_hash { + path_t coord_path; + struct tree_node *node; +}; + +void * +uct_htable_alloc(int hbits) +{ + return calloc2(1 << hbits, sizeof(struct tree_hash)); +} + +/* Clear the hash table. Used only when running as slave for the distributed engine. */ +void uct_htable_reset(struct tree *t) +{ + if (!t->htable) return; + double start = time_now(); + memset(t->htable, 0, (1 << t->hbits) * sizeof(t->htable[0])); + if (DEBUGL(3)) + fprintf(stderr, "tree occupied %ld %.1f%% inserts %ld collisions %ld/%ld %.1f%% clear %.3fms\n" + "parent_not_found %.1f%% parent_leaf %.1f%% node_not_found %.1f%%\n", + h_counts.occupied, h_counts.occupied * 100.0 / (1 << t->hbits), + h_counts.inserts, h_counts.collisions, h_counts.lookups, + h_counts.collisions * 100.0 / (h_counts.lookups + 1), + (time_now() - start)*1000, + parent_not_found * 100.0 / (h_counts.lookups + 1), + parent_leaf * 100.0 / (h_counts.lookups + 1), + node_not_found * 100.0 / (h_counts.lookups + 1)); + if (DEBUG_MODE) h_counts.occupied = 0; +} + +/* Find a node given its coord path from root. Insert it in the + * hash table if it is not already there. + * Return the tree node, or NULL if the node cannot be found. + * The tree is modified in background while this function is running. + * prev is only used to optimize the tree search, given that calls to + * tree_find_node are made with sorted coordinates (increasing levels + * and increasing coord within a level). */ +static struct tree_node * +tree_find_node(struct tree *t, struct incr_stats *is, struct tree_node *prev) +{ + assert(t && t->htable); + path_t path = is->coord_path; + /* pass and resign must never be inserted in the hash table. */ + assert(path > 0); + + int hash, parent_hash; + bool found; + find_hash(hash, t->htable, t->hbits, path, found, h_counts); + struct tree_hash *hnode = &t->htable[hash]; + + if (DEBUGVV(7)) + fprintf(stderr, + "find_node %"PRIpath" %s found %d hash %d playouts %d node %p\n", path, + path2sstr(path, t->board), found, hash, is->incr.playouts, hnode->node); + + if (found) return hnode->node; + + /* The master sends parents before children so the parent should + * already be in the hash table. */ + path_t parent_p = parent_path(path, t->board); + struct tree_node *parent; + if (parent_p) { + find_hash(parent_hash, t->htable, t->hbits, + parent_p, found, h_counts); + parent = t->htable[parent_hash].node; + } else { + parent = t->root; + } + struct tree_node *node = NULL; + if (parent) { + /* Search for the node in parent's children. */ + coord_t leaf = leaf_coord(path, t->board); + node = (prev && prev->parent == parent ? prev->sibling : parent->children); + while (node && node_coord(node) != leaf) node = node->sibling; + + if (DEBUG_MODE) parent_leaf += !parent->is_expanded; + } else { + if (DEBUG_MODE) parent_not_found++; + if (DEBUGVV(7)) + fprintf(stderr, "parent of %"PRIpath" %s not found\n", + path, path2sstr(path, t->board)); + } + + /* Insert the node in the hash table. */ + hnode->node = node; + if (DEBUG_MODE) h_counts.inserts++, h_counts.occupied++; + if (DEBUGVV(7)) + fprintf(stderr, "insert path %"PRIpath" %s hash %d playouts %d node %p\n", + path, path2sstr(path, t->board), hash, is->incr.playouts, node); + + if (DEBUG_MODE && !node) node_not_found++; + + hnode->coord_path = path; + return node; +} + + +/* Read and discard any binary arguments. The number of + * bytes to be skipped is given by @size in the command. */ +static void +discard_bin_args(char *args) +{ + char *s = strchr(args, '@'); + int size = 0; + if (s) size = atoi(s+1); + while (size) { + char buf[64*1024]; + int len = sizeof(buf); + if (len > size) len = size; + len = fread(buf, 1, len, stdin); + if (len <= 0) break; + size -= len; + } +} + +enum parse_code +uct_notify(struct engine *e, struct board *b, int id, char *cmd, char *args, char **reply) +{ + struct uct *u = e->data; + + static bool board_resized = false; + if (is_gamestart(cmd)) { + board_resized = true; + uct_pondering_stop(u); + } + + /* Force resending the whole command history if we are out of sync + * but do it only once, not if already getting the history. */ + if ((move_number(id) != b->moves || !board_resized) + && !reply_disabled(id) && !is_reset(cmd)) { + static char buf[128]; + snprintf(buf, sizeof(buf), "Out of sync, %d %s, move %d expected", id, cmd, b->moves); + if (UDEBUGL(0)) + fprintf(stderr, "%s\n", buf); + discard_bin_args(args); + + *reply = buf; + /* Let gtp_parse() complain about invalid commands. */ + if (!gtp_is_valid(cmd) && !is_repeated(cmd)) return P_OK; + return P_DONE_ERROR; + } + return reply_disabled(id) ? P_NOREPLY : P_OK; +} + + +/* Read the move stats sent by the master, as a binary array of + * incr_stats structs. The stats come sorted by increasing coord path. + * To simplify the code, we assume that master and slave have the same + * architecture (store values identically). + * Keep this code in sync with distributed/merge.c:output_stats() + * Return true if ok, false if error. */ +static bool +receive_stats(struct uct *u, int size) +{ + if (size % sizeof(struct incr_stats)) return false; + int nodes = size / sizeof(struct incr_stats); + if (nodes > (1 << u->stats_hbits)) return false; + + struct tree *t = u->t; + assert(nodes && t->htable); + struct tree_node *prev = NULL; + double start_time = time_now(); + + for (int n = 0; n < nodes; n++) { + struct incr_stats is; + if (fread(&is, sizeof(struct incr_stats), 1, stdin) != 1) + return false; + + if (UDEBUGL(7)) + fprintf(stderr, "read %5d/%d %6d %.3f %"PRIpath" %s\n", n, nodes, + is.incr.playouts, is.incr.value, is.coord_path, + path2sstr(is.coord_path, t->board)); + + struct tree_node *node = tree_find_node(t, &is, prev); + if (!node) continue; + + /* node_total += others_incr */ + stats_add_result(&node->u, is.incr.value, is.incr.playouts); + + /* last_total += others_incr */ + stats_add_result(&node->pu, is.incr.value, is.incr.playouts); + + prev = node; + } + if (DEBUGVV(2)) + fprintf(stderr, "read args for %d nodes in %.4fms\n", nodes, + (time_now() - start_time)*1000); + return true; +} + +/* A tree traversal fills this array, then the nodes with most increments are sent. */ +struct stats_candidate { + path_t coord_path; + int playout_incr; + struct tree_node *node; +}; + +/* We maintain counts per bucket to avoid sorting stats_queue. + * All nodes with n updates since last send go to bucket n. + * If we put all nodes above 1023 updates in the top bucket, + * we get at most 27 nodes in this bucket. So we can select + * exactly the best shared_nodes nodes if shared_nodes >= 27. */ +#define MAX_BUCKETS 1024 +static int bucket_count[MAX_BUCKETS]; + +/* Traverse the tree rooted at node, and append incremental stats + * for children to stats_queue. start_path is the coordinate path + * for the top node. Stats for a node are only appended if enough playouts + * have been made since the last send, and the level is not too deep. + * Return the updated stats count. */ +static int +append_stats(struct stats_candidate *stats_queue, struct tree_node *node, int stats_count, + int max_count, path_t start_path, path_t max_path, int min_increment, struct board *b) +{ + /* The children field is set only after all children are created + * so we can traverse the the tree while it is updated. */ + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) { + + if (is_pass(node_coord(ni))) continue; + if (ni->hints & TREE_HINT_INVALID) continue; + + int incr = ni->u.playouts - ni->pu.playouts; + if (incr < min_increment) continue; + + /* min_increment should be tuned to avoid overflow. */ + if (stats_count >= max_count) { + if (DEBUGL(0)) + fprintf(stderr, "*** stats overflow %d nodes\n", stats_count); + return stats_count; + } + path_t child_path = append_child(start_path, node_coord(ni), b); + stats_queue[stats_count].playout_incr = incr; + stats_queue[stats_count].coord_path = child_path; + stats_queue[stats_count++].node = ni; + + if (incr >= MAX_BUCKETS) incr = MAX_BUCKETS - 1; + bucket_count[incr]++; + + /* Do not recurse if level deep enough. */ + if (child_path >= max_path) continue; + + stats_count = append_stats(stats_queue, ni, stats_count, max_count, + child_path, max_path, min_increment, b); + } + return stats_count; +} + +/* Used to sort by coord path the incremental stats to be sent. */ +static int +coord_cmp(const void *p1, const void *p2) +{ + path_t diff = ((struct incr_stats *)p1)->coord_path + - ((struct incr_stats *)p2)->coord_path; + return (int)(diff >> 32) | !!(int)diff; +} + +/* Select from stats_queue at most shared_nodes candidates with + * biggest increments. Return a binary array sorted by coord path. */ +static struct incr_stats * +select_best_stats(struct stats_candidate *stats_queue, int stats_count, + int shared_nodes, int *byte_size) +{ + static struct incr_stats *out_stats = NULL; + if (!out_stats) + out_stats = malloc2(shared_nodes * sizeof(*out_stats)); + + /* Find the minimum increment to send. The bucket with minimum + * increment may be sent only partially. */ + int out_count = 0; + int min_incr = MAX_BUCKETS; + do { + out_count += bucket_count[--min_incr]; + } while (min_incr > 1 && out_count < shared_nodes); + + /* Send all all increments > min_incr plus whatever we can at min_incr. */ + int min_count = bucket_count[min_incr] - (out_count - shared_nodes); + struct incr_stats *os = out_stats; + out_count = 0; + for (int count = 0; count < stats_count; count++) { + int delta = stats_queue[count].playout_incr - min_incr; + if (delta < 0 || (delta == 0 && --min_count < 0)) continue; + + struct tree_node *node = stats_queue[count].node; + os->incr = node->u; + stats_rm_result(&os->incr, node->pu.value, node->pu.playouts); + + /* With virtual loss os->incr.playouts might be <= 0; we only + * send positive increments to other slaves so a virtual loss + * can be propagated to other machines (good). The undo of the + * virtual loss will be propagated later when node->u gets + * above node->pu. */ + if (os->incr.playouts > 0) { + node->pu = node->u; + os->coord_path = stats_queue[count].coord_path; + assert(os->coord_path > 0); + os++; + out_count++; + } + assert (out_count <= shared_nodes); + } + *byte_size = (char *)os - (char *)out_stats; + + /* Sort the increments by increasing coord path (required by master). + * Can be done in linear time with radix sort if qsort is too slow. */ + qsort(out_stats, out_count, sizeof(*os), coord_cmp); + return out_stats; +} + +/* Get incremental stats updates for the distributed engine. + * Return a binary array of incr_stats structs in coordinate order + * (increasing levels and increasing coordinates within a level). + * This function is called only by the main thread, but may be + * called while the tree is updated by the worker threads. Keep this + * code in sync with distributed/merge.c:merge_new_stats(). */ +static void * +report_incr_stats(struct uct *u, int *stats_size) +{ + double start_time = time_now(); + + struct tree_node *root = u->t->root; + struct board *b = u->t->board; + + /* The factor 3 below has experimentally been found to be + * sufficient. At worst if we fill stats_queue we will + * discard some stats updates but this is rare. */ + int max_nodes = 3 * u->shared_nodes; + static struct stats_candidate *stats_queue = NULL; + if (!stats_queue) stats_queue = malloc2(max_nodes * sizeof(*stats_queue)); + + memset(bucket_count, 0, sizeof(bucket_count)); + + /* Try to fill the output buffer with the most important + * nodes (highest increments), while still traversing + * as little of the tree as possible. If we set min_increment + * too low we waste time. If we set it too high we can't + * fill the output buffer with the desired number of nodes. + * The best min_increment results in stats_count just above + * shared_nodes. However perfect tuning is not necessary: + * if we send too few nodes we just send shorter buffers + * more frequently. */ + static int min_increment = 1; + static int stats_count = 0; + if (stats_count > 2 * u->shared_nodes) { + min_increment++; + } else if (stats_count < u->shared_nodes / 2 && min_increment > 1) { + min_increment--; + } + + stats_count = append_stats(stats_queue, root, 0, max_nodes, 0, + max_parent_path(u, b), min_increment, b); + + void *buf = select_best_stats(stats_queue, stats_count, u->shared_nodes, stats_size); + + if (DEBUGVV(2)) + fprintf(stderr, + "min_incr %d games %d stats_queue %d/%d sending %d/%d in %.3fms\n", + min_increment, root->u.playouts - root->pu.playouts, stats_count, + max_nodes, *stats_size / (int)sizeof(struct incr_stats), u->shared_nodes, + (time_now() - start_time)*1000); + root->pu = root->u; + return buf; +} + +/* Get stats for the distributed engine. Return a buffer with one + * line "played_own root_playouts threads keep_looking @size", then + * a list of lines "coord playouts value" with absolute counts for + * children of the root node (including contributions from other + * slaves). The last line must not end with \n. + * If c is non-zero, add this move with a large weight. + * This function is called only by the main thread, but may be + * called while the tree is updated by the worker threads. Keep this + * code in sync with distributed/distributed.c:select_best_move(). */ +static char * +report_stats(struct uct *u, struct board *b, coord_t c, + bool keep_looking, int bin_size) +{ + static char reply[10240]; + char *r = reply; + char *end = reply + sizeof(reply); + struct tree_node *root = u->t->root; + r += snprintf(r, end - r, "%d %d %d %d @%d", u->played_own, root->u.playouts, + u->threads, keep_looking, bin_size); + int min_playouts = root->u.playouts / 100; + if (min_playouts < GJ_MINGAMES) + min_playouts = GJ_MINGAMES; + int max_playouts = 1; + + /* We rely on the fact that root->children is set only + * after all children are created. */ + for (struct tree_node *ni = root->children; ni; ni = ni->sibling) { + + if (is_pass(node_coord(ni))) continue; + assert(node_coord(ni) > 0 && node_coord(ni) < board_size2(b)); + + if (ni->u.playouts > max_playouts) + max_playouts = ni->u.playouts; + if (ni->u.playouts <= min_playouts || ni->hints & TREE_HINT_INVALID) + continue; + /* A book move is only added at the end: */ + if (node_coord(ni) == c) continue; + + char buf[4]; + /* We return the values as stored in the tree, so from black's view. */ + r += snprintf(r, end - r, "\n%s %d %.16f", coord2bstr(buf, node_coord(ni), b), + ni->u.playouts, ni->u.value); + } + /* Give a large but not infinite weight to pass, resign or book move, to avoid + * forcing resign if other slaves don't like it. */ + if (c) { + double resign_value = u->t->root_color == S_WHITE ? 0.0 : 1.0; + double c_value = is_resign(c) ? resign_value : 1.0 - resign_value; + r += snprintf(r, end - r, "\n%s %d %.1f", coord2sstr(c, b), + 2 * max_playouts, c_value); + } + return reply; +} + +/* genmoves is issued by the distributed engine master to all slaves, to: + * 1. Start a MCTS search if not running yet + * 2. Report current move statistics of the on-going search. + * The MCTS search is left running on the background when uct_genmoves() + * returns. It is stopped by receiving a play GTP command, triggering + * uct_pondering_stop(). */ +/* genmoves gets in the args parameter + * "played_games nodes main_time byoyomi_time byoyomi_periods byoyomi_stones @size" + * and reads a binary array of coord, playouts, value to get stats of other slaves, + * except possibly for the first call at a given move number. + * See report_stats() for the description of the return value. */ +char * +uct_genmoves(struct engine *e, struct board *b, struct time_info *ti, enum stone color, + char *args, bool pass_all_alive, void **stats_buf, int *stats_size) +{ + struct uct *u = e->data; + assert(u->slave); + u->pass_all_alive |= pass_all_alive; + + /* Prepare the state if the search is not already running. + * We must do this first since we tweak the state below + * based on instructions from the master. */ + if (!thread_manager_running) + uct_genmove_setup(u, b, color); + + /* Get playouts and time information from master. Keep this code + * in sync with distibuted/distributed.c:distributed_genmove(). */ + if ((ti->dim == TD_WALLTIME + && sscanf(args, "%d %lf %lf %d %d", &u->played_all, + &ti->len.t.main_time, &ti->len.t.byoyomi_time, + &ti->len.t.byoyomi_periods, &ti->len.t.byoyomi_stones) != 5) + + || (ti->dim == TD_GAMES && sscanf(args, "%d", &u->played_all) != 1)) { + return NULL; + } + + static struct uct_search_state s; + if (!thread_manager_running) { + /* This is the first genmoves issue, start the MCTS + * now and let it run while we receive stats. */ + memset(&s, 0, sizeof(s)); + uct_search_start(u, b, color, u->t, ti, &s); + } + + /* Read binary incremental stats if present, otherwise + * wait a bit to populate the statistics. */ + int size = 0; + char *sizep = strchr(args, '@'); + if (sizep) size = atoi(sizep+1); + if (!size) { + time_sleep(u->stats_delay); + } else if (!receive_stats(u, size)) { + return NULL; + } + + /* Check the state of the Monte Carlo Tree Search. */ + + int played_games = uct_search_games(&s); + uct_search_progress(u, b, color, u->t, ti, &s, played_games); + u->played_own = played_games - s.base_playouts; + + *stats_size = 0; + bool keep_looking = false; + coord_t best_coord = pass; + if (b->fbook) + best_coord = fbook_check(b); + if (best_coord == pass) { + keep_looking = !uct_search_check_stop(u, b, color, u->t, ti, &s, played_games); + uct_search_result(u, b, color, u->pass_all_alive, played_games, s.base_playouts, &best_coord); + /* Give heavy weight only to pass, resign and book move: */ + if (best_coord > 0) best_coord = 0; + + if (u->shared_levels) { + *stats_buf = report_incr_stats(u, stats_size); + } + } + char *reply = report_stats(u, b, best_coord, keep_looking, *stats_size); + return reply; +} diff --git a/jni/pachi/uct/slave.h b/jni/pachi/uct/slave.h new file mode 100644 index 0000000..454ad14 --- /dev/null +++ b/jni/pachi/uct/slave.h @@ -0,0 +1,17 @@ +#ifndef PACHI_UCT_SLAVE_H +#define PACHI_UCT_SLAVE_H + +#include "move.h" +#include "distributed/distributed.h" + +struct board; +struct engine; +struct time_info; + +enum parse_code uct_notify(struct engine *e, struct board *b, int id, char *cmd, char *args, char **reply); +char *uct_genmoves(struct engine *e, struct board *b, struct time_info *ti, enum stone color, + char *args, bool pass_all_alive, void **stats_buf, int *stats_size); +void *uct_htable_alloc(int hbits); +void uct_htable_reset(struct tree *t); + +#endif diff --git a/jni/pachi/uct/tree.c b/jni/pachi/uct/tree.c new file mode 100644 index 0000000..4c38582 --- /dev/null +++ b/jni/pachi/uct/tree.c @@ -0,0 +1,805 @@ +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG +#include "board.h" +#include "debug.h" +#include "engine.h" +#include "move.h" +#include "playout.h" +#include "tactics/util.h" +#include "timeinfo.h" +#include "uct/internal.h" +#include "uct/prior.h" +#include "uct/tree.h" +#include "uct/slave.h" + + +/* Allocate tree node(s). The returned nodes are initialized with zeroes. + * Returns NULL if not enough memory. + * This function may be called by multiple threads in parallel. */ +static struct tree_node * +tree_alloc_node(struct tree *t, int count, bool fast_alloc) +{ + struct tree_node *n = NULL; + size_t nsize = count * sizeof(*n); + unsigned long old_size = __sync_fetch_and_add(&t->nodes_size, nsize); + + if (fast_alloc) { + if (old_size + nsize > t->max_tree_size) + return NULL; + assert(t->nodes != NULL); + n = (struct tree_node *)(t->nodes + old_size); + memset(n, 0, nsize); + } else { + n = calloc2(count, sizeof(*n)); + } + return n; +} + +/* Initialize a node at a given place in memory. + * This function may be called by multiple threads in parallel. */ +static void +tree_setup_node(struct tree *t, struct tree_node *n, coord_t coord, int depth) +{ + static volatile unsigned int hash = 0; + n->coord = coord; + n->depth = depth; + /* n->hash is used only for debugging. It is very likely (but not + * guaranteed) to be unique. */ + hash_t h = n - (struct tree_node *)0; + n->hash = (h << 32) + (hash++ & 0xffffffff); + if (depth > t->max_depth) + t->max_depth = depth; +} + +/* Allocate and initialize a node. Returns NULL (fast_alloc mode) + * or exits the main program if not enough memory. + * This function may be called by multiple threads in parallel. */ +static struct tree_node * +tree_init_node(struct tree *t, coord_t coord, int depth, bool fast_alloc) +{ + struct tree_node *n; + n = tree_alloc_node(t, 1, fast_alloc); + if (!n) return NULL; + tree_setup_node(t, n, coord, depth); + return n; +} + +/* Create a tree structure. Pre-allocate all nodes if max_tree_size is > 0. */ +struct tree * +tree_init(struct board *board, enum stone color, unsigned long max_tree_size, + unsigned long max_pruned_size, unsigned long pruning_threshold, floating_t ltree_aging, int hbits) +{ + struct tree *t = calloc2(1, sizeof(*t)); + t->board = board; + t->max_tree_size = max_tree_size; + t->max_pruned_size = max_pruned_size; + t->pruning_threshold = pruning_threshold; + if (max_tree_size != 0) { + t->nodes = malloc2(max_tree_size); + /* The nodes buffer doesn't need initialization. This is currently + * done by tree_init_node to spread the load. Doing a memset for the + * entire buffer here would be too slow for large trees (>10 GB). */ + } + /* The root PASS move is only virtual, we never play it. */ + t->root = tree_init_node(t, pass, 0, t->nodes); + t->root_symmetry = board->symmetry; + t->root_color = stone_other(color); // to research black moves, root will be white + + t->ltree_black = tree_init_node(t, pass, 0, false); + t->ltree_white = tree_init_node(t, pass, 0, false); + t->ltree_aging = ltree_aging; + + t->hbits = hbits; + if (hbits) t->htable = uct_htable_alloc(hbits); + return t; +} + + +/* This function may be called by multiple threads in parallel on the + * same tree, but not on node n. n may be detached from the tree but + * must have been created in this tree originally. + * It returns the remaining size of the tree after n has been freed. */ +static unsigned long +tree_done_node(struct tree *t, struct tree_node *n) +{ + struct tree_node *ni = n->children; + while (ni) { + struct tree_node *nj = ni->sibling; + tree_done_node(t, ni); + ni = nj; + } + free(n); + unsigned long old_size = __sync_fetch_and_sub(&t->nodes_size, sizeof(*n)); + return old_size - sizeof(*n); +} + +struct subtree_ctx { + struct tree *t; + struct tree_node *n; +}; + +/* Worker thread for tree_done_node_detached(). Only for fast_alloc=false. */ +static void * +tree_done_node_worker(void *ctx_) +{ + struct subtree_ctx *ctx = ctx_; + char *str = coord2str(node_coord(ctx->n), ctx->t->board); + + unsigned long tree_size = tree_done_node(ctx->t, ctx->n); + if (!tree_size) + free(ctx->t); + if (DEBUGL(2)) + fprintf(stderr, "done freeing node at %s, tree size %lu\n", str, tree_size); + free(str); + free(ctx); + return NULL; +} + +/* Asynchronously free the subtree of nodes rooted at n. If the tree becomes + * empty free the tree also. Only for fast_alloc=false. */ +static void +tree_done_node_detached(struct tree *t, struct tree_node *n) +{ + if (n->u.playouts < 1000) { // no thread for small tree + if (!tree_done_node(t, n)) + free(t); + return; + } + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_t thread; + struct subtree_ctx *ctx = malloc2(sizeof(struct subtree_ctx)); + ctx->t = t; + ctx->n = n; + pthread_create(&thread, &attr, tree_done_node_worker, ctx); + pthread_attr_destroy(&attr); +} + +void +tree_done(struct tree *t) +{ + tree_done_node(t, t->ltree_black); + tree_done_node(t, t->ltree_white); + + if (t->htable) free(t->htable); + if (t->nodes) { + free(t->nodes); + free(t); + } else if (!tree_done_node(t, t->root)) { + free(t); + /* A tree_done_node_worker might still be running on this tree but + * it will free the tree later. It is also freeing nodes faster than + * we will create new ones. */ + } +} + + +static void +tree_node_dump(struct tree *tree, struct tree_node *node, int treeparity, int l, int thres) +{ + for (int i = 0; i < l; i++) fputc(' ', stderr); + int children = 0; + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) + children++; + /* We use 1 as parity, since for all nodes we want to know the + * win probability of _us_, not the node color. */ + fprintf(stderr, "[%s] %.3f/%d [prior %.3f/%d amaf %.3f/%d crit %.3f] h=%x c#=%d <%"PRIhash">\n", + coord2sstr(node_coord(node), tree->board), + tree_node_get_value(tree, treeparity, node->u.value), node->u.playouts, + tree_node_get_value(tree, treeparity, node->prior.value), node->prior.playouts, + tree_node_get_value(tree, treeparity, node->amaf.value), node->amaf.playouts, + tree_node_criticality(tree, node), + node->hints, children, node->hash); + + /* Print nodes sorted by #playouts. */ + + struct tree_node *nbox[1000]; int nboxl = 0; + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) + if (ni->u.playouts > thres) + nbox[nboxl++] = ni; + + while (true) { + int best = -1; + for (int i = 0; i < nboxl; i++) + if (nbox[i] && (best < 0 || nbox[i]->u.playouts > nbox[best]->u.playouts)) + best = i; + if (best < 0) + break; + tree_node_dump(tree, nbox[best], treeparity, l + 1, /* node->u.value < 0.1 ? 0 : */ thres); + nbox[best] = NULL; + } +} + +void +tree_dump(struct tree *tree, int thres) +{ + if (thres && tree->root->u.playouts / thres > 100) { + /* Be a bit sensible about this; the opening tbook can create + * huge dumps at first. */ + thres = tree->root->u.playouts / 100 * (thres < 1000 ? 1 : thres / 1000); + } + fprintf(stderr, "(UCT tree; root %s; extra komi %f; max depth %d)\n", + stone2str(tree->root_color), tree->extra_komi, + tree->max_depth - tree->root->depth); + tree_node_dump(tree, tree->root, 1, 0, thres); + + if (DEBUGL(3) && tree->ltree_black) { + fprintf(stderr, "B local tree:\n"); + tree_node_dump(tree, tree->ltree_black, tree->root_color == S_WHITE ? 1 : -1, 0, thres); + fprintf(stderr, "W local tree:\n"); + tree_node_dump(tree, tree->ltree_white, tree->root_color == S_BLACK ? 1 : -1, 0, thres); + } +} + + +static char * +tree_book_name(struct board *b) +{ + static char buf[256]; + if (b->handicap > 0) { + sprintf(buf, "ucttbook-%d-%02.01f-h%d.pachitree", b->size - 2, b->komi, b->handicap); + } else { + sprintf(buf, "ucttbook-%d-%02.01f.pachitree", b->size - 2, b->komi); + } + return buf; +} + +static void +tree_node_save(FILE *f, struct tree_node *node, int thres) +{ + bool save_children = node->u.playouts >= thres; + + if (!save_children) + node->is_expanded = 0; + + fputc(1, f); + fwrite(((void *) node) + offsetof(struct tree_node, depth), + sizeof(struct tree_node) - offsetof(struct tree_node, depth), + 1, f); + + if (save_children) { + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) + tree_node_save(f, ni, thres); + } else { + if (node->children) + node->is_expanded = 1; + } + + fputc(0, f); +} + +void +tree_save(struct tree *tree, struct board *b, int thres) +{ + char *filename = tree_book_name(b); + FILE *f = fopen(filename, "wb"); + if (!f) { + perror("fopen"); + return; + } + tree_node_save(f, tree->root, thres); + fputc(0, f); + fclose(f); +} + + +void +tree_node_load(FILE *f, struct tree_node *node, int *num) +{ + (*num)++; + + fread(((void *) node) + offsetof(struct tree_node, depth), + sizeof(struct tree_node) - offsetof(struct tree_node, depth), + 1, f); + + /* Keep values in sane scale, otherwise we start overflowing. */ +#define MAX_PLAYOUTS 10000000 + if (node->u.playouts > MAX_PLAYOUTS) { + node->u.playouts = MAX_PLAYOUTS; + } + if (node->amaf.playouts > MAX_PLAYOUTS) { + node->amaf.playouts = MAX_PLAYOUTS; + } + memcpy(&node->pu, &node->u, sizeof(node->u)); + + struct tree_node *ni = NULL, *ni_prev = NULL; + while (fgetc(f)) { + ni_prev = ni; ni = calloc2(1, sizeof(*ni)); + if (!node->children) + node->children = ni; + else + ni_prev->sibling = ni; + ni->parent = node; + tree_node_load(f, ni, num); + } +} + +void +tree_load(struct tree *tree, struct board *b) +{ + char *filename = tree_book_name(b); + FILE *f = fopen(filename, "rb"); + if (!f) + return; + + fprintf(stderr, "Loading opening tbook %s...\n", filename); + + int num = 0; + if (fgetc(f)) + tree_node_load(f, tree->root, &num); + fprintf(stderr, "Loaded %d nodes.\n", num); + + fclose(f); +} + + +/* Copy the subtree rooted at node: all nodes at or below depth + * or with at least threshold playouts. Only for fast_alloc. + * The code is destructive on src. The relative order of children of + * a given node is preserved (assumed by tree_get_node in particular). + * Returns the copy of node in the destination tree, or NULL + * if we could not copy it. */ +static struct tree_node * +tree_prune(struct tree *dest, struct tree *src, struct tree_node *node, + int threshold, int depth) +{ + assert(dest->nodes && node); + struct tree_node *n2 = tree_alloc_node(dest, 1, true); + if (!n2) + return NULL; + *n2 = *node; + if (n2->depth > dest->max_depth) + dest->max_depth = n2->depth; + n2->children = NULL; + n2->is_expanded = false; + + if (node->depth >= depth && node->u.playouts < threshold) + return n2; + /* For deep nodes with many playouts, we must copy all children, + * even those with zero playouts, because partially expanded + * nodes are not supported. Considering them as fully expanded + * would degrade the playing strength. The only exception is + * when dest becomes full, but this should never happen in practice + * if threshold is chosen to limit the number of nodes traversed. */ + struct tree_node *ni = node->children; + if (!ni) + return n2; + struct tree_node **prev2 = &(n2->children); + while (ni) { + struct tree_node *ni2 = tree_prune(dest, src, ni, threshold, depth); + if (!ni2) break; + *prev2 = ni2; + prev2 = &(ni2->sibling); + ni2->parent = n2; + ni = ni->sibling; + } + if (!ni) { + n2->is_expanded = true; + } else { + n2->children = NULL; // avoid partially expanded nodes + } + return n2; +} + +/* The following constants are used for garbage collection of nodes. + * A tree is considered large if the top node has >= 40K playouts. + * For such trees, we copy deep nodes only if they have enough + * playouts, with a gradually increasing threshold up to 40. + * These constants define how much time we're willing to spend + * scanning the source tree when promoting a move. The chosen values + * make worst case pruning in about 3s for 20 GB ram, and this + * is only for long thinking time (>1M playouts). For fast games the + * trees don't grow large. For small ram or fast game we copy the + * entire tree. These values do not degrade playing strength and are + * necessary to avoid losing on time; increasing DEEP_PLAYOUTS_THRESHOLD + * or decreasing LARGE_TREE_PLAYOUTS will make the program faster but + * playing worse. */ +#define LARGE_TREE_PLAYOUTS 40000LL +#define DEEP_PLAYOUTS_THRESHOLD 40 + +/* Garbage collect the tree early if the top node has < 5K playouts, + * to avoid having to do it later on a large subtree. + * This guarantees garbage collection in < 1s. */ +#define SMALL_TREE_PLAYOUTS 5000 + +/* Free all the tree, keeping only the subtree rooted at node. + * Prune the subtree if necessary to fit in memory or + * to save time scanning the tree. + * Returns the moved node. Only for fast_alloc. */ +struct tree_node * +tree_garbage_collect(struct tree *tree, struct tree_node *node) +{ + assert(tree->nodes && !node->parent && !node->sibling); + double start_time = time_now(); + unsigned long orig_size = tree->nodes_size; + + struct tree *temp_tree = tree_init(tree->board, tree->root_color, + tree->max_pruned_size, 0, 0, tree->ltree_aging, 0); + temp_tree->nodes_size = 0; // We do not want the dummy pass node + struct tree_node *temp_node; + + /* Find the maximum depth at which we can copy all nodes. */ + int max_nodes = 1; + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) + max_nodes++; + unsigned long nodes_size = max_nodes * sizeof(*node); + int max_depth = node->depth; + while (nodes_size < tree->max_pruned_size && max_nodes > 1) { + max_nodes--; + nodes_size += max_nodes * nodes_size; + max_depth++; + } + + /* Copy all nodes for small trees. For large trees, copy all nodes + * with depth <= max_depth, and all nodes with enough playouts. + * Avoiding going too deep (except for nodes with many playouts) is mostly + * to save time scanning the source tree. It can take over 20s to traverse + * completely a large source tree (20 GB) even without copying because + * the traversal is not friendly at all with the memory cache. */ + int threshold = (node->u.playouts - LARGE_TREE_PLAYOUTS) * DEEP_PLAYOUTS_THRESHOLD / LARGE_TREE_PLAYOUTS; + if (threshold < 0) threshold = 0; + if (threshold > DEEP_PLAYOUTS_THRESHOLD) threshold = DEEP_PLAYOUTS_THRESHOLD; + temp_node = tree_prune(temp_tree, tree, node, threshold, max_depth); + assert(temp_node); + + /* Now copy back to original tree. */ + tree->nodes_size = 0; + tree->max_depth = 0; + struct tree_node *new_node = tree_prune(tree, temp_tree, temp_node, 0, temp_tree->max_depth); + + if (DEBUGL(1)) { + double now = time_now(); + static double prev_time; + if (!prev_time) prev_time = start_time; + fprintf(stderr, + "tree pruned in %0.6g s, prev %0.3g s ago, dest depth %d wanted %d," + " size %lu->%lu/%lu, playouts %d\n", + now - start_time, start_time - prev_time, temp_tree->max_depth, max_depth, + orig_size, temp_tree->nodes_size, tree->max_pruned_size, new_node->u.playouts); + prev_time = start_time; + } + if (temp_tree->nodes_size >= temp_tree->max_tree_size) { + fprintf(stderr, "temp tree overflow, max_tree_size %lu, pruning_threshold %lu\n", + tree->max_tree_size, tree->pruning_threshold); + /* This is not a serious problem, we will simply recompute the discarded nodes + * at the next move if necessary. This is better than frequently wasting memory. */ + } else { + assert(tree->nodes_size == temp_tree->nodes_size); + assert(tree->max_depth == temp_tree->max_depth); + } + tree_done(temp_tree); + return new_node; +} + + +/* Get a node of given coordinate from within parent, possibly creating it + * if necessary - in a very raw form (no .d, priors, ...). */ +/* FIXME: Adjust for board symmetry. */ +struct tree_node * +tree_get_node(struct tree *t, struct tree_node *parent, coord_t c, bool create) +{ + if (!parent->children || node_coord(parent->children) >= c) { + /* Special case: Insertion at the beginning. */ + if (parent->children && node_coord(parent->children) == c) + return parent->children; + if (!create) + return NULL; + + struct tree_node *nn = tree_init_node(t, c, parent->depth + 1, false); + nn->parent = parent; nn->sibling = parent->children; + parent->children = nn; + return nn; + } + + /* No candidate at the beginning, look through all the children. */ + + struct tree_node *ni; + for (ni = parent->children; ni->sibling; ni = ni->sibling) + if (node_coord(ni->sibling) >= c) + break; + + if (ni->sibling && node_coord(ni->sibling) == c) + return ni->sibling; + assert(node_coord(ni) < c); + if (!create) + return NULL; + + struct tree_node *nn = tree_init_node(t, c, parent->depth + 1, false); + nn->parent = parent; nn->sibling = ni->sibling; ni->sibling = nn; + return nn; +} + +/* Get local tree node corresponding to given node, given local node child + * iterator @lni (which points either at the corresponding node, or at the + * nearest local tree node after @ni). */ +struct tree_node * +tree_lnode_for_node(struct tree *tree, struct tree_node *ni, struct tree_node *lni, int tenuki_d) +{ + /* Now set up lnode, which is the actual local node + * corresponding to ni - either lni if it is an + * exact match and ni is not tenuki, local + * node if ni is tenuki, or NULL if there is no + * corresponding node available. */ + + if (is_pass(node_coord(ni))) { + /* Also, for sanity reasons we never use local + * tree for passes. (Maybe we could, but it's + * too hard to think about.) */ + return NULL; + } + + if (node_coord(lni) == node_coord(ni)) { + /* We don't consider tenuki a sequence play + * that we have in local tree even though + * ni->d is too high; this can happen if this + * occured in different board topology. */ + return lni; + } + + if (ni->d >= tenuki_d) { + /* Tenuki, pick a pass lsibling if available. */ + assert(lni->parent && lni->parent->children); + if (is_pass(node_coord(lni->parent->children))) { + return lni->parent->children; + } else { + return NULL; + } + } + + /* No corresponding local node, lnode stays NULL. */ + return NULL; +} + + +/* Tree symmetry: When possible, we will localize the tree to a single part + * of the board in tree_expand_node() and possibly flip along symmetry axes + * to another part of the board in tree_promote_at(). We follow b->symmetry + * guidelines here. */ + + +/* This function must be thread safe, given that board b is only modified by the calling thread. */ +void +tree_expand_node(struct tree *t, struct tree_node *node, struct board *b, enum stone color, struct uct *u, int parity) +{ + /* Get a Common Fate Graph distance map from parent node. */ + int distances[board_size2(b)]; + if (!is_pass(b->last_move.coord) && !is_resign(b->last_move.coord)) { + cfg_distances(b, node_coord(node), distances, TREE_NODE_D_MAX); + } else { + // Pass or resign - everything is too far. + foreach_point(b) { distances[c] = TREE_NODE_D_MAX + 1; } foreach_point_end; + } + + /* Get a map of prior values to initialize the new nodes with. */ + struct prior_map map = { + .b = b, + .to_play = color, + .parity = tree_parity(t, parity), + .distances = distances, + }; + // Include pass in the prior map. + struct move_stats map_prior[board_size2(b) + 1]; map.prior = &map_prior[1]; + bool map_consider[board_size2(b) + 1]; map.consider = &map_consider[1]; + memset(map_prior, 0, sizeof(map_prior)); + memset(map_consider, 0, sizeof(map_consider)); + map.consider[pass] = true; + int child_count = 1; // for pass + foreach_free_point(b) { + assert(board_at(b, c) == S_NONE); + if (!board_is_valid_play(b, color, c)) + continue; + map.consider[c] = true; + child_count++; + } foreach_free_point_end; + uct_prior(u, node, &map); + + /* Now, create the nodes (all at once if fast_alloc) */ + struct tree_node *ni = t->nodes ? tree_alloc_node(t, child_count, true) : tree_alloc_node(t, 1, false); + /* In fast_alloc mode we might temporarily run out of nodes but this should be rare. */ + if (!ni) { + node->is_expanded = false; + return; + } + tree_setup_node(t, ni, pass, node->depth + 1); + + struct tree_node *first_child = ni; + ni->parent = node; + ni->prior = map.prior[pass]; ni->d = TREE_NODE_D_MAX + 1; + + /* The loop considers only the symmetry playground. */ + if (UDEBUGL(6)) { + fprintf(stderr, "expanding %s within [%d,%d],[%d,%d] %d-%d\n", + coord2sstr(node_coord(node), b), + b->symmetry.x1, b->symmetry.y1, + b->symmetry.x2, b->symmetry.y2, + b->symmetry.type, b->symmetry.d); + } + int child = 1; + for (int j = b->symmetry.y1; j <= b->symmetry.y2; j++) { + for (int i = b->symmetry.x1; i <= b->symmetry.x2; i++) { + if (b->symmetry.d) { + int x = b->symmetry.type == SYM_DIAG_DOWN ? board_size(b) - 1 - i : i; + if (x > j) { + if (UDEBUGL(7)) + fprintf(stderr, "drop %d,%d\n", i, j); + continue; + } + } + + coord_t c = coord_xy(t->board, i, j); + if (!map.consider[c]) // Filter out invalid moves + continue; + assert(c != node_coord(node)); // I have spotted "C3 C3" in some sequence... + + struct tree_node *nj = t->nodes ? first_child + child++ : tree_alloc_node(t, 1, false); + tree_setup_node(t, nj, c, node->depth + 1); + nj->parent = node; ni->sibling = nj; ni = nj; + + ni->prior = map.prior[c]; + ni->d = distances[c]; + } + } + node->children = first_child; // must be done at the end to avoid race +} + + +static coord_t +flip_coord(struct board *b, coord_t c, + bool flip_horiz, bool flip_vert, int flip_diag) +{ + int x = coord_x(c, b), y = coord_y(c, b); + if (flip_diag) { + int z = x; x = y; y = z; + } + if (flip_horiz) { + x = board_size(b) - 1 - x; + } + if (flip_vert) { + y = board_size(b) - 1 - y; + } + return coord_xy(b, x, y); +} + +static void +tree_fix_node_symmetry(struct board *b, struct tree_node *node, + bool flip_horiz, bool flip_vert, int flip_diag) +{ + if (!is_pass(node_coord(node))) + node->coord = flip_coord(b, node_coord(node), flip_horiz, flip_vert, flip_diag); + + for (struct tree_node *ni = node->children; ni; ni = ni->sibling) + tree_fix_node_symmetry(b, ni, flip_horiz, flip_vert, flip_diag); +} + +static void +tree_fix_symmetry(struct tree *tree, struct board *b, coord_t c) +{ + if (is_pass(c)) + return; + + struct board_symmetry *s = &tree->root_symmetry; + int cx = coord_x(c, b), cy = coord_y(c, b); + + /* playground X->h->v->d normalization + * :::.. .d... + * .::.. v.... + * ..:.. ..... + * ..... h...X + * ..... ..... */ + bool flip_horiz = cx < s->x1 || cx > s->x2; + bool flip_vert = cy < s->y1 || cy > s->y2; + + bool flip_diag = 0; + if (s->d) { + bool dir = (s->type == SYM_DIAG_DOWN); + int x = dir ^ flip_horiz ^ flip_vert ? board_size(b) - 1 - cx : cx; + if (flip_vert ? x < cy : x > cy) { + flip_diag = 1; + } + } + + if (DEBUGL(4)) { + fprintf(stderr, "%s [%d,%d -> %d,%d;%d,%d] will flip %d %d %d -> %s, sym %d (%d) -> %d (%d)\n", + coord2sstr(c, b), + cx, cy, s->x1, s->y1, s->x2, s->y2, + flip_horiz, flip_vert, flip_diag, + coord2sstr(flip_coord(b, c, flip_horiz, flip_vert, flip_diag), b), + s->type, s->d, b->symmetry.type, b->symmetry.d); + } + if (flip_horiz || flip_vert || flip_diag) + tree_fix_node_symmetry(b, tree->root, flip_horiz, flip_vert, flip_diag); +} + + +static void +tree_unlink_node(struct tree_node *node) +{ + struct tree_node *ni = node->parent; + if (ni->children == node) { + ni->children = node->sibling; + } else { + ni = ni->children; + while (ni->sibling != node) + ni = ni->sibling; + ni->sibling = node->sibling; + } + node->sibling = NULL; + node->parent = NULL; +} + +/* Reduce weight of statistics on promotion. Remove nodes that + * get reduced to zero playouts; returns next node to consider + * in the children list (@node may get deleted). */ +static struct tree_node * +tree_age_node(struct tree *tree, struct tree_node *node) +{ + node->u.playouts /= tree->ltree_aging; + if (node->parent && !node->u.playouts) { + struct tree_node *sibling = node->sibling; + /* Delete node, no playouts. */ + tree_unlink_node(node); + tree_done_node(tree, node); + return sibling; + } + + struct tree_node *ni = node->children; + while (ni) ni = tree_age_node(tree, ni); + return node->sibling; +} + +/* Promotes the given node as the root of the tree. In the fast_alloc + * mode, the node may be moved and some of its subtree may be pruned. */ +void +tree_promote_node(struct tree *tree, struct tree_node **node) +{ + assert((*node)->parent == tree->root); + tree_unlink_node(*node); + if (!tree->nodes) { + /* Freeing the rest of the tree can take several seconds on large + * trees, so we must do it asynchronously: */ + tree_done_node_detached(tree, tree->root); + } else { + /* Garbage collect if we run out of memory, or it is cheap to do so now: */ + if (tree->nodes_size >= tree->pruning_threshold + || (tree->nodes_size >= tree->max_tree_size / 10 && (*node)->u.playouts < SMALL_TREE_PLAYOUTS)) + *node = tree_garbage_collect(tree, *node); + } + tree->root = *node; + tree->root_color = stone_other(tree->root_color); + + board_symmetry_update(tree->board, &tree->root_symmetry, node_coord(*node)); + tree->avg_score.playouts = 0; + + /* If the tree deepest node was under node, or if we called tree_garbage_collect, + * tree->max_depth is correct. Otherwise we could traverse the tree + * to recompute max_depth but it's not worth it: it's just for debugging + * and soon the tree will grow and max_depth will become correct again. */ + + if (tree->ltree_aging != 1.0f) { // XXX: != should work here even with the floating_t + tree_age_node(tree, tree->ltree_black); + tree_age_node(tree, tree->ltree_white); + } +} + +bool +tree_promote_at(struct tree *tree, struct board *b, coord_t c) +{ + tree_fix_symmetry(tree, b, c); + + for (struct tree_node *ni = tree->root->children; ni; ni = ni->sibling) { + if (node_coord(ni) == c) { + tree_promote_node(tree, &ni); + return true; + } + } + return false; +} diff --git a/jni/pachi/uct/tree.h b/jni/pachi/uct/tree.h new file mode 100644 index 0000000..c16080a --- /dev/null +++ b/jni/pachi/uct/tree.h @@ -0,0 +1,194 @@ +#ifndef PACHI_UCT_TREE_H +#define PACHI_UCT_TREE_H + +/* Management of UCT trees. See diagram below for the node structure. + * + * Two allocation methods are supported for the tree nodes: + * + * - calloc/free: each node is allocated with one calloc. + * After a move, all nodes except the subtree rooted at + * the played move are freed one by one with free(). + * Since this can be very slow (seen 9s and loss on time because + * of this) the nodes are freed in a background thread. + * We still reserve enough memory for the next move in case + * the background thread doesn't free nodes fast enough. + * + * - fast_alloc: a large buffer is allocated once, and each + * node allocation takes some of this buffer. After a move + * is played, no memory if freed if the buffer still has + * enough free space. Otherwise the subtree rooted at the + * played move is copied to a temporary buffer, pruning it + * if necessary to fit in this small buffer. We copy by + * preference nodes with largest number of playouts. + * Then the temporary buffer is copied back to the original + * buffer, which has now plenty of space. + * Once the fast_alloc mode is proven reliable, the + * calloc/free method will be removed. */ + +#include +#include +#include "move.h" +#include "stats.h" +#include "probdist.h" + +struct board; +struct uct; + +/* + * +------+ + * | node | + * +------+ + * / <- parent + * +------+ v- sibling +------+ + * | node | ------------ | node | + * +------+ +------+ + * | <- children | + * +------+ +------+ +------+ +------+ + * | node | - | node | | node | - | node | + * +------+ +------+ +------+ +------+ + */ + +/* TODO: Performance would benefit from a reorganization: + * (i) Allocate all children of a node within a single block. + * (ii) Keep all u stats together, and all amaf stats together. + * Currently, rave_update is top source of cache misses, and + * there is large memory overhead for having all nodes separate. */ + +struct tree_node { + hash_t hash; + struct tree_node *parent, *sibling, *children; + + /*** From here on, struct is saved/loaded from opening tbook */ + + unsigned short depth; // just for statistics + + /* Common Fate Graph distance from parent, but at most TREE_NODE_D_MAX+1 */ +#define TREE_NODE_D_MAX 3 + unsigned char d; + +#define TREE_HINT_INVALID 1 // don't go to this node, invalid move + unsigned char hints; + + /* coord is usually coord_t, but this is very space-sensitive. */ +#define node_coord(n) ((int) (n)->coord) + short coord; + + /* In case multiple threads walk the tree, is_expanded is set + * atomically. Only the first thread setting it expands the node. + * The node goes through 3 states: + * 1) children == null, is_expanded == false: leaf node + * 2) children == null, is_expanded == true: one thread currently expanding + * 2) children != null, is_expanded == true: fully expanded node */ + bool is_expanded; + + struct move_stats u; + struct move_stats prior; + /* XXX: Should be way for policies to add their own stats */ + struct move_stats amaf; + /* Stats before starting playout; used for distributed engine. */ + struct move_stats pu; + /* Criticality information; information about final board owner + * of the tree coordinate corresponding to the node */ + struct move_stats winner_owner; // owner == winner + struct move_stats black_owner; // owner == black +}; + +struct tree_hash; + +struct tree { + struct board *board; + struct tree_node *root; + struct board_symmetry root_symmetry; + enum stone root_color; + + /* Whether to use any extra komi during score counting. This is + * tree-specific variable since this can arbitrarily change between + * moves. */ + bool use_extra_komi; + /* The value of applied extra komi. For DYNKOMI_LINEAR, this value + * is only informative, the actual value is computed per simulation + * based on leaf node depth. */ + floating_t extra_komi; + /* Score in simulations, averaged over all branches, in the last + * search episode. */ + struct move_stats avg_score; + + /* We merge local (non-tenuki) sequences for both colors, occuring + * anywhere in the tree; nodes are created on-demand, special 'pass' + * nodes represent tenuki. Only u move_stats are used, prior and amaf + * is ignored. Values in root node are ignored. */ + /* The value corresponds to black-to-play as usual; i.e. if white + * succeeds in its replies, the values will be low. */ + struct tree_node *ltree_black; + /* ltree_white has white-first sequences as children. */ + struct tree_node *ltree_white; + /* Aging factor; 2 means halve all playout values after each turn. + * 1 means don't age at all. */ + floating_t ltree_aging; + + /* Hash table used when working as slave for the distributed engine. + * Maps coordinate path to tree node. */ + struct tree_hash *htable; + int hbits; + + // Statistics + int max_depth; + volatile unsigned long nodes_size; // byte size of all allocated nodes + unsigned long max_tree_size; // maximum byte size for entire tree, > 0 only for fast_alloc + unsigned long max_pruned_size; + unsigned long pruning_threshold; + void *nodes; // nodes buffer, only for fast_alloc +}; + +/* Warning: all functions below except tree_expand_node & tree_leaf_node are THREAD-UNSAFE! */ +struct tree *tree_init(struct board *board, enum stone color, unsigned long max_tree_size, + unsigned long max_pruned_size, unsigned long pruning_threshold, floating_t ltree_aging, int hbits); +void tree_done(struct tree *tree); +void tree_dump(struct tree *tree, int thres); +void tree_save(struct tree *tree, struct board *b, int thres); +void tree_load(struct tree *tree, struct board *b); + +struct tree_node *tree_get_node(struct tree *tree, struct tree_node *node, coord_t c, bool create); +struct tree_node *tree_garbage_collect(struct tree *tree, struct tree_node *node); +void tree_promote_node(struct tree *tree, struct tree_node **node); +bool tree_promote_at(struct tree *tree, struct board *b, coord_t c); + +void tree_expand_node(struct tree *tree, struct tree_node *node, struct board *b, enum stone color, struct uct *u, int parity); +struct tree_node *tree_lnode_for_node(struct tree *tree, struct tree_node *ni, struct tree_node *lni, int tenuki_d); + +static bool tree_leaf_node(struct tree_node *node); + +#define tree_node_parity(tree, node) \ + ((((node)->depth ^ (tree)->root->depth) & 1) ? -1 : 1) + +/* Get black parity from parity within the tree. */ +#define tree_parity(tree, parity) \ + (tree->root_color == S_WHITE ? (parity) : -1 * (parity)) + +/* Get a 0..1 value to maximize; @parity is parity within the tree. */ +#define tree_node_get_value(tree, parity, value) \ + (tree_parity(tree, parity) > 0 ? value : 1 - value) + +static inline bool +tree_leaf_node(struct tree_node *node) +{ + return !(node->children); +} + +static inline floating_t +tree_node_criticality(const struct tree *t, const struct tree_node *node) +{ + /* cov(player_gets, player_wins) = + * [The argument: If 'gets' and 'wins' is uncorrelated, b_gets * b_wins + * is valid way to obtain winner_gets. The more correlated it is, the + * more distorted the result.] + * = winner_gets - (b_gets * b_wins + w_gets * w_wins) + * = winner_gets - (b_gets * b_wins + (1 - b_gets) * (1 - b_wins)) + * = winner_gets - (b_gets * b_wins + 1 - b_gets - b_wins + b_gets * b_wins) + * = winner_gets - (2 * b_gets * b_wins - b_gets - b_wins + 1) */ + return node->winner_owner.value + - (2 * node->black_owner.value * node->u.value + - node->black_owner.value - node->u.value + 1); +} + +#endif diff --git a/jni/pachi/uct/uct.c b/jni/pachi/uct/uct.c new file mode 100644 index 0000000..8db9ff9 --- /dev/null +++ b/jni/pachi/uct/uct.c @@ -0,0 +1,1207 @@ +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#include "debug.h" +#include "board.h" +#include "gtp.h" +#include "chat.h" +#include "move.h" +#include "mq.h" +#include "joseki/base.h" +#include "playout.h" +#include "playout/moggy.h" +#include "playout/light.h" +#include "tactics/util.h" +#include "timeinfo.h" +#include "uct/dynkomi.h" +#include "uct/internal.h" +#include "uct/plugins.h" +#include "uct/prior.h" +#include "uct/search.h" +#include "uct/slave.h" +#include "uct/tree.h" +#include "uct/uct.h" +#include "uct/walk.h" + +struct uct_policy *policy_ucb1_init(struct uct *u, char *arg); +struct uct_policy *policy_ucb1amaf_init(struct uct *u, char *arg, struct board *board); +static void uct_pondering_start(struct uct *u, struct board *b0, struct tree *t, enum stone color); + +/* Maximal simulation length. */ +#define MC_GAMELEN MAX_GAMELEN + + +static void +setup_state(struct uct *u, struct board *b, enum stone color) +{ + u->t = tree_init(b, color, u->fast_alloc ? u->max_tree_size : 0, + u->max_pruned_size, u->pruning_threshold, u->local_tree_aging, u->stats_hbits); + if (u->initial_extra_komi) + u->t->extra_komi = u->initial_extra_komi; + if (u->force_seed) + fast_srandom(u->force_seed); + if (UDEBUGL(3)) + fprintf(stderr, "Fresh board with random seed %lu\n", fast_getseed()); + if (!u->no_tbook && b->moves == 0) { + if (color == S_BLACK) { + tree_load(u->t, b); + } else if (DEBUGL(0)) { + fprintf(stderr, "Warning: First move appears to be white\n"); + } + } +} + +static void +reset_state(struct uct *u) +{ + assert(u->t); + tree_done(u->t); u->t = NULL; +} + +static void +setup_dynkomi(struct uct *u, struct board *b, enum stone to_play) +{ + if (u->t->use_extra_komi && !u->pondering && u->dynkomi->permove) + u->t->extra_komi = u->dynkomi->permove(u->dynkomi, b, u->t); + else if (!u->t->use_extra_komi) + u->t->extra_komi = 0; +} + +void +uct_prepare_move(struct uct *u, struct board *b, enum stone color) +{ + if (u->t) { + /* Verify that we have sane state. */ + assert(b->es == u); + assert(u->t && b->moves); + if (color != stone_other(u->t->root_color)) { + fprintf(stderr, "Fatal: Non-alternating play detected %d %d\n", + color, u->t->root_color); + exit(1); + } + uct_htable_reset(u->t); + + } else { + /* We need fresh state. */ + b->es = u; + setup_state(u, b, color); + } + + u->ownermap.playouts = 0; + memset(u->ownermap.map, 0, board_size2(b) * sizeof(u->ownermap.map[0])); + u->played_own = u->played_all = 0; +} + +static void +dead_group_list(struct uct *u, struct board *b, struct move_queue *mq) +{ + enum gj_state gs_array[board_size2(b)]; + struct group_judgement gj = { .thres = GJ_THRES, .gs = gs_array }; + board_ownermap_judge_groups(b, &u->ownermap, &gj); + groups_of_status(b, &gj, GS_DEAD, mq); +} + +bool +uct_pass_is_safe(struct uct *u, struct board *b, enum stone color, bool pass_all_alive) +{ + /* Make sure enough playouts are simulated to get a reasonable dead group list. */ + while (u->ownermap.playouts < GJ_MINGAMES) + uct_playout(u, b, color, u->t); + + struct move_queue mq = { .moves = 0 }; + dead_group_list(u, b, &mq); + if (pass_all_alive) { + for (unsigned int i = 0; i < mq.moves; i++) { + if (board_at(b, mq.move[i]) == stone_other(color)) { + return false; // We need to remove opponent dead groups first. + } + } + mq.moves = 0; // our dead stones are alive when pass_all_alive is true + } + if (u->allow_losing_pass) { + foreach_point(b) { + if (board_at(b, c) == S_OFFBOARD) + continue; + if (board_ownermap_judge_point(&u->ownermap, c, GJ_THRES) == PJ_UNKNOWN) { + if (UDEBUGL(3)) + fprintf(stderr, "uct_pass_is_safe fails at %s[%d]\n", coord2sstr(c, b), c); + return false; // Unclear point, clarify first. + } + } foreach_point_end; + return true; + } + return pass_is_safe(b, color, &mq); +} + +static char * +uct_printhook_ownermap(struct board *board, coord_t c, char *s, char *end) +{ + struct uct *u = board->es; + if (!u) { + strcat(s, ". "); + return s + 2; + } + const char chr[] = ":XO,"; // dame, black, white, unclear + const char chm[] = ":xo,"; + char ch = chr[board_ownermap_judge_point(&u->ownermap, c, GJ_THRES)]; + if (ch == ',') { // less precise estimate then? + ch = chm[board_ownermap_judge_point(&u->ownermap, c, 0.67)]; + } + s += snprintf(s, end - s, "%c ", ch); + return s; +} + +static char * +uct_notify_play(struct engine *e, struct board *b, struct move *m, char *enginearg) +{ + struct uct *u = e->data; + if (!u->t) { + /* No state, create one - this is probably game beginning + * and we need to load the opening tbook right now. */ + uct_prepare_move(u, b, m->color); + assert(u->t); + } + + /* Stop pondering, required by tree_promote_at() */ + uct_pondering_stop(u); + if (UDEBUGL(2) && u->slave) + tree_dump(u->t, u->dumpthres); + + if (is_resign(m->coord)) { + /* Reset state. */ + reset_state(u); + return NULL; + } + + /* Promote node of the appropriate move to the tree root. */ + assert(u->t->root); + if (!tree_promote_at(u->t, b, m->coord)) { + if (UDEBUGL(3)) + fprintf(stderr, "Warning: Cannot promote move node! Several play commands in row?\n"); + /* Preserve dynamic komi information, though, that is important. */ + u->initial_extra_komi = u->t->extra_komi; + reset_state(u); + return NULL; + } + + /* If we are a slave in a distributed engine, start pondering once + * we know which move we actually played. See uct_genmove() about + * the check for pass. */ + if (u->pondering_opt && u->slave && m->color == u->my_color && !is_pass(m->coord)) + uct_pondering_start(u, b, u->t, stone_other(m->color)); + + return NULL; +} + +static char * +uct_undo(struct engine *e, struct board *b) +{ + struct uct *u = e->data; + + if (!u->t) return NULL; + uct_pondering_stop(u); + u->initial_extra_komi = u->t->extra_komi; + reset_state(u); + return NULL; +} + +static char * +uct_result(struct engine *e, struct board *b) +{ + struct uct *u = e->data; + static char reply[1024]; + + if (!u->t) + return NULL; + enum stone color = u->t->root_color; + struct tree_node *n = u->t->root; + snprintf(reply, 1024, "%s %s %d %.2f %.1f", + stone2str(color), coord2sstr(node_coord(n), b), + n->u.playouts, tree_node_get_value(u->t, -1, n->u.value), + u->t->use_extra_komi ? u->t->extra_komi : 0); + return reply; +} + +static char * +uct_chat(struct engine *e, struct board *b, bool opponent, char *from, char *cmd) +{ + struct uct *u = e->data; + + if (!u->t) + return generic_chat(b, opponent, from, cmd, S_NONE, pass, 0, 1, u->threads, 0.0, 0.0); + + struct tree_node *n = u->t->root; + double winrate = tree_node_get_value(u->t, -1, n->u.value); + double extra_komi = u->t->use_extra_komi && abs(u->t->extra_komi) >= 0.5 ? u->t->extra_komi : 0; + + return generic_chat(b, opponent, from, cmd, u->t->root_color, node_coord(n), n->u.playouts, 1, + u->threads, winrate, extra_komi); +} + +static void +uct_dead_group_list(struct engine *e, struct board *b, struct move_queue *mq) +{ + struct uct *u = e->data; + + /* This means the game is probably over, no use pondering on. */ + uct_pondering_stop(u); + + if (u->pass_all_alive) + return; // no dead groups + + bool mock_state = false; + + if (!u->t) { + /* No state, but we cannot just back out - we might + * have passed earlier, only assuming some stones are + * dead, and then re-connected, only to lose counting + * when all stones are assumed alive. */ + uct_prepare_move(u, b, S_BLACK); assert(u->t); + mock_state = true; + } + /* Make sure the ownermap is well-seeded. */ + while (u->ownermap.playouts < GJ_MINGAMES) + uct_playout(u, b, S_BLACK, u->t); + /* Show the ownermap: */ + if (DEBUGL(2)) + board_print_custom(b, stderr, uct_printhook_ownermap); + + dead_group_list(u, b, mq); + + if (mock_state) { + /* Clean up the mock state in case we will receive + * a genmove; we could get a non-alternating-move + * error from uct_prepare_move() in that case otherwise. */ + reset_state(u); + } +} + +static void +playout_policy_done(struct playout_policy *p) +{ + if (p->done) p->done(p); + if (p->data) free(p->data); + free(p); +} + +static void +uct_done(struct engine *e) +{ + /* This is called on engine reset, especially when clear_board + * is received and new game should begin. */ + struct uct *u = e->data; + uct_pondering_stop(u); + if (u->t) reset_state(u); + free(u->ownermap.map); + + free(u->policy); + free(u->random_policy); + playout_policy_done(u->playout); + uct_prior_done(u->prior); + joseki_done(u->jdict); + pluginset_done(u->plugins); +} + + + +/* Run time-limited MCTS search on foreground. */ +static int +uct_search(struct uct *u, struct board *b, struct time_info *ti, enum stone color, struct tree *t, bool print_progress) +{ + struct uct_search_state s; + uct_search_start(u, b, color, t, ti, &s); + if (UDEBUGL(2) && s.base_playouts > 0) + fprintf(stderr, "\n", s.base_playouts); + + /* The search tree is ctx->t. This is currently == . It is important + * to reference ctx->t directly since the + * thread manager will swap the tree pointer asynchronously. */ + + /* Now, just periodically poll the search tree. */ + /* Note that in case of TD_GAMES, threads will not wait for + * the uct_search_check_stop() signalization. */ + while (1) { + time_sleep(TREE_BUSYWAIT_INTERVAL); + /* TREE_BUSYWAIT_INTERVAL should never be less than desired time, or the + * time control is broken. But if it happens to be less, we still search + * at least 100ms otherwise the move is completely random. */ + + int i = uct_search_games(&s); + /* Print notifications etc. */ + uct_search_progress(u, b, color, t, ti, &s, i); + /* Check if we should stop the search. */ + if (uct_search_check_stop(u, b, color, t, ti, &s, i)) + break; + } + + struct uct_thread_ctx *ctx = uct_search_stop(); + if (UDEBUGL(2)) tree_dump(t, u->dumpthres); + if (UDEBUGL(2)) + fprintf(stderr, "(avg score %f/%d; dynkomi's %f/%d value %f/%d)\n", + t->avg_score.value, t->avg_score.playouts, + u->dynkomi->score.value, u->dynkomi->score.playouts, + u->dynkomi->value.value, u->dynkomi->value.playouts); + if (print_progress) + uct_progress_status(u, t, color, ctx->games, NULL); + + u->played_own += ctx->games; + return ctx->games; +} + +/* Start pondering background with @color to play. */ +static void +uct_pondering_start(struct uct *u, struct board *b0, struct tree *t, enum stone color) +{ + if (UDEBUGL(1)) + fprintf(stderr, "Starting to ponder with color %s\n", stone2str(stone_other(color))); + u->pondering = true; + + /* We need a local board copy to ponder upon. */ + struct board *b = malloc2(sizeof(*b)); board_copy(b, b0); + + /* *b0 did not have the genmove'd move played yet. */ + struct move m = { node_coord(t->root), t->root_color }; + int res = board_play(b, &m); + assert(res >= 0); + setup_dynkomi(u, b, stone_other(m.color)); + + /* Start MCTS manager thread "headless". */ + static struct uct_search_state s; + uct_search_start(u, b, color, t, NULL, &s); +} + +/* uct_search_stop() frontend for the pondering (non-genmove) mode, and + * to stop the background search for a slave in the distributed engine. */ +void +uct_pondering_stop(struct uct *u) +{ + if (!thread_manager_running) + return; + + /* Stop the thread manager. */ + struct uct_thread_ctx *ctx = uct_search_stop(); + if (UDEBUGL(1)) { + if (u->pondering) fprintf(stderr, "(pondering) "); + uct_progress_status(u, ctx->t, ctx->color, ctx->games, NULL); + } + if (u->pondering) { + free(ctx->b); + u->pondering = false; + } +} + + +void +uct_genmove_setup(struct uct *u, struct board *b, enum stone color) +{ + if (b->superko_violation) { + fprintf(stderr, "!!! WARNING: SUPERKO VIOLATION OCCURED BEFORE THIS MOVE\n"); + fprintf(stderr, "Maybe you play with situational instead of positional superko?\n"); + fprintf(stderr, "I'm going to ignore the violation, but note that I may miss\n"); + fprintf(stderr, "some moves valid under this ruleset because of this.\n"); + b->superko_violation = false; + } + + uct_prepare_move(u, b, color); + + assert(u->t); + u->my_color = color; + + /* How to decide whether to use dynkomi in this game? Since we use + * pondering, it's not simple "who-to-play" matter. Decide based on + * the last genmove issued. */ + u->t->use_extra_komi = !!(u->dynkomi_mask & color); + setup_dynkomi(u, b, color); + + if (b->rules == RULES_JAPANESE) + u->territory_scoring = true; + + /* Make pessimistic assumption about komi for Japanese rules to + * avoid losing by 0.5 when winning by 0.5 with Chinese rules. + * The rules usually give the same winner if the integer part of komi + * is odd so we adjust the komi only if it is even (for a board of + * odd size). We are not trying to get an exact evaluation for rare + * cases of seki. For details see http://home.snafu.de/jasiek/parity.html */ + if (u->territory_scoring && (((int)floor(b->komi) + board_size(b)) & 1)) { + b->komi += (color == S_BLACK ? 1.0 : -1.0); + if (UDEBUGL(0)) + fprintf(stderr, "Setting komi to %.1f assuming Japanese rules\n", + b->komi); + } +} + +static coord_t * +uct_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive) +{ + double start_time = time_now(); + struct uct *u = e->data; + u->pass_all_alive |= pass_all_alive; + uct_pondering_stop(u); + uct_genmove_setup(u, b, color); + + /* Start the Monte Carlo Tree Search! */ + int base_playouts = u->t->root->u.playouts; + int played_games = uct_search(u, b, ti, color, u->t, false); + + coord_t best_coord; + struct tree_node *best; + best = uct_search_result(u, b, color, u->pass_all_alive, played_games, base_playouts, &best_coord); + + if (UDEBUGL(2)) { + double time = time_now() - start_time + 0.000001; /* avoid divide by zero */ + fprintf(stderr, "genmove in %0.2fs (%d games/s, %d games/s/thread)\n", + time, (int)(played_games/time), (int)(played_games/time/u->threads)); + } + + uct_progress_status(u, u->t, color, played_games, &best_coord); + + if (!best) { + /* Pass or resign. */ + if (is_pass(best_coord)) + u->initial_extra_komi = u->t->extra_komi; + reset_state(u); + return coord_copy(best_coord); + } + tree_promote_node(u->t, &best); + + /* After a pass, pondering is harmful for two reasons: + * (i) We might keep pondering even when the game is over. + * Of course this is the case for opponent resign as well. + * (ii) More importantly, the ownermap will get skewed since + * the UCT will start cutting off any playouts. */ + if (u->pondering_opt && !is_pass(node_coord(best))) { + uct_pondering_start(u, b, u->t, stone_other(color)); + } + return coord_copy(best_coord); +} + + +bool +uct_gentbook(struct engine *e, struct board *b, struct time_info *ti, enum stone color) +{ + struct uct *u = e->data; + if (!u->t) uct_prepare_move(u, b, color); + assert(u->t); + + if (ti->dim == TD_GAMES) { + /* Don't count in games that already went into the tbook. */ + ti->len.games += u->t->root->u.playouts; + } + uct_search(u, b, ti, color, u->t, true); + + assert(ti->dim == TD_GAMES); + tree_save(u->t, b, ti->len.games / 100); + + return true; +} + +void +uct_dumptbook(struct engine *e, struct board *b, enum stone color) +{ + struct uct *u = e->data; + struct tree *t = tree_init(b, color, u->fast_alloc ? u->max_tree_size : 0, + u->max_pruned_size, u->pruning_threshold, u->local_tree_aging, 0); + tree_load(t, b); + tree_dump(t, 0); + tree_done(t); +} + + +floating_t +uct_evaluate_one(struct engine *e, struct board *b, struct time_info *ti, coord_t c, enum stone color) +{ + struct uct *u = e->data; + + struct board b2; + board_copy(&b2, b); + struct move m = { c, color }; + int res = board_play(&b2, &m); + if (res < 0) + return NAN; + color = stone_other(color); + + if (u->t) reset_state(u); + uct_prepare_move(u, &b2, color); + assert(u->t); + + floating_t bestval; + uct_search(u, &b2, ti, color, u->t, true); + struct tree_node *best = u->policy->choose(u->policy, u->t->root, &b2, color, resign); + if (!best) { + bestval = NAN; // the opponent has no reply! + } else { + bestval = tree_node_get_value(u->t, 1, best->u.value); + } + + reset_state(u); // clean our junk + + return isnan(bestval) ? NAN : 1.0f - bestval; +} + +void +uct_evaluate(struct engine *e, struct board *b, struct time_info *ti, floating_t *vals, enum stone color) +{ + for (int i = 0; i < b->flen; i++) { + if (is_pass(b->f[i])) + vals[i] = NAN; + else + vals[i] = uct_evaluate_one(e, b, ti, b->f[i], color); + } +} + + +struct uct * +uct_state_init(char *arg, struct board *b) +{ + struct uct *u = calloc2(1, sizeof(struct uct)); + bool pat_setup = false; + + u->debug_level = debug_level; + u->reportfreq = 10000; + u->gamelen = MC_GAMELEN; + u->resign_threshold = 0.2; + u->sure_win_threshold = 0.95; + u->mercymin = 0; + u->significant_threshold = 50; + u->expand_p = 8; + u->dumpthres = 1000; + u->playout_amaf = true; + u->amaf_prior = false; + u->max_tree_size = 1408ULL * 1048576; + u->fast_alloc = true; + u->pruning_threshold = 0; + + u->threads = 1; + u->thread_model = TM_TREEVL; + u->virtual_loss = 1; + + u->pondering_opt = true; + + u->fuseki_end = 20; // max time at 361*20% = 72 moves (our 36th move, still 99 to play) + u->yose_start = 40; // (100-40-25)*361/100/2 = 63 moves still to play by us then + u->bestr_ratio = 0.02; + // 2.5 is clearly too much, but seems to compensate well for overly stern time allocations. + // TODO: Further tuning and experiments with better time allocation schemes. + u->best2_ratio = 2.5; + // Higher values of max_maintime_ratio sometimes cause severe time trouble in tournaments + // It might be necessary to reduce it to 1.5 on large board, but more tuning is needed. + u->max_maintime_ratio = 2.0; + + u->val_scale = 0; u->val_points = 40; + u->dynkomi_interval = 1000; + u->dynkomi_mask = S_BLACK | S_WHITE; + + u->tenuki_d = 4; + u->local_tree_aging = 80; + u->local_tree_depth_decay = 1.5; + u->local_tree_eval = LTE_ROOT; + u->local_tree_neival = true; + + u->max_slaves = -1; + u->slave_index = -1; + u->stats_delay = 0.01; // 10 ms + u->shared_levels = 1; + + u->plugins = pluginset_init(b); + + u->jdict = joseki_load(b->size); + + if (arg) { + char *optspec, *next = arg; + while (*next) { + optspec = next; + next += strcspn(next, ","); + if (*next) { *next++ = 0; } else { *next = 0; } + + char *optname = optspec; + char *optval = strchr(optspec, '='); + if (optval) *optval++ = 0; + + /** Basic options */ + + if (!strcasecmp(optname, "debug")) { + if (optval) + u->debug_level = atoi(optval); + else + u->debug_level++; + } else if (!strcasecmp(optname, "reporting") && optval) { + /* The format of output for detailed progress + * information (such as current best move and + * its value, etc.). */ + if (!strcasecmp(optval, "text")) { + /* Plaintext traditional output. */ + u->reporting = UR_TEXT; + } else if (!strcasecmp(optval, "json")) { + /* JSON output. Implies debug=0. */ + u->reporting = UR_JSON; + u->debug_level = 0; + } else if (!strcasecmp(optval, "jsonbig")) { + /* JSON output, but much more detailed. + * Implies debug=0. */ + u->reporting = UR_JSON_BIG; + u->debug_level = 0; + } else { + fprintf(stderr, "UCT: Invalid reporting format %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "reportfreq") && optval) { + /* The progress information line will be shown + * every simulations. */ + u->reportfreq = atoi(optval); + } else if (!strcasecmp(optname, "dumpthres") && optval) { + /* When dumping the UCT tree on output, include + * nodes with at least this many playouts. + * (This value is re-scaled "intelligently" + * in case of very large trees.) */ + u->dumpthres = atoi(optval); + } else if (!strcasecmp(optname, "resign_threshold") && optval) { + /* Resign when this ratio of games is lost + * after GJ_MINGAMES sample is taken. */ + u->resign_threshold = atof(optval); + } else if (!strcasecmp(optname, "sure_win_threshold") && optval) { + /* Stop reading when this ratio of games is won + * after PLAYOUT_EARLY_BREAK_MIN sample is + * taken. (Prevents stupid time losses, + * friendly to human opponents.) */ + u->sure_win_threshold = atof(optval); + } else if (!strcasecmp(optname, "force_seed") && optval) { + /* Set RNG seed at the tree setup. */ + u->force_seed = atoi(optval); + } else if (!strcasecmp(optname, "no_tbook")) { + /* Disable UCT opening tbook. */ + u->no_tbook = true; + } else if (!strcasecmp(optname, "pass_all_alive")) { + /* Whether to consider passing only after all + * dead groups were removed from the board; + * this is like all genmoves are in fact + * kgs-genmove_cleanup. */ + u->pass_all_alive = !optval || atoi(optval); + } else if (!strcasecmp(optname, "allow_losing_pass")) { + /* Whether to consider passing in a clear + * but losing situation, to be scored as a loss + * for us. */ + u->allow_losing_pass = !optval || atoi(optval); + } else if (!strcasecmp(optname, "territory_scoring")) { + /* Use territory scoring (default is area scoring). + * An explicit kgs-rules command overrides this. */ + u->territory_scoring = !optval || atoi(optval); + } else if (!strcasecmp(optname, "stones_only")) { + /* Do not count eyes. Nice to teach go to kids. + * http://strasbourg.jeudego.org/regle_strasbourgeoise.htm */ + b->rules = RULES_STONES_ONLY; + u->pass_all_alive = true; + } else if (!strcasecmp(optname, "banner") && optval) { + /* Additional banner string. This must come as the + * last engine parameter. */ + if (*next) *--next = ','; + u->banner = strdup(optval); + break; + } else if (!strcasecmp(optname, "plugin") && optval) { + /* Load an external plugin; filename goes before the colon, + * extra arguments after the colon. */ + char *pluginarg = strchr(optval, ':'); + if (pluginarg) + *pluginarg++ = 0; + plugin_load(u->plugins, optval, pluginarg); + + /** UCT behavior and policies */ + + } else if ((!strcasecmp(optname, "policy") + /* Node selection policy. ucb1amaf is the + * default policy implementing RAVE, while + * ucb1 is the simple exploration/exploitation + * policy. Policies can take further extra + * options. */ + || !strcasecmp(optname, "random_policy")) && optval) { + /* A policy to be used randomly with small + * chance instead of the default policy. */ + char *policyarg = strchr(optval, ':'); + struct uct_policy **p = !strcasecmp(optname, "policy") ? &u->policy : &u->random_policy; + if (policyarg) + *policyarg++ = 0; + if (!strcasecmp(optval, "ucb1")) { + *p = policy_ucb1_init(u, policyarg); + } else if (!strcasecmp(optval, "ucb1amaf")) { + *p = policy_ucb1amaf_init(u, policyarg, b); + } else { + fprintf(stderr, "UCT: Invalid tree policy %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "playout") && optval) { + /* Random simulation (playout) policy. + * moggy is the default policy with large + * amount of domain-specific knowledge and + * heuristics. light is a simple uniformly + * random move selection policy. */ + char *playoutarg = strchr(optval, ':'); + if (playoutarg) + *playoutarg++ = 0; + if (!strcasecmp(optval, "moggy")) { + u->playout = playout_moggy_init(playoutarg, b, u->jdict); + } else if (!strcasecmp(optval, "light")) { + u->playout = playout_light_init(playoutarg, b); + } else { + fprintf(stderr, "UCT: Invalid playout policy %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "prior") && optval) { + /* Node priors policy. When expanding a node, + * it will seed node values heuristically + * (most importantly, based on playout policy + * opinion, but also with regard to other + * things). See uct/prior.c for details. + * Use prior=eqex=0 to disable priors. */ + u->prior = uct_prior_init(optval, b, u); + } else if (!strcasecmp(optname, "mercy") && optval) { + /* Minimal difference of black/white captures + * to stop playout - "Mercy Rule". Speeds up + * hopeless playouts at the expense of some + * accuracy. */ + u->mercymin = atoi(optval); + } else if (!strcasecmp(optname, "gamelen") && optval) { + /* Maximum length of single simulation + * in moves. */ + u->gamelen = atoi(optval); + } else if (!strcasecmp(optname, "expand_p") && optval) { + /* Expand UCT nodes after it has been + * visited this many times. */ + u->expand_p = atoi(optval); + } else if (!strcasecmp(optname, "random_policy_chance") && optval) { + /* If specified (N), with probability 1/N, random_policy policy + * descend is used instead of main policy descend; useful + * if specified policy (e.g. UCB1AMAF) can make unduly biased + * choices sometimes, you can fall back to e.g. + * random_policy=UCB1. */ + u->random_policy_chance = atoi(optval); + + /** General AMAF behavior */ + /* (Only relevant if the policy supports AMAF. + * More variables can be tuned as policy + * parameters.) */ + + } else if (!strcasecmp(optname, "playout_amaf")) { + /* Whether to include random playout moves in + * AMAF as well. (Otherwise, only tree moves + * are included in AMAF. Of course makes sense + * only in connection with an AMAF policy.) */ + /* with-without: 55.5% (+-4.1) */ + if (optval && *optval == '0') + u->playout_amaf = false; + else + u->playout_amaf = true; + } else if (!strcasecmp(optname, "playout_amaf_cutoff") && optval) { + /* Keep only first N% of playout stage AMAF + * information. */ + u->playout_amaf_cutoff = atoi(optval); + } else if (!strcasecmp(optname, "amaf_prior") && optval) { + /* In node policy, consider prior values + * part of the real result term or part + * of the AMAF term? */ + u->amaf_prior = atoi(optval); + + /** Performance and memory management */ + + } else if (!strcasecmp(optname, "threads") && optval) { + /* By default, Pachi will run with only single + * tree search thread! */ + u->threads = atoi(optval); + } else if (!strcasecmp(optname, "thread_model") && optval) { + if (!strcasecmp(optval, "tree")) { + /* Tree parallelization - all threads + * grind on the same tree. */ + u->thread_model = TM_TREE; + u->virtual_loss = 0; + } else if (!strcasecmp(optval, "treevl")) { + /* Tree parallelization, but also + * with virtual losses - this discou- + * rages most threads choosing the + * same tree branches to read. */ + u->thread_model = TM_TREEVL; + } else { + fprintf(stderr, "UCT: Invalid thread model %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "virtual_loss") && optval) { + /* Number of virtual losses added before evaluating a node. */ + u->virtual_loss = atoi(optval); + } else if (!strcasecmp(optname, "pondering")) { + /* Keep searching even during opponent's turn. */ + u->pondering_opt = !optval || atoi(optval); + } else if (!strcasecmp(optname, "max_tree_size") && optval) { + /* Maximum amount of memory [MiB] consumed by the move tree. + * For fast_alloc it includes the temp tree used for pruning. + * Default is 3072 (3 GiB). */ + u->max_tree_size = atol(optval) * 1048576; + } else if (!strcasecmp(optname, "fast_alloc")) { + u->fast_alloc = !optval || atoi(optval); + } else if (!strcasecmp(optname, "pruning_threshold") && optval) { + /* Force pruning at beginning of a move if the tree consumes + * more than this [MiB]. Default is 10% of max_tree_size. + * Increase to reduce pruning time overhead if memory is plentiful. + * This option is meaningful only for fast_alloc. */ + u->pruning_threshold = atol(optval) * 1048576; + + /** Time control */ + + } else if (!strcasecmp(optname, "best2_ratio") && optval) { + /* If set, prolong simulating while + * first_best/second_best playouts ratio + * is less than best2_ratio. */ + u->best2_ratio = atof(optval); + } else if (!strcasecmp(optname, "bestr_ratio") && optval) { + /* If set, prolong simulating while + * best,best_best_child values delta + * is more than bestr_ratio. */ + u->bestr_ratio = atof(optval); + } else if (!strcasecmp(optname, "max_maintime_ratio") && optval) { + /* If set and while not in byoyomi, prolong simulating no more than + * max_maintime_ratio times the normal desired thinking time. */ + u->max_maintime_ratio = atof(optval); + } else if (!strcasecmp(optname, "fuseki_end") && optval) { + /* At the very beginning it's not worth thinking + * too long because the playout evaluations are + * very noisy. So gradually increase the thinking + * time up to maximum when fuseki_end percent + * of the board has been played. + * This only applies if we are not in byoyomi. */ + u->fuseki_end = atoi(optval); + } else if (!strcasecmp(optname, "yose_start") && optval) { + /* When yose_start percent of the board has been + * played, or if we are in byoyomi, stop spending + * more time and spread the remaining time + * uniformly. + * Between fuseki_end and yose_start, we spend + * a constant proportion of the remaining time + * on each move. (yose_start should actually + * be much earlier than when real yose start, + * but "yose" is a good short name to convey + * the idea.) */ + u->yose_start = atoi(optval); + + /** Dynamic komi */ + + } else if (!strcasecmp(optname, "dynkomi") && optval) { + /* Dynamic komi approach; there are multiple + * ways to adjust komi dynamically throughout + * play. We currently support two: */ + char *dynkomiarg = strchr(optval, ':'); + if (dynkomiarg) + *dynkomiarg++ = 0; + if (!strcasecmp(optval, "none")) { + u->dynkomi = uct_dynkomi_init_none(u, dynkomiarg, b); + } else if (!strcasecmp(optval, "linear")) { + /* You should set dynkomi_mask=1 or a very low + * handicap_value for white. */ + u->dynkomi = uct_dynkomi_init_linear(u, dynkomiarg, b); + } else if (!strcasecmp(optval, "adaptive")) { + /* There are many more knobs to + * crank - see uct/dynkomi.c. */ + u->dynkomi = uct_dynkomi_init_adaptive(u, dynkomiarg, b); + } else { + fprintf(stderr, "UCT: Invalid dynkomi mode %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "dynkomi_mask") && optval) { + /* Bitmask of colors the player must be + * for dynkomi be applied; the default dynkomi_mask=3 allows + * dynkomi even in games where Pachi is white. */ + u->dynkomi_mask = atoi(optval); + } else if (!strcasecmp(optname, "dynkomi_interval") && optval) { + /* If non-zero, re-adjust dynamic komi + * throughout a single genmove reading, + * roughly every N simulations. */ + /* XXX: Does not work with tree + * parallelization. */ + u->dynkomi_interval = atoi(optval); + } else if (!strcasecmp(optname, "extra_komi") && optval) { + /* Initial dynamic komi settings. This + * is useful for the adaptive dynkomi + * policy as the value to start with + * (this is NOT kept fixed) in case + * there is not enough time in the search + * to adjust the value properly (e.g. the + * game was interrupted). */ + u->initial_extra_komi = atof(optval); + + /** Node value result scaling */ + + } else if (!strcasecmp(optname, "val_scale") && optval) { + /* How much of the game result value should be + * influenced by win size. Zero means it isn't. */ + u->val_scale = atof(optval); + } else if (!strcasecmp(optname, "val_points") && optval) { + /* Maximum size of win to be scaled into game + * result value. Zero means boardsize^2. */ + u->val_points = atoi(optval) * 2; // result values are doubled + } else if (!strcasecmp(optname, "val_extra")) { + /* If false, the score coefficient will be simply + * added to the value, instead of scaling the result + * coefficient because of it. */ + u->val_extra = !optval || atoi(optval); + } else if (!strcasecmp(optname, "val_byavg")) { + /* If true, the score included in the value will + * be relative to average score in the current + * search episode inst. of jigo. */ + u->val_byavg = !optval || atoi(optval); + } else if (!strcasecmp(optname, "val_bytemp")) { + /* If true, the value scaling coefficient + * is different based on value extremity + * (dist. from 0.5), linear between + * val_bytemp_min, val_scale. */ + u->val_bytemp = !optval || atoi(optval); + } else if (!strcasecmp(optname, "val_bytemp_min") && optval) { + /* Minimum val_scale in case of val_bytemp. */ + u->val_bytemp_min = atof(optval); + + /** Local trees */ + /* (Purely experimental. Does not work - yet!) */ + + } else if (!strcasecmp(optname, "local_tree")) { + /* Whether to bias exploration by local tree values. */ + u->local_tree = !optval || atoi(optval); + } else if (!strcasecmp(optname, "tenuki_d") && optval) { + /* Tenuki distance at which to break the local tree. */ + u->tenuki_d = atoi(optval); + if (u->tenuki_d > TREE_NODE_D_MAX + 1) { + fprintf(stderr, "uct: tenuki_d must not be larger than TREE_NODE_D_MAX+1 %d\n", TREE_NODE_D_MAX + 1); + exit(1); + } + } else if (!strcasecmp(optname, "local_tree_aging") && optval) { + /* How much to reduce local tree values between moves. */ + u->local_tree_aging = atof(optval); + } else if (!strcasecmp(optname, "local_tree_depth_decay") && optval) { + /* With value x>0, during the descent the node + * contributes 1/x^depth playouts in + * the local tree. I.e., with x>1, nodes more + * distant from local situation contribute more + * than nodes near the root. */ + u->local_tree_depth_decay = atof(optval); + } else if (!strcasecmp(optname, "local_tree_allseq")) { + /* If disabled, only complete sequences are stored + * in the local tree. If this is on, also + * subsequences starting at each move are stored. */ + u->local_tree_allseq = !optval || atoi(optval); + } else if (!strcasecmp(optname, "local_tree_neival")) { + /* If disabled, local node value is not + * computed just based on terminal status + * of the coordinate, but also its neighbors. */ + u->local_tree_neival = !optval || atoi(optval); + } else if (!strcasecmp(optname, "local_tree_eval")) { + /* How is the value inserted in the local tree + * determined. */ + if (!strcasecmp(optval, "root")) + /* All moves within a tree branch are + * considered wrt. their merit + * reaching tachtical goal of making + * the first move in the branch + * survive. */ + u->local_tree_eval = LTE_ROOT; + else if (!strcasecmp(optval, "each")) + /* Each move is considered wrt. + * its own survival. */ + u->local_tree_eval = LTE_EACH; + else if (!strcasecmp(optval, "total")) + /* The tactical goal is the survival + * of all the moves of my color and + * non-survival of all the opponent + * moves. Local values (and their + * inverses) are averaged. */ + u->local_tree_eval = LTE_TOTAL; + else { + fprintf(stderr, "uct: unknown local_tree_eval %s\n", optval); + exit(1); + } + } else if (!strcasecmp(optname, "local_tree_rootchoose")) { + /* If disabled, only moves within the local + * tree branch are considered; the values + * of the branch roots (i.e. root children) + * are ignored. This may make sense together + * with eval!=each, we consider only moves + * that influence the goal, not the "rating" + * of the goal itself. (The real solution + * will be probably using criticality to pick + * local tree branches.) */ + u->local_tree_rootchoose = !optval || atoi(optval); + + /** Other heuristics */ + } else if (!strcasecmp(optname, "patterns")) { + /* Load pattern database. Various modules + * (priors, policies etc.) may make use + * of this database. They will request + * it automatically in that case, but you + * can use this option to tweak the pattern + * parameters. */ + patterns_init(&u->pat, optval, false, true); + u->want_pat = pat_setup = true; + } else if (!strcasecmp(optname, "significant_threshold") && optval) { + /* Some heuristics (XXX: none in mainline) rely + * on the knowledge of the last "significant" + * node in the descent. Such a node is + * considered reasonably trustworthy to carry + * some meaningful information in the values + * of the node and its children. */ + u->significant_threshold = atoi(optval); + + /** Distributed engine slaves setup */ + + } else if (!strcasecmp(optname, "slave")) { + /* Act as slave for the distributed engine. */ + u->slave = !optval || atoi(optval); + } else if (!strcasecmp(optname, "slave_index") && optval) { + /* Optional index if per-slave behavior is desired. + * Must be given as index/max */ + u->slave_index = atoi(optval); + char *p = strchr(optval, '/'); + if (p) u->max_slaves = atoi(++p); + } else if (!strcasecmp(optname, "shared_nodes") && optval) { + /* Share at most shared_nodes between master and slave at each genmoves. + * Must use the same value in master and slaves. */ + u->shared_nodes = atoi(optval); + } else if (!strcasecmp(optname, "shared_levels") && optval) { + /* Share only nodes of level <= shared_levels. */ + u->shared_levels = atoi(optval); + } else if (!strcasecmp(optname, "stats_hbits") && optval) { + /* Set hash table size to 2^stats_hbits for the shared stats. */ + u->stats_hbits = atoi(optval); + } else if (!strcasecmp(optname, "stats_delay") && optval) { + /* How long to wait in slave for initial stats to build up before + * replying to the genmoves command (in ms) */ + u->stats_delay = 0.001 * atof(optval); + + /** Presets */ + + } else if (!strcasecmp(optname, "maximize_score")) { + /* A combination of settings that will make + * Pachi try to maximize his points (instead + * of playing slack yose) or minimize his loss + * (and proceed to counting even when losing). */ + /* Please note that this preset might be + * somewhat weaker than normal Pachi, and the + * score maximization is approximate; point size + * of win/loss still should not be used to judge + * strength of Pachi or the opponent. */ + /* See README for some further notes. */ + if (!optval || atoi(optval)) { + /* Allow scoring a lost game. */ + u->allow_losing_pass = true; + /* Make Pachi keep his calm when losing + * and/or maintain winning marging. */ + /* Do not play games that are losing + * by too much. */ + /* XXX: komi_ratchet_age=40000 is necessary + * with losing_komi_ratchet, but 40000 + * is somewhat arbitrary value. */ + char dynkomi_args[] = "losing_komi_ratchet:komi_ratchet_age=60000:no_komi_at_game_end=0:max_losing_komi=30"; + u->dynkomi = uct_dynkomi_init_adaptive(u, dynkomi_args, b); + /* XXX: Values arbitrary so far. */ + /* XXX: Also, is bytemp sensible when + * combined with dynamic komi?! */ + u->val_scale = 0.01; + u->val_bytemp = true; + u->val_bytemp_min = 0.001; + u->val_byavg = true; + } + + } else { + fprintf(stderr, "uct: Invalid engine argument %s or missing value\n", optname); + exit(1); + } + } + } + + if (!u->policy) + u->policy = policy_ucb1amaf_init(u, NULL, b); + + if (!!u->random_policy_chance ^ !!u->random_policy) { + fprintf(stderr, "uct: Only one of random_policy and random_policy_chance is set\n"); + exit(1); + } + + if (!u->local_tree) { + /* No ltree aging. */ + u->local_tree_aging = 1.0f; + } + + if (u->fast_alloc) { + if (u->pruning_threshold < u->max_tree_size / 10) + u->pruning_threshold = u->max_tree_size / 10; + if (u->pruning_threshold > u->max_tree_size / 2) + u->pruning_threshold = u->max_tree_size / 2; + + /* Limit pruning temp space to 20% of memory. Beyond this we discard + * the nodes and recompute them at the next move if necessary. */ + u->max_pruned_size = u->max_tree_size / 5; + u->max_tree_size -= u->max_pruned_size; + } else { + /* Reserve 5% memory in case the background free() are slower + * than the concurrent allocations. */ + u->max_tree_size -= u->max_tree_size / 20; + } + + if (!u->prior) + u->prior = uct_prior_init(NULL, b, u); + + if (!u->playout) + u->playout = playout_moggy_init(NULL, b, u->jdict); + if (!u->playout->debug_level) + u->playout->debug_level = u->debug_level; + + if (u->want_pat && !pat_setup) + patterns_init(&u->pat, NULL, false, true); + + u->ownermap.map = malloc2(board_size2(b) * sizeof(u->ownermap.map[0])); + + if (u->slave) { + if (!u->stats_hbits) u->stats_hbits = DEFAULT_STATS_HBITS; + if (!u->shared_nodes) u->shared_nodes = DEFAULT_SHARED_NODES; + assert(u->shared_levels * board_bits2(b) <= 8 * (int)sizeof(path_t)); + } + + if (!u->dynkomi) + u->dynkomi = board_small(b) ? uct_dynkomi_init_none(u, NULL, b) + : uct_dynkomi_init_linear(u, NULL, b); + + /* Some things remain uninitialized for now - the opening tbook + * is not loaded and the tree not set up. */ + /* This will be initialized in setup_state() at the first move + * received/requested. This is because right now we are not aware + * about any komi or handicap setup and such. */ + + return u; +} + +struct engine * +engine_uct_init(char *arg, struct board *b) +{ + struct uct *u = uct_state_init(arg, b); + struct engine *e = calloc2(1, sizeof(struct engine)); + e->name = "UCT"; + e->printhook = uct_printhook_ownermap; + e->notify_play = uct_notify_play; + e->chat = uct_chat; + e->undo = uct_undo; + e->result = uct_result; + e->genmove = uct_genmove; + e->genmoves = uct_genmoves; + e->evaluate = uct_evaluate; + e->dead_group_list = uct_dead_group_list; + e->done = uct_done; + e->data = u; + if (u->slave) + e->notify = uct_notify; + + const char banner[] = "If you believe you have won but I am still playing, " + "please help me understand by capturing all dead stones. " + "Anyone can send me 'winrate' in private chat to get my assessment of the position."; + if (!u->banner) u->banner = ""; + e->comment = malloc2(sizeof(banner) + strlen(u->banner) + 1); + sprintf(e->comment, "%s %s", banner, u->banner); + + return e; +} diff --git a/jni/pachi/uct/uct.h b/jni/pachi/uct/uct.h new file mode 100644 index 0000000..50c5a70 --- /dev/null +++ b/jni/pachi/uct/uct.h @@ -0,0 +1,14 @@ +#ifndef PACHI_UCT_UCT_H +#define PACHI_UCT_UCT_H + +#include "engine.h" +#include "move.h" + +struct engine *engine_uct_init(char *arg, struct board *b); + +struct board; +struct time_info; +bool uct_gentbook(struct engine *e, struct board *b, struct time_info *ti, enum stone color); +void uct_dumptbook(struct engine *e, struct board *b, enum stone color); + +#endif diff --git a/jni/pachi/uct/walk.c b/jni/pachi/uct/walk.c new file mode 100644 index 0000000..6a3e7d0 --- /dev/null +++ b/jni/pachi/uct/walk.c @@ -0,0 +1,633 @@ +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#include "debug.h" +#include "board.h" +#include "move.h" +#include "playout.h" +#include "probdist.h" +#include "random.h" +#include "tactics/util.h" +#include "uct/dynkomi.h" +#include "uct/internal.h" +#include "uct/search.h" +#include "uct/tree.h" +#include "uct/uct.h" +#include "uct/walk.h" + +#define DESCENT_DLEN 512 + + +void +uct_progress_text(struct uct *u, struct tree *t, enum stone color, int playouts) +{ + if (!UDEBUGL(0)) + return; + + /* Best move */ + struct tree_node *best = u->policy->choose(u->policy, t->root, t->board, color, resign); + if (!best) { + fprintf(stderr, "... No moves left\n"); + return; + } + fprintf(stderr, "[%d] ", playouts); + fprintf(stderr, "best %f ", tree_node_get_value(t, 1, best->u.value)); + + /* Dynamic komi */ + if (t->use_extra_komi) + fprintf(stderr, "xkomi %.1f ", t->extra_komi); + + /* Best sequence */ + fprintf(stderr, "| seq "); + for (int depth = 0; depth < 4; depth++) { + if (best && best->u.playouts >= 25) { + fprintf(stderr, "%3s ", coord2sstr(node_coord(best), t->board)); + best = u->policy->choose(u->policy, best, t->board, color, resign); + } else { + fprintf(stderr, " "); + } + } + + /* Best candidates */ + fprintf(stderr, "| can "); + int cans = 4; + struct tree_node *can[cans]; + memset(can, 0, sizeof(can)); + best = t->root->children; + while (best) { + int c = 0; + while ((!can[c] || best->u.playouts > can[c]->u.playouts) && ++c < cans); + for (int d = 0; d < c; d++) can[d] = can[d + 1]; + if (c > 0) can[c - 1] = best; + best = best->sibling; + } + while (--cans >= 0) { + if (can[cans]) { + fprintf(stderr, "%3s(%.3f) ", + coord2sstr(node_coord(can[cans]), t->board), + tree_node_get_value(t, 1, can[cans]->u.value)); + } else { + fprintf(stderr, " "); + } + } + + fprintf(stderr, "\n"); +} + +void +uct_progress_json(struct uct *u, struct tree *t, enum stone color, int playouts, coord_t *final, bool big) +{ + /* Prefix indicating JSON line. */ + fprintf(stderr, "{\"%s\": {", final ? "move" : "frame"); + + /* Plaout count */ + fprintf(stderr, "\"playouts\": %d", playouts); + + /* Dynamic komi */ + if (t->use_extra_komi) + fprintf(stderr, ", \"extrakomi\": %.1f", t->extra_komi); + + if (final) { + /* Final move choice */ + fprintf(stderr, ", \"choice\": {\"%s\"}", + coord2sstr(*final, t->board)); + } else { + struct tree_node *best = u->policy->choose(u->policy, t->root, t->board, color, resign); + if (best) { + /* Best move */ + fprintf(stderr, ", \"best\": {\"%s\": %f}", + coord2sstr(best->coord, t->board), + tree_node_get_value(t, 1, best->u.value)); + } + } + + /* Best candidates */ + int cans = 4; + struct tree_node *can[cans]; + memset(can, 0, sizeof(can)); + struct tree_node *best = t->root->children; + while (best) { + int c = 0; + while ((!can[c] || best->u.playouts > can[c]->u.playouts) && ++c < cans); + for (int d = 0; d < c; d++) can[d] = can[d + 1]; + if (c > 0) can[c - 1] = best; + best = best->sibling; + } + fprintf(stderr, ", \"can\": ["); + while (--cans >= 0) { + if (!can[cans]) break; + /* Best sequence */ + fprintf(stderr, "%s[", cans < 3 ? ", " : ""); + best = can[cans]; + for (int depth = 0; depth < 4; depth++) { + if (!best || best->u.playouts < 25) break; + fprintf(stderr, "%s{\"%s\":%.3f}", depth > 0 ? "," : "", + coord2sstr(best->coord, t->board), + tree_node_get_value(t, 1, best->u.value)); + best = u->policy->choose(u->policy, best, t->board, color, resign); + } + fprintf(stderr, "]"); + } + fprintf(stderr, "]"); + + if (big) { + /* Average score. */ + if (t->avg_score.playouts > 0) + fprintf(stderr, ", \"avg\": {\"score\": %.3f}", t->avg_score.value); + /* Per-intersection information. */ + fprintf(stderr, ", \"boards\": {"); + /* Position coloring information. */ + fprintf(stderr, "\"colors\": ["); + int f = 0; + foreach_point(t->board) { + if (board_at(t->board, c) == S_OFFBOARD) continue; + fprintf(stderr, "%s%d", f++ > 0 ? "," : "", board_at(t->board, c)); + } foreach_point_end; + fprintf(stderr, "]"); + /* Ownership statistics. Value (0..1000) for each possible + * point describes likelihood of this point becoming black. + * Normally, white rate is 1000-value; exception are possible + * seki points, but these should be rare. */ + fprintf(stderr, ", \"territory\": ["); + f = 0; + foreach_point(t->board) { + if (board_at(t->board, c) == S_OFFBOARD) continue; + int rate = u->ownermap.map[c][S_BLACK] * 1000 / u->ownermap.playouts; + fprintf(stderr, "%s%d", f++ > 0 ? "," : "", rate); + } foreach_point_end; + fprintf(stderr, "]"); + fprintf(stderr, "}"); + } + + fprintf(stderr, "}}\n"); +} + +void +uct_progress_status(struct uct *u, struct tree *t, enum stone color, int playouts, coord_t *final) +{ + switch (u->reporting) { + case UR_TEXT: + uct_progress_text(u, t, color, playouts); + break; + case UR_JSON: + case UR_JSON_BIG: + uct_progress_json(u, t, color, playouts, final, + u->reporting == UR_JSON_BIG); + break; + default: assert(0); + } +} + + +static inline void +record_amaf_move(struct playout_amafmap *amaf, coord_t coord, bool is_ko_capture) +{ + assert(amaf->gamelen < MAX_GAMELEN); + amaf->is_ko_capture[amaf->gamelen] = is_ko_capture; + amaf->game[amaf->gamelen++] = coord; +} + + +struct uct_playout_callback { + struct uct *uct; + struct tree *tree; + struct tree_node *lnode; +}; + + +static coord_t +uct_playout_hook(struct playout_policy *playout, struct playout_setup *setup, struct board *b, enum stone color, int mode) +{ + /* XXX: This is used in some non-master branches. */ + return pass; +} + +static coord_t +uct_playout_prepolicy(struct playout_policy *playout, struct playout_setup *setup, struct board *b, enum stone color) +{ + return uct_playout_hook(playout, setup, b, color, 0); +} + +static coord_t +uct_playout_postpolicy(struct playout_policy *playout, struct playout_setup *setup, struct board *b, enum stone color) +{ + return uct_playout_hook(playout, setup, b, color, 1); +} + + +static int +uct_leaf_node(struct uct *u, struct board *b, enum stone player_color, + struct playout_amafmap *amaf, + struct uct_descent *descent, int *dlen, + struct tree_node *significant[2], + struct tree *t, struct tree_node *n, enum stone node_color, + char *spaces) +{ + enum stone next_color = stone_other(node_color); + int parity = (next_color == player_color ? 1 : -1); + + if (UDEBUGL(7)) + fprintf(stderr, "%s*-- UCT playout #%d start [%s] %f\n", + spaces, n->u.playouts, coord2sstr(node_coord(n), t->board), + tree_node_get_value(t, -parity, n->u.value)); + + struct uct_playout_callback upc = { + .uct = u, + .tree = t, + /* TODO: Don't necessarily restart the sequence walk when + * entering playout. */ + .lnode = NULL, + }; + + struct playout_setup ps = { + .gamelen = u->gamelen, + .mercymin = u->mercymin, + .prepolicy_hook = uct_playout_prepolicy, + .postpolicy_hook = uct_playout_postpolicy, + .hook_data = &upc, + }; + int result = play_random_game(&ps, b, next_color, + u->playout_amaf ? amaf : NULL, + &u->ownermap, u->playout); + if (next_color == S_WHITE) { + /* We need the result from black's perspective. */ + result = - result; + } + if (UDEBUGL(7)) + fprintf(stderr, "%s -- [%d..%d] %s random playout result %d\n", + spaces, player_color, next_color, coord2sstr(node_coord(n), t->board), result); + + return result; +} + +static floating_t +scale_value(struct uct *u, struct board *b, enum stone node_color, struct tree_node *significant[2], int result) +{ + floating_t rval = result > 0 ? 1.0 : result < 0 ? 0.0 : 0.5; + if (u->val_scale && result != 0) { + if (u->val_byavg) { + if (u->t->avg_score.playouts < 50) + return rval; + result -= u->t->avg_score.value * 2; + } + + double scale = u->val_scale; + if (u->val_bytemp) { + /* xvalue is 0 at 0.5, 1 at 0 or 1 */ + /* No correction for parity necessary. */ + double xvalue = significant[node_color - 1] ? fabs(significant[node_color - 1]->u.value - 0.5) * 2 : 0; + scale = u->val_bytemp_min + (u->val_scale - u->val_bytemp_min) * xvalue; + } + + int vp = u->val_points; + if (!vp) { + vp = board_size(b) - 1; vp *= vp; vp *= 2; + } + + floating_t sval = (floating_t) abs(result) / vp; + sval = sval > 1 ? 1 : sval; + if (result < 0) sval = 1 - sval; + if (u->val_extra) + rval += scale * sval; + else + rval = (1 - scale) * rval + scale * sval; + // fprintf(stderr, "score %d => sval %f, rval %f\n", result, sval, rval); + } + return rval; +} + +static double +local_value(struct uct *u, struct board *b, coord_t coord, enum stone color) +{ + /* Tactical evaluation of move @coord by color @color, given + * simulation end position @b. I.e., a move is tactically good + * if the resulting group stays on board until the game end. */ + /* We can also take into account surrounding stones, e.g. to + * encourage taking off external liberties during a semeai. */ + double val = board_local_value(u->local_tree_neival, b, coord, color); + return (color == S_WHITE) ? 1.f - val : val; +} + +static void +record_local_sequence(struct uct *u, struct tree *t, struct board *endb, + struct uct_descent *descent, int dlen, int di, + enum stone seq_color) +{ +#define LTREE_DEBUG if (UDEBUGL(6)) + + /* Ignore pass sequences. */ + if (is_pass(node_coord(descent[di].node))) + return; + + LTREE_DEBUG board_print(endb, stderr); + LTREE_DEBUG fprintf(stderr, "recording local %s sequence: ", + stone2str(seq_color)); + + /* Sequences starting deeper are less relevant in general. */ + int pval = LTREE_PLAYOUTS_MULTIPLIER; + if (u->local_tree && u->local_tree_depth_decay > 0) + pval = ((floating_t) pval) / pow(u->local_tree_depth_decay, di - 1); + if (!pval) { + LTREE_DEBUG fprintf(stderr, "too deep @%d\n", di); + return; + } + + /* Pick the right local tree root... */ + struct tree_node *lnode = seq_color == S_BLACK ? t->ltree_black : t->ltree_white; + lnode->u.playouts++; + + /* ...determine the sequence value... */ + double sval = 0.5; + if (u->local_tree_eval != LTE_EACH) { + sval = local_value(u, endb, node_coord(descent[di].node), seq_color); + LTREE_DEBUG fprintf(stderr, "(goal %s[%s %1.3f][%d]) ", + coord2sstr(node_coord(descent[di].node), t->board), + stone2str(seq_color), sval, descent[di].node->d); + + if (u->local_tree_eval == LTE_TOTAL) { + int di0 = di; + while (di < dlen && (di == di0 || descent[di].node->d < u->tenuki_d)) { + enum stone color = (di - di0) % 2 ? stone_other(seq_color) : seq_color; + double rval = local_value(u, endb, node_coord(descent[di].node), color); + if ((di - di0) % 2) + rval = 1 - rval; + sval += rval; + di++; + } + sval /= (di - di0 + 1); + di = di0; + } + } + + /* ...and record the sequence. */ + int di0 = di; + while (di < dlen && !is_pass(node_coord(descent[di].node)) + && (di == di0 || descent[di].node->d < u->tenuki_d)) { + enum stone color = (di - di0) % 2 ? stone_other(seq_color) : seq_color; + double rval; + if (u->local_tree_eval != LTE_EACH) + rval = sval; + else + rval = local_value(u, endb, node_coord(descent[di].node), color); + LTREE_DEBUG fprintf(stderr, "%s[%s %1.3f][%d] ", + coord2sstr(node_coord(descent[di].node), t->board), + stone2str(color), rval, descent[di].node->d); + lnode = tree_get_node(t, lnode, node_coord(descent[di++].node), true); + assert(lnode); + stats_add_result(&lnode->u, rval, pval); + } + + /* Add lnode for tenuki (pass) if we descended further. */ + if (di < dlen) { + double rval = u->local_tree_eval != LTE_EACH ? sval : 0.5; + LTREE_DEBUG fprintf(stderr, "pass "); + lnode = tree_get_node(t, lnode, pass, true); + assert(lnode); + stats_add_result(&lnode->u, rval, pval); + } + + LTREE_DEBUG fprintf(stderr, "\n"); +} + + +int +uct_playout(struct uct *u, struct board *b, enum stone player_color, struct tree *t) +{ + struct board b2; + board_copy(&b2, b); + + struct playout_amafmap amaf; + amaf.gamelen = amaf.game_baselen = 0; + + /* Walk the tree until we find a leaf, then expand it and do + * a random playout. */ + struct tree_node *n = t->root; + enum stone node_color = stone_other(player_color); + assert(node_color == t->root_color); + + /* Make sure the root node is expanded. */ + if (tree_leaf_node(n) && !__sync_lock_test_and_set(&n->is_expanded, 1)) + tree_expand_node(t, n, &b2, player_color, u, 1); + + /* Tree descent history. */ + /* XXX: This is somewhat messy since @n and descent[dlen-1].node are + * redundant. */ + struct uct_descent descent[DESCENT_DLEN]; + descent[0].node = n; descent[0].lnode = NULL; + int dlen = 1; + /* Total value of the sequence. */ + struct move_stats seq_value = { .playouts = 0 }; + /* The last "significant" node along the descent (i.e. node + * with higher than configured number of playouts). For black + * and white. */ + struct tree_node *significant[2] = { NULL, NULL }; + if (n->u.playouts >= u->significant_threshold) + significant[node_color - 1] = n; + + int result; + int pass_limit = (board_size(&b2) - 2) * (board_size(&b2) - 2) / 2; + int passes = is_pass(b->last_move.coord) && b->moves > 0; + + /* debug */ + static char spaces[] = "\0 "; + /* /debug */ + if (UDEBUGL(8)) + fprintf(stderr, "--- UCT walk with color %d\n", player_color); + + while (!tree_leaf_node(n) && passes < 2) { + spaces[dlen - 1] = ' '; spaces[dlen] = 0; + + + /*** Choose a node to descend to: */ + + /* Parity is chosen already according to the child color, since + * it is applied to children. */ + node_color = stone_other(node_color); + int parity = (node_color == player_color ? 1 : -1); + + assert(dlen < DESCENT_DLEN); + descent[dlen] = descent[dlen - 1]; + if (u->local_tree && (!descent[dlen].lnode || descent[dlen].node->d >= u->tenuki_d)) { + /* Start new local sequence. */ + /* Remember that node_color already holds color of the + * to-be-found child. */ + descent[dlen].lnode = node_color == S_BLACK ? t->ltree_black : t->ltree_white; + } + + if (!u->random_policy_chance || fast_random(u->random_policy_chance)) + u->policy->descend(u->policy, t, &descent[dlen], parity, b2.moves > pass_limit); + else + u->random_policy->descend(u->random_policy, t, &descent[dlen], parity, b2.moves > pass_limit); + + + /*** Perform the descent: */ + + if (descent[dlen].node->u.playouts >= u->significant_threshold) { + significant[node_color - 1] = descent[dlen].node; + } + + seq_value.playouts += descent[dlen].value.playouts; + seq_value.value += descent[dlen].value.value * descent[dlen].value.playouts; + n = descent[dlen++].node; + assert(n == t->root || n->parent); + if (UDEBUGL(7)) + fprintf(stderr, "%s+-- UCT sent us to [%s:%d] %d,%f\n", + spaces, coord2sstr(node_coord(n), t->board), + node_coord(n), n->u.playouts, + tree_node_get_value(t, parity, n->u.value)); + + /* Add virtual loss if we need to; this is used to discourage + * other threads from visiting this node in case of multiple + * threads doing the tree search. */ + if (u->virtual_loss) + stats_add_result(&n->u, node_color == S_BLACK ? 0.0 : 1.0, u->virtual_loss); + + struct move m = { node_coord(n), node_color }; + int res = board_play(&b2, &m); + + if (res < 0 || (!is_pass(m.coord) && !group_at(&b2, m.coord)) /* suicide */ + || b2.superko_violation) { + if (UDEBUGL(4)) { + for (struct tree_node *ni = n; ni; ni = ni->parent) + fprintf(stderr, "%s<%"PRIhash"> ", coord2sstr(node_coord(ni), t->board), ni->hash); + fprintf(stderr, "marking invalid %s node %d,%d res %d group %d spk %d\n", + stone2str(node_color), coord_x(node_coord(n),b), coord_y(node_coord(n),b), + res, group_at(&b2, m.coord), b2.superko_violation); + } + n->hints |= TREE_HINT_INVALID; + result = 0; + goto end; + } + + assert(node_coord(n) >= -1); + record_amaf_move(&amaf, node_coord(n), board_playing_ko_threat(&b2)); + + if (is_pass(node_coord(n))) + passes++; + else + passes = 0; + + enum stone next_color = stone_other(node_color); + /* We need to make sure only one thread expands the node. If + * we are unlucky enough for two threads to meet in the same + * node, the latter one will simply do another simulation from + * the node itself, no big deal. t->nodes_size may exceed + * the maximum in multi-threaded case but not by much so it's ok. + * The size test must be before the test&set not after, to allow + * expansion of the node later if enough nodes have been freed. */ + if (tree_leaf_node(n) + && n->u.playouts - u->virtual_loss >= u->expand_p && t->nodes_size < u->max_tree_size + && !__sync_lock_test_and_set(&n->is_expanded, 1)) + tree_expand_node(t, n, &b2, next_color, u, -parity); + } + + amaf.game_baselen = amaf.gamelen; + + if (t->use_extra_komi && u->dynkomi->persim) { + b2.komi += round(u->dynkomi->persim(u->dynkomi, &b2, t, n)); + } + + if (passes >= 2) { + /* XXX: No dead groups support. */ + floating_t score = board_official_score(&b2, NULL); + /* Result from black's perspective (no matter who + * the player; black's perspective is always + * what the tree stores. */ + result = - (score * 2); + + if (UDEBUGL(5)) + fprintf(stderr, "[%d..%d] %s p-p scoring playout result %d (W %f)\n", + player_color, node_color, coord2sstr(node_coord(n), t->board), result, score); + if (UDEBUGL(6)) + board_print(&b2, stderr); + + board_ownermap_fill(&u->ownermap, &b2); + + } else { // assert(tree_leaf_node(n)); + /* In case of parallel tree search, the assertion might + * not hold if two threads chew on the same node. */ + result = uct_leaf_node(u, &b2, player_color, &amaf, descent, &dlen, significant, t, n, node_color, spaces); + } + + if (u->policy->wants_amaf && u->playout_amaf_cutoff) { + unsigned int cutoff = amaf.game_baselen; + cutoff += (amaf.gamelen - amaf.game_baselen) * u->playout_amaf_cutoff / 100; + amaf.gamelen = cutoff; + } + + /* Record the result. */ + + assert(n == t->root || n->parent); + floating_t rval = scale_value(u, b, node_color, significant, result); + u->policy->update(u->policy, t, n, node_color, player_color, &amaf, &b2, rval); + + stats_add_result(&t->avg_score, result / 2, 1); + if (t->use_extra_komi) { + stats_add_result(&u->dynkomi->score, result / 2, 1); + stats_add_result(&u->dynkomi->value, rval, 1); + } + + if (u->local_tree && n->parent && !is_pass(node_coord(n)) && dlen > 0) { + /* Get the local sequences and record them in ltree. */ + /* We will look for sequence starts in our descent + * history, then run record_local_sequence() for each + * found sequence start; record_local_sequence() may + * pick longer sequences from descent history then, + * which is expected as it will create new lnodes. */ + enum stone seq_color = player_color; + /* First move always starts a sequence. */ + record_local_sequence(u, t, &b2, descent, dlen, 1, seq_color); + seq_color = stone_other(seq_color); + for (int dseqi = 2; dseqi < dlen; dseqi++, seq_color = stone_other(seq_color)) { + if (u->local_tree_allseq) { + /* We are configured to record all subsequences. */ + record_local_sequence(u, t, &b2, descent, dlen, dseqi, seq_color); + continue; + } + if (descent[dseqi].node->d >= u->tenuki_d) { + /* Tenuki! Record the fresh sequence. */ + record_local_sequence(u, t, &b2, descent, dlen, dseqi, seq_color); + continue; + } + if (descent[dseqi].lnode && !descent[dseqi].lnode) { + /* Record result for in-descent picked sequence. */ + record_local_sequence(u, t, &b2, descent, dlen, dseqi, seq_color); + continue; + } + } + } + +end: + /* We need to undo the virtual loss we added during descend. */ + if (u->virtual_loss) { + floating_t loss = node_color == S_BLACK ? 0.0 : 1.0; + for (; n->parent; n = n->parent) { + stats_rm_result(&n->u, loss, u->virtual_loss); + loss = 1.0 - loss; + } + } + + board_done_noalloc(&b2); + return result; +} + +int +uct_playouts(struct uct *u, struct board *b, enum stone color, struct tree *t, struct time_info *ti) +{ + int i; + if (ti && ti->dim == TD_GAMES) { + for (i = 0; t->root->u.playouts <= ti->len.games && !uct_halt; i++) + uct_playout(u, b, color, t); + } else { + for (i = 0; !uct_halt; i++) + uct_playout(u, b, color, t); + } + return i; +} diff --git a/jni/pachi/uct/walk.h b/jni/pachi/uct/walk.h new file mode 100644 index 0000000..5c6c47b --- /dev/null +++ b/jni/pachi/uct/walk.h @@ -0,0 +1,15 @@ +#ifndef PACHI_UCT_WALK_H +#define PACHI_UCT_WALK_H + +#include "move.h" + +struct tree; +struct uct; +struct board; + +void uct_progress_status(struct uct *u, struct tree *t, enum stone color, int playouts, coord_t *final); + +int uct_playout(struct uct *u, struct board *b, enum stone player_color, struct tree *t); +int uct_playouts(struct uct *u, struct board *b, enum stone color, struct tree *t, struct time_info *ti); + +#endif diff --git a/jni/pachi/util.h b/jni/pachi/util.h new file mode 100644 index 0000000..188f3e4 --- /dev/null +++ b/jni/pachi/util.h @@ -0,0 +1,78 @@ +#ifndef PACHI_UTIL_H +#define PACHI_UTIL_H + +#include + +/* Portability definitions. */ + +#ifdef _WIN32 +#include + +#define sleep(seconds) Sleep((seconds) * 1000) +#define __sync_fetch_and_add(ap, b) InterlockedExchangeAdd((LONG volatile *) (ap), (b)); +#define __sync_fetch_and_sub(ap, b) InterlockedExchangeAdd((LONG volatile *) (ap), -(b)); + +/* MinGW gcc, no function prototype for built-in function stpcpy() */ +char *stpcpy (char *dest, const char *src); + +#include +static inline const char * +strcasestr(const char *haystack, const char *needle) +{ + for (const char *p = haystack; *p; p++) { + for (int ni = 0; needle[ni]; ni++) { + if (!p[ni]) + return NULL; + if (toupper(p[ni]) != toupper(needle[ni])) + goto more_hay; + } + return p; +more_hay:; + } + return NULL; +} +#endif + +/* Misc. definitions. */ + +/* Use make DOUBLE=1 in large configurations with counts > 1M + * where 24 bits of floating_t mantissa become insufficient. */ +#ifdef DOUBLE +# define floating_t double +# define PRIfloating "%lf" +#else +# define floating_t float +# define PRIfloating "%f" +#endif + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +static inline void * +checked_malloc(size_t size, char *filename, unsigned int line, const char *func) +{ + void *p = malloc(size); + if (!p) { + fprintf(stderr, "%s:%u: %s: OUT OF MEMORY malloc(%zu)\n", + filename, line, func, size); + exit(1); + } + return p; +} + +static inline void * +checked_calloc(size_t nmemb, size_t size, char *filename, unsigned int line, const char *func) +{ + void *p = calloc(nmemb, size); + if (!p) { + fprintf(stderr, "%s:%u: %s: OUT OF MEMORY calloc(%zu, %zu)\n", + filename, line, func, nmemb, size); + exit(1); + } + return p; +} + +#define malloc2(size) checked_malloc((size), __FILE__, __LINE__, __func__) +#define calloc2(nmemb, size) checked_calloc((nmemb), (size), __FILE__, __LINE__, __func__) + +#endif diff --git a/jni/pachi/version.h b/jni/pachi/version.h new file mode 100644 index 0000000..b3b3e41 --- /dev/null +++ b/jni/pachi/version.h @@ -0,0 +1,39 @@ +#ifndef PACHI_VERSION_H +#define PACHI_VERSION_H + +#define PACHI_VERNUM 10.00 +#define PACHI_VERNUMS "10.00" + +/* 00. Wang Zhi + * 01. Sansa Meijin 1612-1623 + * 02. Sanetsu 8-Dan 1630-1658 + * 03. Doetsu 7-Dan 1658-1677 + * 04. Dosaku Meijin 1677-1702 + * (h) Doteki 7-Dan (1684-1690) + * (h) Sakugen 7-Dan (1692-1699) + * 05. Dochi Meijin 1702-1727 + * 06. Chihaku 6-Dan 1727-1733 + * 07. Shuhaku 6-Dan 1733-1741 + * 08. Hakugen 6-Dan 1741-1754 + * 09. Satsugen Meijin 1754-1788 + * 10. Retsugen 8-Dan 1788-1808 + * 11. Genjo 8-Dan 1808-1827 + * 12. Jowa Meijin 1827-1839 + * 13. Josaku 7-Dan 1839-1847 + * 14. Shuwa 8-Dan 1847-1873 + * (h) Shusaku 7-Dan (1848-1862) + * 15. Shuetsu 6-Dan 1873-1879 + * 16. Shugen 4-Dan 1879-1884 + * 17. Shuei 7-Dan 1884-1886 + * 18. Shuho 8-Dan 1886 + * 19. Shuei Meijin 1887-1907 + * 20. Shugen 6-Dan 1907-1908 + * 21. Shusai Meijin 1908-1940 */ +#define PACHI_VERNAME "Satsugen" + +/* -devel except when tagged. */ +#define PACHI_VERDEV "" + +#define PACHI_VERSION PACHI_VERNUMS " (" PACHI_VERNAME PACHI_VERDEV ")" + +#endif

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