Monthly Archives: July 2011

Dune 2, .PAK files unPAKer in python.

about 15 years ago i wrote an unpacker for dune 2 .pak files (in pascal). i wanted to have the .voc (creative voice files).i decided to take a couple of hours to figure out (again) the format for these pak files and write a python script to unpack them. its probably useless these days but maybe someone can use it ūüôā

import struct
import os

def unPAK(filename):
    print "unPAKING:", filename
    fnlist = []
    filesize = os.path.getsize(filename)
    f = open(filename,"rb")
    count = 0;
    size = f.read(4)
    beginData =  struct.unpack('i', size)[0]
    print beginData
    data = f.read(beginData-4)
    data = filter(None, data.split('\x00'))
    print data
    fnlist.append((data[0],beginData))
    for i in range(1,len(data)):
        if ((i % 2) == 1):
            if len(data[i]) == 1:
                print i, data[i]
                pos = struct.unpack('i',data[i]+"\x00\x00\x00")
            elif len(data[i]) == 2:
                print i, data[i]
                pos = struct.unpack('i',data[i]+"\x00\x00")
            elif len(data[i]) == 3:
                print i, data[i]
                pos = struct.unpack('i',data[i]+"\x00")
            fnlist.append((data[i+1],pos[0]))
    print fnlist
    for i in range(1,len(fnlist)):
          total = int(fnlist[i][1]) - int(fnlist[i-1][1])
          print "saving file: ",fnlist[i-1][0], "total bytes", total
          fdata = f.read(total)
          f2 = open(fnlist[i-1][0],"w")
          f2.write(fdata)
          f2.close() 

    #last file.
    print filesize
    total = filesize - int(fnlist[i][1])
    print "saving file: ",fnlist[i][0], "total bytes", total
    f2 = open(fnlist[i][0],"w")
    f2.write(f.read(total))
    f2.close()
    print fnlist

    f.close()

unPAK("DUNE.PAK")
unPAK("ENGLISH.PAK")
unPAK("FINALE.PAK")
unPAK("HARK.PAK")
unPAK("HERC.PAK")
unPAK("INTRO.PAK")
unPAK("INTROVOC.PAK")
unPAK("MENTAT.PAK")
unPAK("MERC.PAK")
unPAK("ORDOS.PAK")
unPAK("SCENARIO.PAK")
unPAK("SOUND.PAK")
unPAK("VOC.PAK")

#unPAK("ATRE.PAK")
#unPAK("XTRE.PAK")

Facebook puzzles – solutions (battleship + simon says w/ thrift)

A year ago i was looking for a job, Facebook was a valid choice. but before they would contact you, you would have to solve their puzzles. These puzzles have various difficulties, and i managed to solve quite a few, i am publishing some of my solutions here so that people can understand a little bit about thrift.

 

the first one is Simon says (the easiest) and it is only there to get you going with the networking protocol of thrift.

#!/usr/bin/env python
import sys
from random import randrange
sys.path.append('../gen-py')

import SimonSays 
from ttypes import *
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

# Make socket
transport = TSocket.TSocket('thriftpuzzle.facebook.com', 9030)  
#transport = TSocket.TSocket('localhost', 9090)

# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)

# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)

# Create a client to use the protocol encoder
client = SimonSays.Client(protocol)

# Connect!
transport.open()

#Player one or two (two is run via command line) 
if len(sys.argv) <= 1:
    emailAddress = 'blah@gmail.com'
    gameID = client.registerClient(emailAddress)
    print 'gameID =', gameID
else:
    joinstatus = False
    emailAddress = 'blah2@gmail.com'    
    try:
        gameID = int(sys.argv[1])
        print gameID,'from command argument'
        joinstatus = client.join(gameID, emailAddress)
    except DuplicateEmailException:
        print "bad email"

    if joinstatus == False:
        print "Failed to join game: " + str(gameID)
        sys.exit(1)

endT = False
while endT==False:
    listC = client.startTurn()
    print 'list',listC
    for color in listC:
        print 'c',color
        res = client.chooseColor(color)
        print res
    endT = client.endTurn()
