konaneAgent / main.py
main.py
Raw
import sys, os, game_rules, curses, game_manager
import player

class Display(object):
    def __init__(self, stdscr, manager):
        super(Display, self).__init__()
        self._stdscr = stdscr
        self._gm = manager
        self.SetDimensions()

        self._cursor = self._start
        self._last = 0

        self._p1state = self._p2state = None
        if isinstance(self._gm.p1, player.HumanPlayer): self._p1state = self.PlayerInputState()
        if isinstance(self._gm.p2, player.HumanPlayer): self._p2state = self.PlayerInputState()

        inbounds = self._dimensions > (self._gm.rows+3,self._gm.cols+3)
        # ASSERRTIONS ARE CONTRACTS WITHOUT BLAME.
        # We do this so that the screen isn't too small. There will still be problems if people resize.
        assert inbounds, "Screen too small for board: {} vs {}".format((self._gm.rows, self._gm.cols), self._dimensions)

    def MoveCursor(self, dx, dy):
        x, y = self._cursor
        height,width = self._dimensions
        x = min(width - 1, max(0, x+dx))
        y = min(height - 2, max(0, y+dy))

        self._cursor = (x,y)


    def SetDimensions(self):
        self._dimensions = self._stdscr.getmaxyx()
        height,width = self._dimensions
        self._center = (width//2,height//2)
        self._start = (width//2 - self._gm.cols//2, height//2 - self._gm.rows//2)


    def Loop(self):
        stdscr = self._stdscr
        stdscr.clear()

        self.SetDimensions()
        self.Draw(stdscr)

        cursor_x, cursor_y = self._cursor
        stdscr.move(cursor_y, cursor_x)        
        self.ProcessInput(stdscr)

        stdscr.refresh()


    def Draw(self, stdscr):
        self.DrawBoard(stdscr)
        self.DrawStatus(stdscr)


    def DrawBoard(self, stdscr):
        height, width = self._dimensions
        cursor_x, cursor_y = self._cursor

        board = self._gm.board
        center_x,center_y = self._center
        start = self._start
        for i, row in enumerate(board):
            string = ''.join(row)
            stdscr.addstr(start[1]+i, start[0], string)

        stdscr.attron(curses.color_pair(2))
        if self._gm.GetTurn() == 'x' and self._p1state and self._p1state.selected:
            pos = self._p1state.selected; stdscr.addstr(pos[1], pos[0], 'x')
        if self._gm.GetTurn() == 'o' and self._p2state and self._p2state.selected:
            pos = self._p2state.selected; stdscr.addstr(pos[1], pos[0], 'o')
        stdscr.attroff(curses.color_pair(2))


    def DrawStatus(self, stdscr):
        symbol = self._gm.GetTurn().capitalize()
        player = self._gm.p1 if symbol == 'X' else self._gm.p2
        
        text = ' Press \'q\' to quit'
        if self._gm.GetWinner(): text += ' | {} wins!'.format(self._gm.GetWinner())
        else: text += ' | Turn {0:{width}} ({1}, {2}) '.format(self._gm.turn_number, symbol, player, width=3)
        height, width = self._dimensions
        
        stdscr.attron(curses.color_pair(3))
        stdscr.addstr(height-1, 0, text)
        stdscr.addstr(height-1, len(text), ' ' * (width - len(text) - 1))
        stdscr.attroff(curses.color_pair(3))


    def ProcessInput(self, stdscr):
        self._last = stdscr.getch()

        if   self._last == curses.KEY_DOWN:  self.MoveCursor(0,1)
        elif self._last == curses.KEY_UP:    self.MoveCursor(0,-1)
        elif self._last == curses.KEY_RIGHT: self.MoveCursor(1,0)
        elif self._last == curses.KEY_LEFT:  self.MoveCursor(-1,0)
        elif self._last == 32: self.TakeTurn()
        elif self._last == ord('q'): self._gm.interrupt(None, None)


    def TakeTurn(self):
        if self._gm.GetTurn() == 'x' and self._p1state:
            if self._p1state.selected:
                pair = self.GetMovePair(self._p1state.selected, self._cursor)
                self._gm._takeTurn(pair)
                self._p1state.Deselect()
            else: self._p1state.Select(self._cursor)
        elif self._gm.GetTurn() == 'o' and self._p2state:
            if self._p2state.selected:
                pair = self.GetMovePair(self._p2state.selected, self._cursor)
                self._gm._takeTurn(pair)
                self._p2state.Deselect()
            else: self._p2state.Select(self._cursor)
        else: self._gm._takeTurn()

    def GetMovePair(self, start, end):
        startpos = (start[1] - self._start[1], start[0] - self._start[0])
        endpos = (end[1] - self._start[1], end[0] - self._start[0])
        return (startpos, endpos)


    class PlayerInputState(object):
        """Container class for input state. Just keep adding layers of abstraction; it's the computer science way."""
        def __init__(self): self.selected = None
        def __str__(self): return str(self.selected)
        def Select(self, cursor): self.selected = cursor
        def Deselect(self): self.selected = None


def MakeBoard(p1, p2):
    iterations = 1 
    depth = 3
    rows, cols = 10, 10
    gm = game_manager.GameManager(rows, cols, player.makePlayer(p1, 'x', depth), player.makePlayer(p2, 'o', depth), True)

    return gm

def Init(stdscr):
    stdscr.clear()
    stdscr.refresh()
    stdscr.keypad(True)
    curses.start_color()
    curses.init_pair(1, curses.COLOR_CYAN, curses.COLOR_BLACK)
    curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
    curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)

def Loop(stdscr, p1, p2):
    Init(stdscr)

    gm = MakeBoard(p1, p2)
    display = Display(stdscr, gm)
    while True: display.Loop()


if __name__ == '__main__':
    args = sys.argv
    p1, p2 = 'H', 'R'

    if len(args) == 3:
        p1 = str(args[1]).capitalize()
        p2 = str(args[2]).capitalize()

    if p1 not in ['A', 'M', 'R', 'D', 'H']: p1 = 'H'
    if p2 not in ['A', 'M', 'R', 'D', 'H']: p2 = 'R'
    
    curses.wrapper(Loop, p1, p2)