#! /usr/bin/python # nDot: Skinny lines and boxes player. No lookahead. Smarter evaluator. # v0.2 2-3-06 MJS (67 lines, 40 code) import sys, random, bisect, time, os, string # Coordinate input and output functions: def conv(cp): return ( string.uppercase.index(cp[0]), int(cp[1])-1 ) def mkmove(mv): return ' '.join(['%s%u'%(string.uppercase[s],d+1) for s,d in mv]) # Read in file and store metadata: data = [z for z in open(sys.argv[1]).readlines() if z.strip() and z.strip()[0]!='#'] me, ht, wd = [eval(z) for z in data[0].split()] # Set up data structures: cells, edges, moveopt, ranked = {}, {}, {}, [] # cells[(x,y)] -> [ (x,y), WallCount, SouthWall, WestWall, EastWall, NorthWall ] # edges[((x,y),(x,y))] -> [ ((x,y),(x,y)), fWallHere, zero|CellN, zero|CellP ] POS, WNUM, SWALL, WWALL, EWALL, NWALL = range(6) # Cell record index values VEC, USED, NCELL, PCELL = range(4) # Edge record index values for x,y in [(x,y) for x in range(wd) for y in range(ht)] : cellrec = [(x,y),0] for a,b,c,d in [(0,0,1,0),(0,0,0,1),(1,0,1,1),(0,1,1,1)]: vec = ( (x+a,y+b), (x+c,y+d) ) edgerec = edges.get( vec, [vec,0,0,0] ) edgerec[PCELL-c*d]= cellrec # pos/neg cells decided by PCELL-c*d!! edges[vec] = edgerec cellrec.append( edgerec ) cells[(x,y)] = cellrec # Compute all move options for board: for x,y in [(x,y) for x in range(wd+1) for y in range(ht+1)] : xlist = [(vx,y) for vx in range(x,wd+1)] ylist = [(x,vy) for vy in range(y,ht+1)] xvec,yvec = [zip(z,z[1:]) for z in (xlist,ylist)] [[moveopt.update( {tuple(z[:i]):1} ) for i in range(1,len(z)+1)] for z in (xvec,yvec)] # Update data structures from moves in datafile: for p,c1,c2 in [ z.upper().split() for z in data[3:] ] : v1, v2 = min(conv(c1),conv(c2)), max(conv(c1),conv(c2)) vertset = [(x,y) for x in range(v1[0],v2[0]+1) for y in range(v1[1],v2[1]+1)] for vec in zip( vertset, vertset[1:] ) : edges[vec][USED] = 1 for c in edges[vec][NCELL:PCELL+1] : if c: c[WNUM] += 1 # Compute move types available: (NOT USED) # boxlist = [ e for e in edges.values() if [c for c in e[NCELL:] if c and c[WNUM]==3]!=[] ] # goodlist = [ e for e in edges.values() if [c for c in e[NCELL:] if c and c[WNUM]>1]==[] ] # Compute next move (avoid free boxes for opponent if possible): mlist = [ s for s in moveopt if [v for v in s if edges[v][USED]==1]==[] ] for move in mlist: celln, cellp = [ [ edges[v][z] for v in move ] for z in (NCELL,PCELL) ] boxc = len( [1 for c in celln+cellp if c and c[WNUM]==3] ) giftn, giftp = [ len( [1 for c in z if c and c[WNUM]==2] ) for z in (celln,cellp) ] # value = boxc - max(giftn,giftp)*.7 + len(move)*.01 # General best evaluator value = boxc - (giftn+giftp)*0.8 - len(move)*.01 # Mean version for systest1 value += len([1 for c in celln+cellp if c and c[WNUM]==1]) * .001 bisect.insort( ranked, (value, move) ) # Output move: bestvalue,bestmove = ranked[-1] move = random.choice( [m for v,m in ranked if v==bestvalue] ) # for v,m in ranked: print v, mkmove( (m[0][0],m[-1][1]) ) ##DEBUG #move = random.choice( mlist ) print mkmove( (move[0][0],move[-1][1]) )