Last active
August 29, 2015 14:04
-
-
Save CyberShadow/ab468eab7cd070864957 to your computer and use it in GitHub Desktop.
lifecompetes.com bot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.txt | |
*.exe | |
/trace/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import win32.winbase; | |
import std.algorithm; | |
import std.array; | |
import std.conv; | |
import std.datetime; | |
import std.parallelism; | |
import std.range; | |
import std.socket; | |
import std.stdio; | |
import std.string; | |
import common; | |
import input; | |
import net; | |
void main() | |
{ | |
calibrate(); | |
string serverAddress = File("serveraddr.txt").readln().strip(); | |
bool isMain; | |
{ | |
uint mainColor = File("mycolor.txt").readln().strip().to!uint(16); | |
Board board; uint myColor; | |
readBoard(board, myColor); | |
isMain = myColor == mainColor; | |
} | |
while(true) | |
try | |
{ | |
writeln("Waiting..."); stdout.flush(); | |
waitTurn(isMain ? 25 : 50); | |
Board board; | |
uint myColor; | |
writeln("Capturing..."); stdout.flush(); | |
readBoard(board, myColor); | |
printBoard(board); | |
writefln("My color: %06X (%s)", myColor, isMain ? "main" : "helper"); | |
if (canPlace()) | |
{ | |
writeln("Connecting..."); stdout.flush(); | |
auto s = new TcpSocket(getAddress(serverAddress, PORT)[0]); | |
writeln("Sending..."); stdout.flush(); | |
auto p = ClientPacket(board, myColor); | |
auto bytes = s.send((&p)[0..1]); | |
enforce(bytes == p.sizeof, "Too little data sent"); | |
writeln("Waiting for reply..."); stdout.flush(); | |
ServerPacket sp; | |
bytes = s.receive((&sp)[0..1]); | |
enforce(bytes == sp.sizeof, "Too little data received"); | |
s.shutdown(SocketShutdown.BOTH); | |
s.close(); | |
if (sp.x == 0 && sp.y == 0) | |
{ | |
writeln("Server said to skip this turn."); | |
continue; | |
} | |
writefln("Clicking %d, %d...", sp.x, sp.y); stdout.flush(); | |
clickCell(sp.x, sp.y); | |
Sleep(250); | |
submit(); | |
} | |
else | |
writeln("Can't place this turn."); | |
} | |
catch (Exception e) | |
writeln(e); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/public/js/app.js b/public/js/app.js | |
index a006ccd..303efbe 100644 | |
--- a/public/js/app.js | |
+++ b/public/js/app.js | |
@@ -59,6 +59,7 @@ define(['core/game', 'renderer', 'gameclient', 'core/playermanager'], function(G | |
_this.renderer.updateLeaderboard(); | |
_this.renderer.updatePlayersOnline(); | |
_this.renderer.flashNews(); | |
+ _this.askServer(); | |
} | |
if (!_this.game.isBehindOnTicks()) { | |
@@ -114,12 +115,16 @@ define(['core/game', 'renderer', 'gameclient', 'core/playermanager'], function(G | |
}; | |
App.prototype.setToken = function(token) { | |
- localStorage.setItem('token', token); | |
+ //localStorage.setItem('token', token); | |
+ document.location.hash = '#' + token; | |
return token; | |
}; | |
App.prototype.getToken = function() { | |
- var token = localStorage.getItem('token'); | |
+ //var token = localStorage.getItem('token'); | |
+ var token = false; | |
+ if (document.location.hash.length > 1) | |
+ token = document.location.hash.substr(1); | |
return (token) ? token : false; | |
}; | |
@@ -134,5 +139,38 @@ define(['core/game', 'renderer', 'gameclient', 'core/playermanager'], function(G | |
this.playerManager.updatePlayers(state.players); | |
}; | |
+ App.prototype.askServer = function() { | |
+ try { | |
+ var player = this.playerManager.getLocalPlayer(); | |
+ if ((player.cells - this.renderer.flaggedCells.length <= 0)) { | |
+ console.log('No cells to place this generation.'); | |
+ return; | |
+ } | |
+ var s = JSON.stringify({cells:this.game.grid.getCells(), playerId:this.playerManager.getLocalPlayer().id}); | |
+ var xmlhttp = new XMLHttpRequest(); | |
+ var _this = this; | |
+ xmlhttp.onreadystatechange=function() | |
+ { | |
+ if (xmlhttp.readyState==4 && xmlhttp.status==200) | |
+ { | |
+ var reply = JSON.parse(xmlhttp.responseText); | |
+ if (reply.x == 0 && reply.y == 0) { | |
+ console.log('EMPTY reply from server'); | |
+ return; | |
+ } | |
+ console.log('Clicking cell ', reply.x, ' ', reply.y); | |
+ var cell = _this.game.grid.getCell(reply.x, reply.y); | |
+ cell.setDirty(); | |
+ _this.gameClient.placeLiveCells([cell]); | |
+ } | |
+ } | |
+ xmlhttp.open("POST","/myserver",true); | |
+ xmlhttp.send(s); | |
+ } | |
+ catch (e) { | |
+ console.log(e); | |
+ } | |
+ } | |
+ | |
return App; | |
}); | |
\ No newline at end of file | |
diff --git a/public/js/gameclient.js b/public/js/gameclient.js | |
index 75386e4..c988627 100644 | |
--- a/public/js/gameclient.js | |
+++ b/public/js/gameclient.js | |
@@ -143,7 +143,7 @@ define(['socket.io'], function(io) { | |
this._requestState(); | |
} | |
- this.hidden = document.hidden; | |
+ //this.hidden = document.hidden; | |
}; | |
GameClient.prototype._requestState = function() { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import core.runtime; | |
import std.algorithm; | |
import std.array; | |
import std.datetime; | |
import std.path; | |
import std.stdio; | |
import std.string; | |
import ae.sys.file; | |
import ae.utils.text; | |
alias uint[W][H] Board; | |
enum W = 90; | |
enum H = 45; | |
int bx(int x) { return x.max(0).min(W-1); } | |
int by(int y) { return y.max(0).min(H-1); } | |
void simulate(ref in Board src, ref Board dst) | |
{ | |
dst[] = src[]; | |
foreach (int y; 0..H) | |
{ | |
int j0 = by(y-1); | |
int j1 = by(y+1) + 1; | |
foreach (int x; 0..W) | |
{ | |
int i0 = bx(x-1); | |
int i1 = bx(x+1) + 1; | |
int n; | |
static uint[8] colors; | |
foreach (j; j0..j1) | |
foreach (i; i0..i1) | |
{ | |
if (i==x && j==y) | |
continue; | |
if (src[j][i]) | |
colors[n++] = src[j][i]; | |
} | |
if (src[y][x]) | |
{ | |
if (n < 2 || n > 3) | |
dst[y][x] = 0; | |
} | |
else | |
if (n == 3) | |
{ | |
auto a = colors[0..n]; | |
dst[y][x] = a[1]==a[2] ? a[1] : a[0]; | |
} | |
} | |
} | |
} | |
void simulatePart(ref in Board src, ref Board dst, int cx, int cy, int r) | |
{ | |
// r++; | |
int x0 = bx(cx - r); | |
int x1 = bx(cx + r); | |
int y0 = by(cy - r); | |
int y1 = by(cy + r); | |
foreach (int y; y0..y1) | |
{ | |
int j0 = by(y-1); | |
int j1 = by(y+1) + 1; | |
foreach (int x; x0..x1) | |
{ | |
int i0 = bx(x-1); | |
int i1 = bx(x+1) + 1; | |
int n = 0; | |
static uint[8] colors; | |
foreach (j; j0..j1) | |
foreach (i; i0..i1) | |
{ | |
if (i==x && j==y) | |
continue; | |
if (src[j][i]) | |
colors[n++] = src[j][i]; | |
} | |
if (src[y][x]) | |
{ | |
if (n < 2 || n > 3) | |
dst[y][x] = 0; | |
else | |
dst[y][x] = src[y][x]; | |
} | |
else | |
{ | |
if (n == 3) | |
{ | |
auto a = colors[0..n]; | |
dst[y][x] = a[1]==a[2] ? a[1] : a[0]; | |
} | |
else | |
dst[y][x] = 0; | |
} | |
} | |
} | |
} | |
enum TURNS = 40; | |
alias Forecast = Board[TURNS]; | |
void createForecast(in ref Board src, ref Forecast dst) | |
{ | |
dst[0] = src; | |
foreach (n; 1..TURNS) | |
simulate(dst[n-1], dst[n]); | |
} | |
void simulatePlace(in ref Forecast src, ref Forecast dst, int x, int y, uint color) | |
{ | |
dst = src; | |
dst[0][y][x] = color; | |
foreach (n; 1..TURNS) | |
simulatePart(dst[n-1], dst[n], x, y, n); | |
} | |
void printBoard(ref Board board, File f = stdout) | |
{ | |
auto alphabet = "!@#$%^&*+=-<>:;ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
size_t[uint] aa; | |
foreach (int y; 0..H) | |
{ | |
foreach (int x; 0..W) | |
{ | |
auto c = board[y][x]; | |
if (!c) | |
f.write(" "); | |
else | |
if (c & 0xFF000000) | |
f.write(cast(char)('0' + (c & ~0xFF000000))); | |
else | |
f.write(alphabet[c%$]); | |
} | |
f.writeln(); | |
} | |
} | |
immutable static string instance; | |
shared static this() { instance = randomString(); } | |
void saveForecast(ref Forecast forecast, string name, SysTime t) | |
{ | |
auto fn = "trace/%s-%s/%s/%s.txt".format(Runtime.args[0].baseName().stripExtension(), instance, t.toUnixTime(), name); | |
ensurePathExists(fn); | |
auto f = File(fn, "w"); | |
foreach (ref board; forecast) | |
{ | |
printBoard(board, f); | |
f.writeln("-".replicate(W)); | |
} | |
} | |
uint count(ref Board board, uint color) | |
{ | |
int result; | |
foreach (int y; 0..H) | |
foreach (int x; 0..W) | |
if (board[y][x] == color) | |
result++; | |
return result; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.algorithm; | |
import std.array; | |
import std.conv; | |
import std.datetime; | |
import std.parallelism; | |
import std.range; | |
import std.stdio; | |
import std.string; | |
import ae.net.asockets; | |
import ae.net.http.responseex; | |
import ae.net.http.server; | |
import ae.utils.json; | |
import common; | |
enum MAX_CLIENTS_PER_TURN = 3; | |
struct JsonRequest | |
{ | |
JsonCell[] cells; | |
int playerId; | |
} | |
struct JsonCell | |
{ | |
int x, y; | |
bool dirty; | |
bool alive = false; | |
int playerId; | |
} | |
struct JsonReply { int x, y; } | |
void main() | |
{ | |
uint mainId = File("myid.txt").readln().strip().to!uint(); | |
SysTime firstPacketTime; | |
Forecast forecast; | |
uint clientNumber; | |
auto s = new HttpServer; | |
s.handleRequest = (HttpRequest request, HttpServerConnection conn) { | |
string post = cast(string)request.data.joinToHeap(); | |
auto r = jsonParse!JsonRequest(post); | |
writefln("Got request from player %s (%s).", r.playerId, r.playerId == mainId ? "main" : "helper"); stdout.flush(); | |
Board board; | |
uint[] boardArr = cast(uint[])(board[]); | |
foreach (n, ref cell; r.cells) | |
boardArr[n] = cell.alive ? cell.playerId : 0; | |
scope(exit) clientNumber++; | |
auto now = Clock.currTime(); | |
if (firstPacketTime < now - 4.seconds) | |
{ | |
printBoard(board); | |
auto t = Clock.currTime(); | |
writeln("Thinking..."); stdout.flush(); | |
createForecast(board, forecast); | |
saveForecast(forecast, "game", t); | |
uint[] counts = forecast[].map!(b => count(b, mainId)).array(); | |
writefln("Current: [%(%3d, %)]", counts); | |
firstPacketTime = now; | |
clientNumber = 0; | |
} | |
JsonReply reply; | |
if (clientNumber >= MAX_CLIENTS_PER_TURN) | |
{ | |
writefln("Sending EMPTY reply to client %d (%s).", clientNumber, r.playerId == mainId ? "main" : "helper"); stdout.flush(); | |
reply = JsonReply(0, 0); | |
} | |
else | |
{ | |
static struct Result { int points, x, y; } | |
Result bestResult; | |
foreach (int y; H.iota.parallel) | |
{ | |
Result bestRowResult; | |
foreach (int x; 0..W) | |
{ | |
if (forecast[0][y][x]) | |
continue; | |
static Forecast newForecast; | |
simulatePlace(forecast, newForecast, x, y, r.playerId); | |
//auto r = count(newForecast[$-1]); | |
int m = 0, t = 0; | |
foreach (ref newBoard; newForecast) | |
{ | |
auto c = count(newBoard, mainId); | |
t += c; | |
m = max(m, c); | |
} | |
auto r = t; | |
if (m >= 1000) | |
r += 100000; | |
//auto r = newForecast[].map!count.reduce!"a+b"(); | |
if (bestRowResult.points < r) | |
bestRowResult = Result(r, x, y); | |
} | |
synchronized | |
if (bestResult.points < bestRowResult.points) | |
bestResult = bestRowResult; | |
} | |
//if (bestResult.x == 0 && bestResult.y == 0) | |
// continue; // failsafe | |
static Forecast bestForecast; | |
simulatePlace(forecast, bestForecast, bestResult.x, bestResult.y, r.playerId); | |
forecast = bestForecast; // for next client | |
uint[] bestCounts = bestForecast[].map!(b => count(b, mainId)).array(); | |
writefln("%2d, %2d : [%(%3d, %)] (%d)", bestResult.x, bestResult.y, bestCounts, bestResult.points); | |
saveForecast(forecast, "my-%d".format(clientNumber), now); | |
writefln("Sending reply to client %d (%s)...", clientNumber, r.playerId == mainId ? "main" : "helper"); stdout.flush(); | |
reply = JsonReply(bestResult.x, bestResult.y); | |
} | |
auto response = new HttpResponseEx; | |
conn.sendResponse(response.serveJson(reply)); | |
writeln("Sent reply: ", reply.toJson()); | |
}; | |
s.listen(55555); | |
socketManager.loop(); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.algorithm; | |
import std.array; | |
import std.exception; | |
import std.stdio; | |
import win32.winbase; | |
import win32.winuser; | |
import ae.sys.windows.input; | |
import ae.utils.graphics.color; | |
import ae.utils.graphics.gdi; | |
import common; | |
uint convertColor(BGRX c) | |
{ | |
return (c.r * 0x10000) | (c.g * 0x100) | (c.b); | |
} | |
auto capture() | |
{ | |
enum WH = 2048; | |
auto canvas = GDICanvas!BGRX(WH, WH); | |
auto ddc = GetDC(null); | |
canvas.BitBlt(0, 0, WH, WH, ddc, 0, 0, SRCCOPY); | |
ReleaseDC(null, ddc); | |
return canvas; | |
} | |
int x0, y0; | |
void calibrate() | |
{ | |
auto canvas = capture(); | |
BGRX white = BGRX(255, 255, 255); | |
BGRX black = BGRX(0, 0, 0); | |
BGRX[] searchPattern = white ~ [black].replicate(W*S + 1) ~ white; | |
auto pixelArr = cast(uint[])canvas.pixels[0..canvas.w*canvas.h]; | |
foreach (ref c; pixelArr) | |
c &= 0x00FFFFFF; | |
auto patternArr = cast(uint[])searchPattern; | |
auto p = pixelArr.countUntil(patternArr); | |
enforce(p >= 0, "Can't find game"); | |
x0 = p % canvas.w + 1; | |
y0 = p / canvas.w; | |
//writeln(x0, " ", y0); | |
} | |
void waitTurn(int t=25) | |
{ | |
auto ddc = GetDC(null); | |
scope(exit) ReleaseDC(null, ddc); | |
writeln(" Waiting for nonblank..."); stdout.flush(); | |
while ((ddc.GetPixel(CX-t, CY) & 0xFFFFFF) == 0xFFFFFF) Sleep(10); | |
writeln(" Waiting for blank..."); stdout.flush(); | |
while ((ddc.GetPixel(CX-t, CY) & 0xFFFFFF) != 0xFFFFFF) Sleep(10); | |
writeln(" Waiting for nonblank..."); stdout.flush(); | |
while ((ddc.GetPixel(CX-t, CY) & 0xFFFFFF) == 0xFFFFFF) Sleep(10); | |
} | |
void readBoard(ref Board board, out uint myColor) | |
{ | |
auto canvas = capture(); | |
foreach (y; 0..H) | |
foreach (x; 0..W) | |
{ | |
auto n = convertColor(canvas[X0 + x*S + 1, Y0 + y*S + 1]); | |
if (n == 0xFFFFFF) | |
n = 0; | |
board[y][x] = n; | |
} | |
myColor = convertColor(canvas[CX, CY]); | |
} | |
enum S = 10; | |
//enum X0 = 62; | |
//enum Y0 = 152; | |
@property int X0() { return x0; } | |
@property int Y0() { return y0; } | |
@property int CX() { return (X0 + (X0+W*S)) / 2; } | |
@property int CY() { return Y0 - 8; } | |
void clickCell(int x, int y) | |
{ | |
click(X0 + x*S + 5, Y0 + y*S + 5); | |
} | |
void click(int x, int y) | |
{ | |
SetCursorPos(x, y); | |
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); | |
mouse_event(MOUSEEVENTF_LEFTUP , 0, 0, 0, 0); | |
} | |
int refreshCounter; | |
enum REFRESH_INTERVAL = 12; | |
bool canPlace() | |
{ | |
int x = 0, y = 0; | |
SetCursorPos(X0 + x*S + 5, Y0 + y*S + 5); | |
Sleep(50); | |
auto ddc = GetDC(null); | |
scope(exit) ReleaseDC(null, ddc); | |
bool result = ddc.GetPixel(X0 + x*S + 1, Y0 + y*S + 1) != ddc.GetPixel(X0 + x*S + 5, Y0 + y*S + 5); | |
SetCursorPos(CX, CY); | |
return result; | |
} | |
unittest | |
{ | |
calibrate(); | |
Board b; uint myColor; | |
readBoard(b, myColor); | |
assert(canPlace()); | |
} | |
void submit() | |
{ | |
click(CX, Y0 + H*S + 25); | |
if (refreshCounter++ % REFRESH_INTERVAL == 0) | |
{ | |
Sleep(500); // Let the packet go through | |
press(VK_F5); | |
Sleep(500); // Wait for page to load | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import common; | |
enum PORT = 55555; | |
struct ClientPacket | |
{ | |
Board board; | |
uint myColor; | |
} | |
struct ServerPacket | |
{ | |
int x, y; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.algorithm; | |
import std.array; | |
import std.conv; | |
import std.datetime; | |
import std.parallelism; | |
import std.range; | |
import std.socket; | |
import std.stdio; | |
import std.string; | |
import common; | |
import net; | |
enum MAX_CLIENTS_PER_TURN = 1; | |
void main() | |
{ | |
auto l = new TcpSocket(); | |
l.bind(getAddress("0.0.0.0", PORT)[0]); | |
l.listen(50); | |
uint mainColor = File("mycolor.txt").readln().strip().to!uint(16); | |
SysTime firstPacketTime; | |
Forecast forecast; | |
uint clientNumber; | |
while (true) | |
try | |
{ | |
writeln("Waiting..."); stdout.flush(); | |
auto s = l.accept(); | |
scope(exit) s.close(); | |
scope(exit) clientNumber++; | |
writeln("Receiving..."); stdout.flush(); | |
ClientPacket p; | |
auto bytes = s.receive((&p)[0..1]); | |
enforce(bytes == p.sizeof, "Too little data received"); | |
auto now = Clock.currTime(); | |
if (firstPacketTime < now - 4.seconds) | |
{ | |
printBoard(p.board); | |
auto t = Clock.currTime(); | |
writeln("Thinking..."); stdout.flush(); | |
createForecast(p.board, forecast); | |
saveForecast(forecast, "game", t); | |
uint[] counts = forecast[].map!(b => count(b, mainColor)).array(); | |
writefln("Current: [%(%3d, %)]", counts); | |
firstPacketTime = now; | |
clientNumber = 0; | |
} | |
ServerPacket sp; | |
if (clientNumber >= MAX_CLIENTS_PER_TURN) | |
{ | |
writefln("Sending EMPTY reply to client %d (%s).", clientNumber, p.myColor == mainColor ? "main" : "helper"); stdout.flush(); | |
sp = ServerPacket(0, 0); | |
} | |
else | |
{ | |
static struct Result { int points, x, y; } | |
Result bestResult; | |
foreach (int y; H.iota.parallel) | |
{ | |
Result bestRowResult; | |
foreach (int x; 0..W) | |
{ | |
if (forecast[0][y][x]) | |
continue; | |
static Forecast newForecast; | |
simulatePlace(forecast, newForecast, x, y, p.myColor); | |
//auto r = count(newForecast[$-1]); | |
int r; | |
foreach (ref newBoard; newForecast) | |
r += count(newBoard, mainColor); | |
//auto r = newForecast[].map!count.reduce!"a+b"(); | |
if (bestRowResult.points < r) | |
bestRowResult = Result(r, x, y); | |
} | |
synchronized | |
if (bestResult.points < bestRowResult.points) | |
bestResult = bestRowResult; | |
} | |
//if (bestResult.x == 0 && bestResult.y == 0) | |
// continue; // failsafe | |
static Forecast bestForecast; | |
simulatePlace(forecast, bestForecast, bestResult.x, bestResult.y, p.myColor); | |
forecast = bestForecast; // for next client | |
uint[] bestCounts = bestForecast[].map!(b => count(b, mainColor)).array(); | |
writefln("%2d, %2d : [%(%3d, %)] (%d)", bestResult.x, bestResult.y, bestCounts, bestResult.points); | |
saveForecast(forecast, "my-%d".format(clientNumber), now); | |
writefln("Sending reply to client %d (%s)...", clientNumber, p.myColor == mainColor ? "main" : "helper"); stdout.flush(); | |
sp = ServerPacket(bestResult.x, bestResult.y); | |
} | |
bytes = s.send((&sp)[0..1]); | |
enforce(bytes == sp.sizeof, "Too little data sent"); | |
s.shutdown(SocketShutdown.BOTH); | |
} | |
catch (Exception e) | |
writeln(e); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment