#!/usr/local/bin/ruby require 'net/http' class JTwixtFormatter def formatInt4Pair(key, value) return formatKey(key) + format4byteInt(4) + format4byteInt(value) end def formatInt1Pair(key, value) return formatKey(key) + format4byteInt(1) + format1byteInt(value) end def formatStringPair(key, value) return formatKey(key) + format4byteInt(value.length) + value end def formatKey(key) return format1byteInt(key.length) + key end def format4byteInt(value) return format2byteInt(value / (2**16)) + format2byteInt(value) end def format2byteInt(value) return format1byteInt(value / (2**8)) + format1byteInt(value) end def format1byteInt(value) return format("%c", value) # just the lowest 8 bits end def formatStandardTwixtHeader(player1, player2) header = formatInt4Pair("gtv", 2); if player1 != nil header += formatStringPair("player1", player1) end if player2 != nil header += formatStringPair("player2", player2) end header += formatInt1Pair("pov", 0) + # point of view (0 = alternate) formatStringPair("type", "twixt1") + formatInt4Pair("bdsize", 24) + # board size formatInt4Pair("tgtv", 1) + # twixt game tree version format2byteInt(0) return header end def formatMove(move) return format2byteInt(1) + # one node (no tree) formatKey("m") + # m for "move" format2byteInt(move.length) + move + format1byteInt(0) end def formatCoordinates(x, y) return format1byteInt(x - 1) + format1byteInt(y - 1) end def formatSimpleMove(x, y, player) return formatCoordinates(x, y) + format1byteInt(player) end def formatShortMove(x, y, player) return formatMove(formatSimpleMove(x, y, player)) end end class LittleGolemParser def initialize(game) @game = game end def getPlayer1 p = @game.scan(/PB\[[^\]]*\]/)[0] return p[3..(p.length - 2)] end def getPlayer2 p = @game.scan(/PW\[[^\]]*\]/)[0] return p[3..(p.length - 2)] end def getMoves moves = @game.scan(/[br]\[[^\]]*\]/) end def getWinner p = @game.scan(/[br]\[resign\]/)[0] if p == nil return nil else return " rb".index(p[0]) end end def forEachMove letters=" abcdefghijklmnopqrstuvwx" moves = getMoves # swap rule if moves.length > 1 and moves[1] == "r[swap]" move = moves[0] x = letters.index(move[3]) y = letters.index(move[2]) yield x, y, 1 # place red peg first so Jtwixt doesn't get confused yield x, y, 2 # then put black peg right on top of it. moves.delete_at(1) # remove the swap move moves.delete_at(0) # remove the first move end # the rest of the moves for move in moves if move.length > 4 and (move[4..4] == "]" or move[4..4] == "|") player = " br".index(move[0]) x = letters.index(move[2]) y = letters.index(move[3]) yield x, y, player end end end end def getJtwixtFileData(lgolem) jtwixt = JTwixtFormatter.new jtwixtFileData = jtwixt.formatStandardTwixtHeader(lgolem.getPlayer1, lgolem.getPlayer2) lgolem.forEachMove { |x, y, player| jtwixtFileData += jtwixt.formatShortMove(x, y, player) } return jtwixtFileData + jtwixt.format2byteInt(0) end def getGame(httpConnection, gameId) directory = "/jsp/game/png.jsp?gid=" lgResponse, lgData = httpConnection.get(directory + gameId) if lgResponse.code == "500" print "No such game: ", gameId, "\n" elsif lgResponse.code != "200" print "HTTP error ", lgResponse.code, " for game: ", gameId, "\n" else lgolem = LittleGolemParser.new(lgData) jtwixtFileData = getJtwixtFileData(lgolem) jtwixtFileName = "game" + gameId + ".tgt" File.new(jtwixtFileName, "wb").write(jtwixtFileData) print jtwixtFileName, ": ", (lgolem.getWinner == 1)? "*":"", lgolem.getPlayer1, " vs. ", (lgolem.getWinner == 2)? "*":"", lgolem.getPlayer2, "\n" end end httpConnection = Net::HTTP::new("www.littlegolem.net") for gameId in ARGV getGame(httpConnection, gameId) end