// Hyperbolic Rogue -- Go // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details // compile the Discord version: // ./mymake rogueviz/gobot.cpp -std=c++17 -lssl -lz -lcrypto -I rogueviz/aegis/include/ -I rogueviz/aegis/lib/json/include/ -I rogueviz/aegis/lib/spdlog/include/ -I rogueviz/aegis/lib/websocketpp/ -I rogueviz/aegis/lib/asio/asio/include/ // to run on an interesting board, run RogueViz with parameters: // -canvas i -fillmodel ff801080 -noscr -geo Bring -gp 5 1 -unrectified -go-local -smart 1 // (add -run before -go-local if you want to select your board manually (press F10 after selecting the board) /** \file gobot.cpp * \brief bot to play Go via Discord */ #define AEGIS 0 #if AEGIS #include #endif #include "rogueviz.h" namespace hr { EX namespace gobot { eWall empty = waChasm; bool in = false; vector ac; map indices; const int Free = 2; struct boarddata { vector taken, owner; array captures; }; boarddata current; int labels_value = 1; vector history; bool draw_go(cell *c, const shiftmatrix& V); void init_go() { ac = currentmap->allcells(); current.taken.resize(isize(ac), 2); current.owner.resize(isize(ac), 2); current.captures[0] = 0; current.captures[1] = 0; for(int i=0; i neigh_indices(int i) { vector res; forCellEx(c1, ac[i]) if(indices.count(c1)) res.push_back(indices[c1]); return res; } string chars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ123456789"; bool valid_index(int q) { return q >= 0 && q < isize(ac); } string index_to_str(int k) { if(k < isize(chars)) return s0 + chars[k]; else return chars[k % isize(chars)] + index_to_str(k / isize(chars) - 1); } int str_to_index(string s) { int val = -1; for(int i=0; iwall == waSea) c->wall = waChasm; if(!indices.count(c)) return false; int id = indices[c]; int lv_needed; forCellIdCM(c1, i, c) if(indices.count(c1) && indices[c1] < id) { vid.linewidth *= 2; gridline(V, C0, currentmap->adj(c, i) * C0, 0xFF, 4); vid.linewidth /= 2; } if(current.taken[id] != 2) { vid.linewidth *= 2; poly_outline = 0xFF; queuepolyat(V, cgi.shHugeDisk, player_colors[current.taken[id]], PPR::SUPERLINE); vid.linewidth /= 2; lv_needed = 3; } else if(current.owner[id] == 2) { lv_needed = 1; } else { queuepoly(V, cgi.shGem[0], player_colors[current.owner[id]]); lv_needed = 2; } if(labels_value >= lv_needed) { string s = index_to_str(id); queuestr(V, isize(s) == 1 ? 0.8 : 0.5, s, 0xFFD500); } return false; } void save_backup() { history.push_back(current); } void undo() { current = history.back(); history.pop_back(); } #if AEGIS aegis::gateway::events::message_create* cur; #endif int shot_state; #if AEGIS std::vector > old_shots; #endif void clean_old_shots() { /* std::vector > remaining; for(auto& sh: old_shots) if(sh.available()) { sh.get().delete_message(); } else remaining.emplace_back(move(sh)); old_shots = move(remaining); */ } bool menubased; void take_shot() { #if AEGIS if(cur) { println(hlog, "taking test screenshot"); shot_state = 1; while(shot_state == 1) usleep(1000); shot_state = 0; aegis::create_message_t msg; aegis::rest::aegis_file f; f.name = "go-board.png"; FILE *ff = fopen("go-temp.png", "r"); if(!ff) { println(hlog, "file missing?!"); return; } int c; while((c = fgetc(ff)) >= 0) f.data.push_back(c); fclose(ff); println(hlog, "file size = ", int(f.data.size())); msg.file(f); println(hlog, "file attached"); clean_old_shots(); // old_shots.push_back(); cur->msg.get_channel().create_message(msg); println(hlog, "message sent"); } #else if(0) {} #endif else if(!menubased) { println(hlog, "taking test screenshot"); shot::take("go-test.png"); } } void go_message(string s) { println(hlog, s); addMessage(s); #if AEGIS if(cur) cur->msg.get_channel().create_message(s); #endif } struct dfs { vector visited; vector q; dfs() { visited.resize(isize(ac), false); } void visit(int i) { if(visited[i]) return; visited[i] = true; q.push_back(i); }; }; int count_breath(int pos) { int who = current.taken[pos]; dfs d; int result = 0; d.visit(pos); for(int i=0; i tokens) { int t = isize(tokens); save_backup(); bool ok = false; for(int i=1; i tokens, int who) { int t = isize(tokens); save_backup(); bool ok = false; for(int i=1; i tokens; string ctoken; for(char c: s + " ") if(among(c, ' ', '\n', '\r', '\t')) { if(ctoken != "") tokens.push_back(ctoken), ctoken = ""; } else ctoken += c; int t = isize(tokens); if(t == 0) return; if(tokens[0] == "b" && t == 2) { try_to_play(tokens[1], 0); } if(tokens[0] == "w" && t == 2) { try_to_play(tokens[1], 1); } if(tokens[0] == "center" && t == 2) { int pos = str_to_index(tokens[1]); if(!valid_index(pos)) go_message("invalid cell: " + tokens[1]); else { centerover = ac[pos]; // fullcenter(); take_shot(); } } if(tokens[0] == "rotate" && t == 2) { try { ld angle = parseld(tokens[1]); View = spin(angle * degree) * View; take_shot(); } catch(hr_parse_exception& exc) { go_message(exc.s); } } if(tokens[0] == "labels" && t == 2) { try { labels_value = parseld(tokens[1]); take_shot(); } catch(hr_parse_exception& exc) { go_message(exc.s); } } if(tokens[0] == "hires") { dynamicval dx(shot::shotx, 1000); dynamicval dy(shot::shoty, 1000); take_shot(); } if(tokens[0] == "help") go_message( "b [where] - play as black\n" "w [where] - play as white\n" "center [where] - center the screen\n" "rotate [degrees] - rotate the screen\n" "die [where] - kill a group\n" "clear - clear owner marks\n" "labels 0..3 - show (0) no labels, (1) labels on unowned, (2) labels on empty, (3) all labels\n" "ob [where] - own area as black\n" "ow [where] - own area as white\n" "score - view the score\n" "hires - take a 1000x1000 screenshot\n" "restart - restart\n" "undo - undo last move\n" ); if(tokens[0] == "save") { save_backup(); fhstream f("go.saved-game", "wb"); f.write(history); undo(); } if(tokens[0] == "die") die_at(tokens); if(tokens[0] == "clear" && t == 1) clear_owner_marks(); if(tokens[0] == "ow" || tokens[0] == "ob") set_owner(tokens, tokens[0][1] == 'w'); if(tokens[0] == "undo") { if(history.empty()) go_message("no undo history"); else { undo(); take_shot(); } } if(tokens[0] == "score") { array owned_by, stones; for(int i=0; i<2; i++) owned_by[i] = stones[i] = 0; for(int i=0; i lk(lock); cur = &obj; accept_command(obj.msg.get_content()); cur = nullptr; }); bot.run(); bot.yield(); }); #endif } void go_menu() { getcstat = '-'; cmode = sm::SIDE | sm::DIALOG_STRICT_X; gamescreen(); dialog::init("HyperGo", iinf[itPalace].color, 150, 100); mousing = false; /* always display letters */ array owned_by, stones; for(int i=0; i<2; i++) owned_by[i] = stones[i] = 0; for(int i=0; i