《付録》hexothello.py

# -*- coding: utf-8 -*-
#===============================================================================
#    Copyright (C) 2000-2008, 小泉ひよ子とタマゴ倶楽部
#
# History: Othello Game
#    1988/05, Smalltalk
#    2004/09, Java
#    2005/02, C#
#    2005/03, Jython
#    2008/01, Jython
#===============================================================================

#===============================================================================
#    配列と別れる50の方法
#===============================================================================
from game import *

#===============================================================================
class HexOthelloFrame(DefaultFrame):
    def initialize(self):
        self.panel = HexOthelloPanel()
        
    def initializeComponent(self):
        self.layout = BorderLayout()
        self.add(self.panel, BorderLayout.CENTER)

#===============================================================================
class HexOthelloPanel(GameBoardPanel):
    dim = 8
    black = False
    white = not black
    _bounds = (-1, -1), (1, -1), (2, 0)
    _bounds = list(_bounds)+[(-x, -y) for x, y in _bounds]
    
    def __init__ (self):
        GameBoardPanel.__init__(self)
        self.mode = self.black
        
    def locateItems(self):
        for x, y in self._items():
            self.items.append(NullStone(x, y, None))
        for x, y, color in (
                  (6 , 3, self.black), 
                  (9 , 4, self.black), 
                  (6 , 5, self.black), 
                  (10, 3, self.white), 
                  (7 , 4, self.white), 
                  (10, 5, self.white)):
            self.place(HexStone(x, y, color))

    def _items(self):
        s = 
        for y, points in enumerate((
                   range(3, 14, 2), 
                   range(2, 15, 2), 
                   range(1, 16, 2), 
                   range(0, 17, 2), 
                   range(1, 16, 2), 
                   range(0, 17, 2), 
                   range(1, 16, 2), 
                   range(2, 15, 2), 
                   range(3, 14, 2))):
            s += [(x, y) for x in points]
        return s
        
    def prepare(self):
        self.__class__.itemExtent = (
            self.width/self.dim, self.height/self.dim)

    def this_mouseClicked(self, e):
        stone = self.detectStone(e)
        print ">>> this_mouseClicked: %s"%stone
        if stone: return
        
        if stone is nullStone:
            print "::: click again ..."
            return

        self.reversed = False
        self.reverse(stone)
        if not self.reversed: return
        
        self.place(HexStone(stone.x, stone.y, self.mode))
        self.mode = not self.mode
        self.repaint()

    def detectStone(self, e):
        return self.detectPoint(e.x, e.y)

    def nullObject(self):
        return nullStone
                    
    def reverse(self, stone):
        for x, y in self._bounds:
            e = self.detect(stone.x+x, stone.y+y)
            if e.state == (not self.mode):
                self.reverseStones(e, x, y)

    def detectPoint(self, x, y):
        obj = self.nullObject()
        for e in self.items:
            if e.isExistPoint(x, y): obj = e; break
        return obj

    def reverseStones(self, stone, x, y):
        stones = [stone]
        for i in range(1, self.dim):
            e = self.detect(stone.x+x*i, stone.y+y*i)
            if e.state == None:
                stones = ; break
            if e.state == self.mode:
                break
            if e.state == (not self.mode):
                stones.append(e)
        if not stones: return
        for e in stones:
            e.state = self.mode
        self.reversed = True
        print stones
        
    def place(self, stone):
        px, py = stone._points()
        null = self.detectPoint(px, py)
        self.items.remove(null)
        self.items.append(stone)
        print "place: %s"%stone, "%s,%s"%(px, py)

#===============================================================================
class HexStone(GameItem):
    _dx, _dy = 7, 4        # 7*7+4*4=65 <=> 8*8=64
    _R2 = (_dx*2)**2
    _width, _height = _dx*2, _dy*4
    _dw, _dh = _dx*2, _dy*3
    deltaX, deltaY  = _dw+1, (_dh+1)*2
    vertices = [(_dx*x, _dy*y) for x, y in
        (1, 0), (2, 1), (2, 3), (1, 4), (0, 3), (0, 1)]
    _nPoints = len(vertices)
    
    def __init__(self, x, y, state):
        GameItem.__init__(self, x, y)
        self.state = state
        
    def __repr__(self):
        if self.state == None:
            s = self.state
        else:
            s = ("black", "white")[not self.state]
        return "(%s,%s)"%(GameItem.__repr__(self), s)
    
    def __nonzero__(self): return True

    def dim(self): return OthelloPanel.dim

    def paintBackground(self, g):
        width = self.width(g)
        height = self.height(g)
        x = self.x * width
        y = self.y * height
        xpoints = [x+dx*2 for dx, dy in self.vertices]
        ypoints = [y+dy*2 for dx, dy in self.vertices]
        N = self._nPoints
        g.color = Color.green
        g.fillPolygon(xpoints, ypoints, N)
        g.color = Color.black
        g.drawPolygon(xpoints, ypoints, N)

    def paintItem(self, g):
        width = self.width(g)
        height = self.height(g)
        x = self.x * width
        y = self.y * height
        color = (Color.black, Color.white)[self.state]
        xpoints = [x+dx*2 for dx, dy in self.vertices]
        ypoints = [y+dy*2 for dx, dy in self.vertices]
        N = self._nPoints
        g.color = color
        g.fillPolygon(xpoints, ypoints, N)
        g.color = Color.black
        g.drawPolygon(xpoints, ypoints, N)

#===============================================================================
    def width (self, g): return self.deltaX
    def height(self, g): return self.deltaY

    def isExistPoint(self, x, y):
        px, py = self._points()
        X, Y = x-px, y-py
        return X*X+Y*Y <= self._R2
    
    def _points(self):
        x = self.x*self.deltaX+self._width 
        y = self.y*self.deltaY+self._height
        return x, y

class NullStone(HexStone):
    def __nonzero__(self): return False
    def paintItem(self, g): pass
        
N = HexOthelloPanel.dim + 1
nullStone = NullStone(None, None, None)
        
#===============================================================================
def example():
    HexOthelloFrame("Othello: hexagon", (270, 262))

#===============================================================================
from _jython2_ import *
signature("-", __file__, '1.0.1')
#===============================================================================