Skip to content

Commit

Permalink
* Updated README
Browse files Browse the repository at this point in the history
* Added two scripts for running a replay from russianaicup.ru
  • Loading branch information
Vasily committed Dec 11, 2015
1 parent d55e2b9 commit 7f18c8d
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 2 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Plugin is based on the source that was provided by AI Cup committee.

# How to control
Plugin is controlled by the property file named `visualizer-plugin.properties` placed in the same directory where `.properties` file which is used by local runner is stored.

Currently there is only one value that plugin will honor, `plugin-port-number`, which is the port which plugin listens for incoming connections. Default value is `13579`.
Properties are:
* `plugin-port-number` - port which plugin listens for incoming connections. Default value is `13579`.
* `plugin-do-tick-sync` - whether to do a sync between local runner and debug client, see "re-playing games" for more.

# How to use
Plugin starts a server thread that accepts **only one** connection to its port number.
Expand All @@ -23,5 +24,14 @@ Currently known commands are:

Color `<color>` is actually an `r g b` triple of floats where `0.0 0.0 0.0` will be black and `1.0 1.0 1.0` will be white.

# Re-playing games from russianaicup.ru with visual debug
To support that your debug client has to support syncing model.
It is currently done as follows:
1. Each tick plugin sends to the client `SYNC <TICK_NUMBER>` line and waits for `ACK` from client
2. Debug client should respond with `ACK` as soon as the strategy using this client has finished computing `<TICK_NUMBER>` tick

This mode has to be enabled in `visualizer-plugin.properties` with setting `plugin-do-tick-sync` to either `true` or to `auto`.
Auto mode will detect replay mode by checking names of players and assuming that if there is **NO** `MyStrategy` then it is a replay and it requires sync mode.

# How strategy can use it
Well, this is actually up to the user... currently there is very simple debug client implemented in Python provided.
38 changes: 38 additions & 0 deletions debug-site-game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import sys
import os
import subprocess
import time
import atexit

REPEATER_SUBDIR = 'repeater'

def startRepeater(token):
cwd = os.getcwd()
try:
os.chdir(REPEATER_SUBDIR)
repeater = subprocess.Popen(['java', '-cp', '.;*;%s/*' % os.getcwd(), '-jar',
'repeater.jar', token], shell=False)
time.sleep(0.5)
finally:
os.chdir(cwd)

def repeaterStop(proc=repeater):
if proc.poll() is None:
proc.kill()
atexit.register(repeaterStop)

def main(gameNumber, repeaterToken):
os.chdir(os.path.abspath(os.path.dirname(__file__)))
if not startRepeater(repeaterToken):
sys.exit('cannot start repeater')
runner = subprocess.Popen([sys.executable, os.path.abspath('local-runner/replay-dump.py'),
gameNumber], shell=False)
runner.wait()

if __name__ == '__main__':
try:
gameNumber, repeaterToken = sys.argv[1:]
except ValueError:
sys.exit('Usage: %s game-number repeater-token' % sys.argv[0])
else:
main(gameNumber, repeaterToken)
107 changes: 107 additions & 0 deletions local-runner/replay-dump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pylint: disable=missing-docstring, invalid-name
import urllib
import re
import sys
import contextlib
import os
import errno
import subprocess

RUN_PLAYER_RE = re.compile(r'''<span\s+class\s*=\s*["']?run-player["']?\s*(.*)>''')
TOKEN_RE = re.compile(r'''.*data-token\s*=\s*["']?([^'"\s]+)['"]?''')
CONTENT_LENGTH_RE = re.compile(r'Content-Length:\s*(\d+)')
CHUNK = 1 * 1024 * 1024

def readPage(url):
with contextlib.closing(urllib.urlopen(url)) as target:
headers = target.info().headers
for header in headers:
try:
size = int(CONTENT_LENGTH_RE.search(header).group(1))
except AttributeError:
continue
else:
break
else:
size = None
readBytes = 0
if size is not None and size >= CHUNK:
print 'Downloading: 00.0%',
while readBytes < size:
chunk = target.read(CHUNK)
readBytes += len(chunk)
print '\rDownloading: %4.1f%%' % (100.0 * readBytes / size),
yield chunk
else:
print 'Downloading: ',
while True:
chunk = target.read(CHUNK)
if not chunk:
break
readBytes += len(chunk)
print '\rDownloading: %0.1fM' % (readBytes / 1024.0 / 1024),
yield chunk
print '\rDownloading: done%s' % (' ' * 10)

def getReplayAddress(gameUrl):
with contextlib.closing(urllib.urlopen(gameUrl)) as gamePage:
game = gamePage.read()
runPlayerSpan = RUN_PLAYER_RE.search(game).group(1)
token = TOKEN_RE.search(runPlayerSpan).group(1)
return 'http://russianaicup.ru/boombox/data/games/%s?offset=0' % token

def downloadReplay(replayUrl, targetPath):
with open(targetPath, 'w') as out:
for chunk in readPage(replayUrl):
out.write(chunk)

def ensureReplay(gameUrl):
targetFile = os.path.join('games', gameUrl.split('/')[-1] + '.json')
if os.path.exists(targetFile):
print 'File already downloaded'
return targetFile

replayUrl = getReplayAddress(gameUrl)
try:
downloadReplay(replayUrl, targetFile)
print 'Downloaded to: %s' % targetFile
except KeyboardInterrupt:
print 'Interrupted, removing partial file'
os.remove(targetFile)
return None

return targetFile

def ensureGameUrl(gameUrl):
try:
gameNumber = int(gameUrl.strip())
except ValueError:
return gameUrl
return 'http://russianaicup.ru/game/view/%d' % gameNumber

def main(gameUrl):
os.chdir(os.path.dirname(os.path.abspath(__file__)))
try:
os.makedirs('games')
except OSError as ex:
if ex.errno != errno.EEXIST:
raise

replayFile = ensureReplay(gameUrl)
if not replayFile:
sys.exit('Cannot run replay')
props = []
with open('local-runner-replay.properties', 'r') as inp:
for line in inp:
if line.startswith('replay-file='):
props.append('replay-file=%s\n' % replayFile.replace('\\', '/'))
else:
props.append(line)
with open('local-runner-replay.properties', 'w') as out:
out.write(''.join(props))
subprocess.check_call(['java', '-jar', "local-runner.jar", 'local-runner-replay.properties'], shell=False)

if __name__ == '__main__':
if len(sys.argv) != 2:
sys.exit('Usage: %s game-url-or-number' % sys.argv[0])
main(ensureGameUrl(sys.argv[1]))

0 comments on commit 7f18c8d

Please sign in to comment.