// Dam bridge for Scan 3.1 — replaces Scan's main.cpp in the WebAssembly build. // Scan is Copyright (C) 2015-2019 Fabien Letouzey, GPL v3 (https://github.com/rhalbersma/scan). // This file is distributed under the same licence. // // The page's Web Worker writes data/eval (and data/book) into the virtual file // system, calls scan_init once, then scan_go for every question. Positions use // Scan's hub format: "W" or "B" (side to move) + 50 squares (w b W B e), squares 1..50. #include #include #include #include #include "bb_base.hpp" #include "bb_comp.hpp" #include "bb_index.hpp" #include "bit.hpp" #include "book.hpp" #include "common.hpp" #include "eval.hpp" #include "fen.hpp" #include "game.hpp" #include "hash.hpp" #include "libmy.hpp" #include "move.hpp" #include "pos.hpp" #include "score.hpp" #include "search.hpp" #include "tt.hpp" #include "util.hpp" #include "var.hpp" static std::string G_Out; static Game G_Game; // big (1024 nodes): kept off the stack static bool G_Ready = false; static void add(std::string & s, const std::string & name, const std::string & value) { if (!s.empty()) s += ";"; s += name + "=" + value; } extern "C" { // book: 1 = use the opening book (needs data/book); tt_bits: hash table = 16 bytes << tt_bits EMSCRIPTEN_KEEPALIVE int scan_init(int book, int tt_bits) { bit::init(); hash::init(); pos::init(); var::init(); bb::index_init(); bb::comp_init(); ml::rand_init(); var::set("variant", "normal"); var::set("book", book ? "true" : "false"); var::set("threads", "1"); var::set("tt-size", std::to_string(tt_bits)); var::set("bb-size", "0"); var::update(); bit::init(); // depends on the variant if (var::Book) book::init(); eval_init(); G_TT.set_size(var::TT_Size); G_Ready = true; return 1; } EMSCRIPTEN_KEEPALIVE void scan_new_game() { G_TT.clear(); } // pos: hub position (empty = start); moves: hub moves separated by spaces; // time: seconds (<= 0 = no limit); depth: max depth (<= 0 = no limit); book: 1 = may answer from the book // Returns "move=…;score=…;depth=…;nodes=…;time=…;pv=…" or "error=…". EMSCRIPTEN_KEEPALIVE const char * scan_go(const char * pos, const char * moves, double time, int depth, int book) { G_Out.clear(); if (!G_Ready) { add(G_Out, "error", "not ready"); return G_Out.c_str(); } Game & game = G_Game; try { std::string p(pos); game.init(p.empty() ? pos::Start : pos_from_hub(p)); } catch (const Bad_Input &) { add(G_Out, "error", "bad position"); return G_Out.c_str(); } std::stringstream ss(moves); std::string arg; while (ss >> arg) { try { Move mv = move::from_hub(arg, game.pos()); if (!move::is_legal(mv, game.pos())) { add(G_Out, "error", "illegal move " + arg); return G_Out.c_str(); } game.add_move(mv); } catch (const Bad_Input &) { add(G_Out, "error", "bad move " + arg); return G_Out.c_str(); } } if (game.is_end()) { add(G_Out, "error", "game over"); return G_Out.c_str(); } Search_Input si; si.init(); si.move = true; si.book = book != 0; si.input = false; si.output = Output_None; if (depth > 0) si.depth = Depth(depth); if (time > 0.0) si.time = time; Search_Output so; search(so, game.node(), si); Move mv = so.move; if (mv == move::None) mv = quick_move(game.node()); if (mv == move::None) { add(G_Out, "error", "no move"); return G_Out.c_str(); } Pos p0 = game.pos(); add(G_Out, "move", move::to_hub(mv, p0)); if (so.score != score::None) { Score sc = so.score; if (score::is_win(sc)) add(G_Out, "win", std::to_string(int(score::Inf - sc))); else if (score::is_loss(sc)) add(G_Out, "loss", std::to_string(int(sc + score::Inf))); add(G_Out, "score", std::to_string(int(sc))); } add(G_Out, "depth", std::to_string(int(so.depth))); add(G_Out, "nodes", std::to_string(so.node)); add(G_Out, "time", ml::ftos(so.time(), 3)); if (so.pv.size() != 0) add(G_Out, "pv", so.pv.to_hub(p0)); return G_Out.c_str(); } } // extern "C"