print client.winGame() 
# Close!
transport.close()

The second one is a little harder, it was the classic battleship game. if you look around the web hard enough you will find all the best tactics to win ūüôā

import sys
from random import randrange
sys.path.append('../gen-py')

import Battleship2
#from battleship2 import Battleship2
from ttypes import *
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

class Player:
    attackPattern = []
    attackNextCellIndex = 0
    attackPatternLength = 0
    emailAddress = ''
    hitLocations = {}
    state = 0 #0 is search,  1 is try to sink via stack
    hitStack = []
    roundRobin = [(0,-1),(0,1),(-1,0),(1,0)]
    currentRobin = roundRobin[:]
    
    def __init__(self,client,emailAddress):
        self.client = client
        self.placePieces()
        self.emailAddress = emailAddress
        print 'finished placing', emailAddress   
        self.createPattern()
        
    def placePieces(self):
        for piece in xrange(1, 6):
            while not self.client.placePiece(piece, 
                         Coordinate(randrange(10), randrange(10)), bool(randrange(2))):
                pass  
    
    def createPattern(self): 
        for i in range(0,10):
            for j in range(0,10):
                if (j % 2 ==0):
                    if (i % 2 ==0):
                        self.attackPattern.append((i,j))
                else:
                    if (i % 2 ==1):
                        self.attackPattern.append((i,j))
        print self.attackPattern
        self.attackPatternLength = len(self.attackPattern)
    
    def changeStateHit(self):        
        self.state = 1 
        
    def changeStateSearch(self):       
        self.state = 0    
    
    def resetRobin(self):
        self.currentRobin = self.roundRobin[:] 
                    
    def addHitStack(self,result,x,y):
        if result==6:
            self.hitStack.append((x,y))
            self.changeStateHit()   
            
    def attackCell(self,x,y):
        result =  self.client.attack(Coordinate(x,y))
        if result in [1,2,3,4]:
            print 'sunk ship=>',result
        return result
        
    def attackNext(self):
        print self.attackNextCellIndex,'my turn, i am gonna kill this guy'
        x,y = self.attackPattern[self.attackNextCellIndex]
        if self.attackNextCellIndex0:
                    "get next most probable cell (for now its the first)"
                    (a,b) = self.currentRobin.pop()
                    "if cell between bounds of grid"
                    if a+x>=0 and a+x<=9 and b+y>=0 and b+y<=9:
                        result = self.attackCell(x+a,y+b)
#                        if result==6:
#                            self.resetRobin()
                        self.addHitStack(result, x+a, y+b)
                        foundNextCell = True
        "may need to put code that knows orientation of ship "
        "if there are two shots next to each other"            
                 
    def hitNext(self):
        "keep searchin"
        if self.state==0: 
            self.attackNext()                    
        else:
            "attack known areas"
            self.attackFirstInStack()
        

# Make socket
transport = TSocket.TSocket('thriftpuzzle.facebook.com', 9031)  
#transport = TSocket.TSocket('localhost', 9090)

# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)

# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)

# Create a client to use the protocol encoder
client = Battleship2.Client(protocol)

# Connect!
transport.open()

    
#Player one or two (two is run via command line) 
if len(sys.argv) <= 1:
    emailAddress = 'blah@gmail.com'
    gameID = client.registerClient(emailAddress)
    print 'gameID =', gameID
else:
    joinstatus = False
    emailAddress = 'blah2@gmail.com'    
    try:
        gameID = int(sys.argv[1])
        print gameID,'from command argument'
        joinstatus = client.join(gameID, emailAddress)
    except DuplicateEmailException:
        print "bad email"

    if joinstatus == False:
        print "Failed to join game: " + str(gameID)
        sys.exit(1)
      
#create a player
player = Player(client,emailAddress)            

totalMoves = 0
while totalMoves<1000:
    totalMoves+=1
    try:
        isTurn = client.isMyTurn()
    except GameOverException:
        break
    if isTurn:    
        player.hitNext()
        print client.winGame() 
# Close!
transport.close()