コード例 #1
0
ファイル: hokm.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Hokm, self).__init__(server, table_name)

        self.game_display_name = "Hokm"
        self.game_name = "hokm"

        self.state = State("need_players")
        self.prefix = "(^RHokm^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Hokm-specific stuff.
        self.goal = 7
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.hakem = None
        self.winner = None

        # Default to four-player mode.
        self.mode = 4
        self.short = True
        self.setup_mode()
コード例 #2
0
ファイル: metamorphosis.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Metamorphosis, self).__init__(server, table_name)

        self.game_display_name = "Metamorphosis"
        self.game_name = "metamorphosis"
        self.seats = [Seat("Black"), Seat("White")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RMetamorphosis^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Metamorphosis-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 12
        self.ko_fight = True
        self.group_count = None
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[0].data.last_was_ko = False
        self.seats[1].data.side = WHITE
        self.seats[1].data.last_was_ko = False
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()
コード例 #3
0
ファイル: breakthrough.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Breakthrough, self).__init__(server, table_name)

        self.game_display_name = "Breakthrough"
        self.game_name = "breakthrough"
        self.seats = [Seat("Black"), Seat("White")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RBreakthrough^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Breakthrough-specific stuff.
        self.width = 8
        self.height = 8
        self.turn = None
        self.black = self.seats[0]
        self.white = self.seats[1]
        self.resigner = None
        self.layout = None

        # We cheat and create a black and white piece here.  Breakthrough
        # doesn't differentiate between pieces, so this allows us to do
        # comparisons later.
        self.bp = Piece("^K", "b", "B")
        self.bp.data.owner = self.black
        self.wp = Piece("^W", "w", "W")
        self.wp.data.owner = self.white

        self.init_board()
コード例 #4
0
    def __init__(self, server, table_name):

        super(SquareOust, self).__init__(server, table_name)

        self.game_display_name = "Square Oust"
        self.game_name = "square_oust"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RSquare Oust^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Square Oust-specific stuff.
        self.height = 11
        self.width = 11
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.groups = []
        self.black.data.made_move = False
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.groups = []
        self.white.data.made_move = False
        self.resigner = None
        self.layout = None
        self.move_was_capture = False

        # Initialize the starting layout.
        self.init_layout()
コード例 #5
0
    def __init__(self, server, table_name):

        super(Gonnect, self).__init__(server, table_name)

        self.game_display_name = "Gonnect"
        self.game_name = "gonnect"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RGonnect^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Gonnect-specific stuff.
        self.turn = None
        self.seats[0].data.side = BLACK
        self.seats[0].data.dir_str = "/Vertical"
        self.seats[1].data.side = WHITE
        self.seats[1].data.dir_str = "/Horizontal"
        self.directional = False
        self.resigner = None
        self.turn_number = 0
        self.goban = giles.games.goban.Goban()
        self.adjacency_map = None
        self.found_winner = False

        # A traditional Gonnect board is 13x13.
        self.goban.resize(13, 13)
コード例 #6
0
    def __init__(self, server, table_name):

        super(Crossway, self).__init__(server, table_name)

        self.game_display_name = "Crossway"
        self.game_name = "crossway"
        self.seats = [Seat("Black"), Seat("White")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RCrossway^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Crossway-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 19
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[1].data.side = WHITE
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()
コード例 #7
0
ファイル: tanbo.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Tanbo, self).__init__(server, table_name)

        self.game_display_name = "Tanbo"
        self.game_name = "tanbo"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RTanbo^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Tanbo-specific stuff.
        self.size = 19
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.root_list = []
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.root_list = []
        self.resigner = None
        self.layout = None

        # Initialize the starting layout.
        self.init_layout()
コード例 #8
0
ファイル: ataxx.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Ataxx, self).__init__(server, table_name)

        self.game_display_name = "Ataxx"
        self.game_name = "ataxx"
        self.seats = [
            Seat("Red"),
            Seat("Blue"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RAtaxx^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Ataxx-specific stuff.
        self.board = None
        self.printable_board = None
        self.sides = {}
        self.size = 7
        self.player_mode = 2
        self.turn = None
        self.last_r = None
        self.last_c = None

        self.init_seats()
        self.init_board()
コード例 #9
0
ファイル: capture_go.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(CaptureGo, self).__init__(server, table_name)

        self.game_display_name = "Capture Go"
        self.game_name = "capturego"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RCapture Go^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Capture Go-specific stuff.
        self.turn = None
        self.seats[0].data.side = BLACK
        self.seats[1].data.side = WHITE
        self.seats[0].data.capture_list = []
        self.seats[1].data.capture_list = []
        self.capture_goal = 1
        self.resigner = None
        self.turn_number = 0
        self.goban = giles.games.goban.Goban()
コード例 #10
0
ファイル: tanbo.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Tanbo, self).__init__(server, table_name)

        self.game_display_name = "Tanbo"
        self.game_name = "tanbo"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RTanbo^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Tanbo-specific stuff.
        self.size = 19
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.root_list = []
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.root_list = []
        self.resigner = None
        self.layout = None

        # Initialize the starting layout.
        self.init_layout()
コード例 #11
0
ファイル: talpa.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Talpa, self).__init__(server, table_name)

        self.game_display_name = "Talpa"
        self.game_name = "talpa"
        self.seats = [Seat("Red"), Seat("Blue")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RTalpa^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Talpa-specific stuff.
        self.size = 8
        self.turn = None
        self.red = self.seats[0]
        self.red.data.seat_str = "^RRed/Vertical^~"
        self.blue = self.seats[1]
        self.blue.data.seat_str = "^BBlue/Horizontal^~"
        self.resigner = None
        self.layout = None

        # Like in most connection games, there is no difference between pieces
        # of a given color, so we save time and create our singleton pieces
        # here.
        self.rp = Piece("^R", "x", "X")
        self.rp.data.owner = self.red
        self.bp = Piece("^B", "o", "O")
        self.bp.data.owner = self.blue

        # Initialize the starting layout.
        self.init_layout()
コード例 #12
0
ファイル: hokm.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Hokm, self).__init__(server, table_name)

        self.game_display_name = "Hokm"
        self.game_name = "hokm"

        self.state = State("need_players")
        self.prefix = "(^RHokm^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Hokm-specific stuff.
        self.goal = 7
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.hakem = None
        self.winner = None

        # Default to four-player mode.
        self.mode = 4
        self.short = True
        self.setup_mode()
コード例 #13
0
ファイル: metamorphosis.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Metamorphosis, self).__init__(server, table_name)

        self.game_display_name = "Metamorphosis"
        self.game_name = "metamorphosis"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RMetamorphosis^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Metamorphosis-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 12
        self.ko_fight = True
        self.group_count = None
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[0].data.last_was_ko = False
        self.seats[1].data.side = WHITE
        self.seats[1].data.last_was_ko = False
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()
コード例 #14
0
ファイル: square_oust.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(SquareOust, self).__init__(server, table_name)

        self.game_display_name = "Square Oust"
        self.game_name = "square_oust"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RSquare Oust^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Square Oust-specific stuff.
        self.height = 11
        self.width = 11
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.groups = []
        self.black.data.made_move = False
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.groups = []
        self.white.data.made_move = False
        self.resigner = None
        self.layout = None
        self.move_was_capture = False

        # Initialize the starting layout.
        self.init_layout()
コード例 #15
0
ファイル: crossway.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Crossway, self).__init__(server, table_name)

        self.game_display_name = "Crossway"
        self.game_name = "crossway"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RCrossway^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Crossway-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 19
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[1].data.side = WHITE
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()
コード例 #16
0
ファイル: breakthrough.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Breakthrough, self).__init__(server, table_name)

        self.game_display_name = "Breakthrough"
        self.game_name = "breakthrough"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RBreakthrough^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Breakthrough-specific stuff.
        self.width = 8
        self.height = 8
        self.turn = None
        self.black = self.seats[0]
        self.white = self.seats[1]
        self.resigner = None
        self.layout = None

        # We cheat and create a black and white piece here.  Breakthrough
        # doesn't differentiate between pieces, so this allows us to do
        # comparisons later.
        self.bp = Piece("^K", "b", "B")
        self.bp.data.owner = self.black
        self.wp = Piece("^W", "w", "W")
        self.wp.data.owner = self.white

        self.init_board()
コード例 #17
0
ファイル: gonnect.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Gonnect, self).__init__(server, table_name)

        self.game_display_name = "Gonnect"
        self.game_name = "gonnect"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RGonnect^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Gonnect-specific stuff.
        self.turn = None
        self.seats[0].data.side = BLACK
        self.seats[0].data.dir_str = "/Vertical"
        self.seats[1].data.side = WHITE
        self.seats[1].data.dir_str = "/Horizontal"
        self.directional = False
        self.resigner = None
        self.turn_number = 0
        self.goban = giles.games.goban.Goban()
        self.adjacency_map = None
        self.found_winner = False

        # A traditional Gonnect board is 13x13.
        self.goban.resize(13, 13)
コード例 #18
0
ファイル: poison.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Poison, self).__init__(server, table_name)

        self.game_display_name = "Poison"
        self.game_name = "poison"
        self.seats = [
            Seat("Alpha"),
            Seat("Bravo"),
            Seat("Charlie"),
            Seat("Delta"),
            Seat("Echo"),
            Seat("Foxtrot"),
            Seat("Golf"),
            Seat("Hotel"),
            Seat("India"),
            Seat("Juliet")
        ]
        self.state = State("need_players")
        self.prefix = "(^RPoison^~) "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)
        self.min_players = 3
        self.max_players = 10

        # Default configuration.
        self.antidote_count = 3
        self.poison_count = 1
        self.goal = 2

        # Mutable information.
        self.turn = None
        self.highest_bidder = None
コード例 #19
0
ファイル: ataxx.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Ataxx, self).__init__(server, table_name)

        self.game_display_name = "Ataxx"
        self.game_name = "ataxx"
        self.seats = [
            Seat("Red"),
            Seat("Blue"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RAtaxx^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Ataxx-specific stuff.
        self.board = None
        self.printable_board = None
        self.sides = {}
        self.size = 7
        self.player_mode = 2
        self.turn = None
        self.last_r = None
        self.last_c = None

        self.init_seats()
        self.init_board()
コード例 #20
0
ファイル: expeditions.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Expeditions, self).__init__(server, table_name)

        self.game_display_name = "Expeditions"
        self.game_name = "expeditions"
        self.seats = [
            Seat("Left"),
            Seat("Right")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RExpeditions^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Expeditions-specific stuff.
        self.suit_count = 5
        self.agreement_count = 3
        self.penalty = 20
        self.bonus = True
        self.bonus_length = 8
        self.bonus_points = 20
        self.hand_size = 8
        self.goal = 1

        self.turn = None
        self.draw_pile = None
        self.discards = []
        self.left = self.seats[0]
        self.right = self.seats[1]
        self.left.data.side = LEFT
        self.left.data.curr_score = 0
        self.left.data.overall_score = 0
        self.left.data.hand = None
        self.left.data.expeditions = []
        self.right.data.side = RIGHT
        self.right.data.curr_score = 0
        self.right.data.overall_score = 0
        self.right.data.hand = None
        self.right.data.expeditions = []
        self.resigner = None
        self.first_player = None
        self.just_discarded_to = None

        self.printable_layout = None
        self.init_hand()
コード例 #21
0
ファイル: redstone.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Redstone, self).__init__(server, table_name)

        self.game_display_name = "Redstone"
        self.game_name = "redstone"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RRedstone^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Redstone-specific stuff.
        self.height = 19
        self.width = 19
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.made_move = False
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.made_move = False
        self.resigner = None
        self.layout = None

        # Like most abstracts, Redstone doesn't need to differentiate between
        # the pieces on the board.
        self.bp = Piece("^K", "x", "X")
        self.bp.data.owner = self.black
        self.black.data.piece = self.bp
        self.wp = Piece("^W", "o", "O")
        self.wp.data.owner = self.white
        self.white.data.piece = self.wp
        self.rp = Piece("^R", "r", "R")
        self.rp.data.owner = None

        # Initialize the starting layout.
        self.init_layout()
コード例 #22
0
ファイル: forty_one.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(FortyOne, self).__init__(server, table_name)

        self.game_display_name = "Forty-One"
        self.game_name = "fortyone"

        # Seat ordering is "Persian."
        self.seats = [
            Seat("North"),
            Seat("West"),
            Seat("South"),
            Seat("East"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RForty-One^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # 41-specific stuff.
        self.goal = 41
        self.double = 7
        self.minimum = 11
        self.whist = False
        self.positive = True
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.north = self.seats[0]
        self.west = self.seats[1]
        self.south = self.seats[2]
        self.east = self.seats[3]

        self.north.data.who = NORTH
        self.west.data.who = WEST
        self.south.data.who = SOUTH
        self.east.data.who = EAST

        self.north.data.partner = self.south
        self.west.data.partner = self.east
        self.south.data.partner = self.north
        self.east.data.partner = self.west

        self.layout = FourPlayerCardGameLayout()

        # Set everyone's score to zero.
        for seat in self.seats:
            seat.data.score = 0
コード例 #23
0
    def __init__(self, server, table_name):

        super(RockPaperScissors, self).__init__(server, table_name)

        self.game_display_name = "Rock-Paper-Scissors"
        self.game_name = "rps"
        self.seats = [
            Seat("Left"),
            Seat("Right"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.plays = [None, None]
        self.prefix = "(^RRPS^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # RPS requires both seats, so may as well mark them active.
        self.seats[0].active = True
        self.seats[1].active = True
コード例 #24
0
    def __init__(self, server, table_name):

        super(Y, self).__init__(server, table_name)

        self.game_display_name = "Y"
        self.game_name = "y"
        self.seats = [
            Seat("White"),
            Seat("Black"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RY^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Y-specific guff.
        self.seats[0].data.color = WHITE
        self.seats[0].data.color_code = "^W"
        self.seats[1].data.color = BLACK
        self.seats[1].data.color_code = "^K"
        self.board = None
        self.printable_board = None
        self.size = 19
        self.empty_space_count = None
        self.master = False
        self.turn = None
        self.turn_number = 0
        self.move_list = []
        self.last_moves = []
        self.resigner = None
        self.adjacency = None
        self.found_winner = False

        # Y requires both seats, so may as well mark them active.
        self.seats[0].active = True
        self.seats[1].active = True

        self.init_board()
コード例 #25
0
ファイル: whist.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()
コード例 #26
0
    def __init__(self, server, table_name):

        super(Set, self).__init__(server, table_name)

        self.game_display_name = "Set"
        self.game_name = "set"
        self.seats = []
        self.min_players = 1
        self.max_players = 32767  # We don't even use this.
        self.state = State("need_players")
        self.prefix = "(^RSet^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Set-specific stuff.
        self.max_cards_on_table = DEFAULT_MAX_CARDS
        self.deal_delay = DEFAULT_DEAL_DELAY
        self.layout = None
        self.printable_layout = None
        self.deck = None
        self.last_play_time = None
        self.max_card_count = 81
        self.has_borders = True
コード例 #27
0
    def __init__(self, server, table_name):

        super(Talpa, self).__init__(server, table_name)

        self.game_display_name = "Talpa"
        self.game_name = "talpa"
        self.seats = [
            Seat("Red"),
            Seat("Blue"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RTalpa^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Talpa-specific stuff.
        self.size = 8
        self.turn = None
        self.red = self.seats[0]
        self.red.data.seat_str = "^RRed/Vertical^~"
        self.blue = self.seats[1]
        self.blue.data.seat_str = "^BBlue/Horizontal^~"
        self.resigner = None
        self.layout = None

        # Like in most connection games, there is no difference between pieces
        # of a given color, so we save time and create our singleton pieces
        # here.
        self.rp = Piece("^R", "x", "X")
        self.rp.data.owner = self.red
        self.bp = Piece("^B", "o", "O")
        self.bp.data.owner = self.blue

        # Initialize the starting layout.
        self.init_layout()
コード例 #28
0
ファイル: game.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        self.server = server
        self.channel = server.channel_manager.has_channel(table_name)
        if not self.channel:
            self.channel = self.server.channel_manager.add_channel(table_name,
                                                gameable=True, persistent=True)
        else:
            self.channel.persistent = True
        self.game_display_name = "Generic Game"
        self.game_name = "game"
        self.table_display_name = table_name
        self.table_name = table_name.lower()

        self.active = False
        self.private = False

        self.state = State("config")
        self.prefix = "(^RGame^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Override this next variable in your subclasses if you're not
        # done debugging them.
        self.debug = False
コード例 #29
0
ファイル: capture_go.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(CaptureGo, self).__init__(server, table_name)

        self.game_display_name = "Capture Go"
        self.game_name = "capturego"
        self.seats = [Seat("Black"), Seat("White")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RCapture Go^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Capture Go-specific stuff.
        self.turn = None
        self.seats[0].data.side = BLACK
        self.seats[1].data.side = WHITE
        self.seats[0].data.capture_list = []
        self.seats[1].data.capture_list = []
        self.capture_goal = 1
        self.resigner = None
        self.turn_number = 0
        self.goban = giles.games.goban.Goban()
コード例 #30
0
ファイル: expeditions.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Expeditions, self).__init__(server, table_name)

        self.game_display_name = "Expeditions"
        self.game_name = "expeditions"
        self.seats = [
            Seat("Left"),
            Seat("Right")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RExpeditions^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Expeditions-specific stuff.
        self.suit_count = 5
        self.agreement_count = 3
        self.penalty = 20
        self.bonus = True
        self.bonus_length = 8
        self.bonus_points = 20
        self.hand_size = 8
        self.goal = 1

        self.turn = None
        self.draw_pile = None
        self.discards = []
        self.left = self.seats[0]
        self.right = self.seats[1]
        self.left.data.side = LEFT
        self.left.data.curr_score = 0
        self.left.data.overall_score = 0
        self.left.data.hand = None
        self.left.data.expeditions = []
        self.right.data.side = RIGHT
        self.right.data.curr_score = 0
        self.right.data.overall_score = 0
        self.right.data.hand = None
        self.right.data.expeditions = []
        self.resigner = None
        self.first_player = None
        self.just_discarded_to = None

        self.printable_layout = None
        self.init_hand()
コード例 #31
0
ファイル: redstone.py プロジェクト: Cloudxtreme/giles
    def __init__(self, server, table_name):

        super(Redstone, self).__init__(server, table_name)

        self.game_display_name = "Redstone"
        self.game_name = "redstone"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RRedstone^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Redstone-specific stuff.
        self.height = 19
        self.width = 19
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.made_move = False
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.made_move = False
        self.resigner = None
        self.layout = None

        # Like most abstracts, Redstone doesn't need to differentiate between
        # the pieces on the board.
        self.bp = Piece("^K", "x", "X")
        self.bp.data.owner = self.black
        self.black.data.piece = self.bp
        self.wp = Piece("^W", "o", "O")
        self.wp.data.owner = self.white
        self.white.data.piece = self.wp
        self.rp = Piece("^R", "r", "R")
        self.rp.data.owner = None

        # Initialize the starting layout.
        self.init_layout()
コード例 #32
0
    def handle(self, player):

        state = player.state

        substate = state.get_sub()

        if substate == None:

            # Just logged in.  Print the helpful banner.
            player.tell_cc(
                "\n\n\n                       Welcome to ^G%s^~!\n\n" %
                self.server.name)
            player.tell_cc("Source URL: ^Y%s^~\n\n" % self.server.source_url)

            state.set_sub("entry_prompt")

        elif substate == "entry_prompt":

            # Ask them for their name and set our state to waiting for an entry.
            player.tell("\n\nPlease enter your name: ")

            state.set_sub("name_entry")

        elif substate == "name_entry":

            name = player.client.get_command()
            if name:

                # Attempt to set their name to the one they requested.
                is_valid = player.set_name(name)

                if is_valid:

                    # Welcome them and move them to chat.
                    player.tell("\nWelcome, %s!\n" % player)
                    player.state = State("chat")

                    self.server.log.log("%s logged in from %s." %
                                        (player, player.client.addrport()))

                else:
                    state.set_sub("entry_prompt")
コード例 #33
0
ファイル: rock_paper_scissors.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(RockPaperScissors, self).__init__(server, table_name)

        self.game_display_name = "Rock-Paper-Scissors"
        self.game_name = "rps"
        self.seats = [
            Seat("Left"),
            Seat("Right"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.plays = [None, None]
        self.prefix = "(^RRPS^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # RPS requires both seats, so may as well mark them active.
        self.seats[0].active = True
        self.seats[1].active = True
コード例 #34
0
ファイル: set.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Set, self).__init__(server, table_name)

        self.game_display_name = "Set"
        self.game_name = "set"
        self.seats = []
        self.min_players = 1
        self.max_players = 32767 # We don't even use this.
        self.state = State("need_players")
        self.prefix = "(^RSet^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Set-specific stuff.
        self.max_cards_on_table = DEFAULT_MAX_CARDS
        self.deal_delay = DEFAULT_DEAL_DELAY
        self.layout = None
        self.printable_layout = None
        self.deck = None
        self.last_play_time = None
        self.max_card_count = 81
        self.has_borders = True
コード例 #35
0
ファイル: whist.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()
コード例 #36
0
ファイル: game.py プロジェクト: sunfall/giles
    def __init__(self, server, table_name):

        self.server = server
        self.channel = server.channel_manager.has_channel(table_name)
        if not self.channel:
            self.channel = self.server.channel_manager.add_channel(table_name,
                                                gameable=True, persistent=True)
        else:
            self.channel.persistent = True
        self.game_display_name = "Generic Game"
        self.game_name = "game"
        self.table_display_name = table_name
        self.table_name = table_name.lower()

        self.active = False
        self.private = False

        self.state = State("config")
        self.prefix = "(^RGame^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Override this next variable in your subclasses if you're not
        # done debugging them.
        self.debug = False
コード例 #37
0
ファイル: hokm.py プロジェクト: Cloudxtreme/giles
class Hokm(SeatedGame):
    """A Hokm game table implementation.  Hokm is a Persian trick-taking
    card game of unknown provenance.  This implementation doesn't
    currently rearrange the seats at the start, but does support both the
    standard 4p partnership game and the quirky 3p mode.  In addition, 3p
    mode can use both a short deck (13 cards per hand) or a long deck (17).
    """
    def __init__(self, server, table_name):

        super(Hokm, self).__init__(server, table_name)

        self.game_display_name = "Hokm"
        self.game_name = "hokm"

        self.state = State("need_players")
        self.prefix = "(^RHokm^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Hokm-specific stuff.
        self.goal = 7
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.hakem = None
        self.winner = None

        # Default to four-player mode.
        self.mode = 4
        self.short = True
        self.setup_mode()

    def setup_mode(self):

        # Sets up all of the structures that depend on the mode of Hokm
        # we're playing: seats, layouts, and (in 4p mode) partnerships.
        # Remember that seats in Hokm go the opposite direction of the
        # American/European standard.

        if self.mode == 4:

            self.seats = [
                Seat("North"),
                Seat("West"),
                Seat("South"),
                Seat("East"),
            ]

            self.seats[0].data.who = NORTH
            self.seats[1].data.who = WEST
            self.seats[2].data.who = SOUTH
            self.seats[3].data.who = EAST

            self.min_players = 4
            self.max_players = 4
            self.layout = FourPlayerCardGameLayout()

            # Set up the partnership structures.
            self.ns = Struct()
            self.ew = Struct()
            self.ns.score = 0
            self.ew.score = 0

        elif self.mode == 3:

            self.seats = [
                Seat("West"),
                Seat("South"),
                Seat("East"),
            ]

            self.seats[0].data.who = WEST
            self.seats[1].data.who = SOUTH
            self.seats[2].data.who = EAST

            self.west = self.seats[0]
            self.south = self.seats[1]
            self.east = self.seats[2]
            self.west.data.score = 0
            self.south.data.score = 0
            self.east.data.score = 0

            self.min_players = 3
            self.max_players = 3
            self.layout = ThreePlayerCardGameLayout()

        else:
            self.log_pre(
                "MAJOR ERROR: Hokm initialization with invalid mode %s!" %
                self.mode)

    def show_help(self, player):

        super(Hokm, self).show_help(player)
        player.tell_cc("\nHOKM SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n"
        )
        player.tell_cc(
            "              ^!players^. 3|4, ^!pl^.     Set the number of players.\n"
        )
        player.tell_cc(
            "             ^!short^. on|off, ^!sh^.     Use a short deck (3p only).\n"
        )
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nHOKM PLAY:\n\n")
        player.tell_cc(
            "            ^!choose^. <suit>, ^!ch^.     Declare <suit> as trumps.  Hakem only.\n"
        )
        player.tell_cc(
            "              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n"
        )
        player.tell_cc(
            "                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n"
        )

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_color_code(self, seat):
        if self.mode == 4:
            if seat == self.seats[0] or seat == self.seats[2]:
                return "^R"
            else:
                return "^M"
        else:
            if seat == self.west:
                return "^M"
            elif seat == self.south:
                return "^R"
            else:
                return "^B"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name,
                                    self.get_color_code(seat), seat)

    def get_score_str(self):
        if self.mode == 4:
            return "          ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (
                self.ns.score, self.ew.score)
        else:
            return "          ^M%s^~: %d    ^R%s^~: %d    ^B%s^~: %d\n" % (
                self.west.player_name, self.west.data.score,
                self.south.player_name, self.south.data.score,
                self.east.player_name, self.east.data.score)

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            seat_color = self.get_color_code(self.turn)
            to_return += "%s is the hakem.\n" % (self.get_sp_str(self.hakem))
            if self.trump_suit:
                trump_str = "^C%s^~" % self.trump_suit
            else:
                trump_str = "^cwaiting to be chosen^~"

            to_return += "It is ^Y%s^~'s turn (%s%s^~).  Trumps are ^C%s^~.\n" % (
                self.turn.player_name, seat_color, self.turn, trump_str)
            if self.mode == 4:
                to_return += "Tricks:   ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (
                    self.ns.tricks, self.ew.tricks)
            else:
                to_return += "Tricks:   ^M%s^~: %d    ^R%s^~: %d    ^B%s^~: %d\n" % (
                    self.west.player_name, self.west.data.tricks,
                    self.south.player_name, self.south.data.tricks,
                    self.east.player_name, self.east.data.tricks)
        to_return += "The goal score for this game is ^C%s^~.\n" % get_plural_str(
            self.goal, "point")
        to_return += self.get_score_str()

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" %
                    (player, get_plural_str(new_goal, "point")))

    def set_short(self, player, short_bits):

        if self.mode != 3:
            self.tell_pre(
                player, "Cannot set short mode when not in 3-player mode.\n")
            return False

        short_bool = booleanize(short_bits)
        if short_bool:
            if short_bool > 0:
                self.short = True
                display_str = "^Con^~"
            elif short_bool < 0:
                self.short = False
                display_str = "^coff^~"
            self.bc_pre("^R%s^~ has turned short suits %s.\n" %
                        (player, display_str))
        else:
            self.tell_pre(player, "Not a valid boolean!\n")

    def set_players(self, player, player_str):

        if not player_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_mode = int(player_str)

        if new_mode == self.mode:
            self.tell_pre(player, "That is the current player count.\n")
            return False

        elif new_mode != 3 and new_mode != 4:
            self.tell_pre(player,
                          "Only 3-player and 4-player Hokm is supported.\n")
            return False

        # Got a valid mode.
        self.mode = new_mode
        self.bc_pre("^M%s^~ has changed the number of players to ^G%s^~.\n" %
                    (player, new_mode))
        self.setup_mode()

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deck(self):

        # In 4-player mode, it's a standard 52-card pack.
        if self.mode == 4:
            self.deck = new_deck()
        else:

            # If it's a short deck, 7-A are full; if a long deck, 3-A are.
            full_ranks = [ACE, KING, QUEEN, JACK, '10', '9', '8', '7', '6']
            if self.short:
                short_rank = '5'
            else:
                full_ranks.extend(['5', '4', '3'])
                short_rank = '2'

            # Build the deck, full ranks first.
            self.deck = Hand()
            for suit in (CLUBS, DIAMONDS, HEARTS, SPADES):
                for rank in full_ranks:
                    self.deck.add(PlayingCard(rank, suit))

            # We only want three of the short rank.  No hearts, because.
            for suit in (CLUBS, DIAMONDS, SPADES):
                self.deck.add(PlayingCard(short_rank, suit))

    def start_deal(self):

        # Set the trick counts to zero, appropriately for the mode.
        if self.mode == 4:
            self.ns.tricks = 0
            self.ew.tricks = 0
        else:
            self.west.data.tricks = 0
            self.south.data.tricks = 0
            self.east.data.tricks = 0

        dealer_name = self.dealer.player_name

        self.bc_pre(
            "^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" %
            (dealer_name, self.get_color_code(self.dealer), self.dealer))
        self.new_deck()
        self.deck.shuffle()

        # Deal out five cards each.
        self.bc_pre("^R%s^~ deals five cards out to each of the players.\n" %
                    dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for i in range(5):
            for seat in self.seats:
                seat.data.hand.add(self.deck.discard())

        # Clear the internal metadata about trumps.
        self.trump_suit = None

        # Sort the hakem's hand.
        self.hakem.data.hand = sorted_hand(self.hakem.data.hand)

        # Show the hakem their hand.
        if self.hakem.player:
            self.tell_pre(self.hakem.player,
                          "Please choose a trump suit for this hand.\n")
            self.show_hand(self.hakem.player)

        # The hakem both chooses and, eventually, leads.
        self.turn = self.hakem
        self.layout.change_turn(self.hakem.data.who)

        # Shift into "choosing" mode.
        self.state.set("choosing")

    def finish_deal(self):

        self.bc_pre("^R%s^~ finishes dealing the cards out.\n" %
                    self.dealer.player_name)
        while len(self.deck):
            for seat in self.seats:
                seat.data.hand.add(self.deck.discard())

        # Sort everyone's hands now that we have a trump suit.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)

        # Show everyone their completed hands.
        self.show_hands()

        # We're playing now.
        self.state.set("playing")

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if (this_suit != self.led_suit
                    and hand_has_suit(seat.data.hand, self.led_suit)):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player,
                              "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" %
                    (self.get_sp_str(seat), action_str,
                     card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        active_seats = [x for x in self.seats if x.player]
        if (self.state.get() == "need_players"
                and len(active_seats) == self.mode and self.active):
            self.state.set("playing")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Pick a hakem at random.
            self.hakem = random.choice(self.seats)
            self.bc_pre("Fate has spoken, and the starting hakem is %s!\n" %
                        self.get_sp_str(self.hakem))

            # The dealer is always the player before the hakem.
            self.dealer = self.prev_seat(self.hakem)
            self.start_deal()

    def choose(self, player, choose_str):

        choose_str = choose_str.lower()

        if choose_str in (
                "clubs",
                "c",
        ):
            self.trump_suit = CLUBS
        elif choose_str in (
                "diamonds",
                "d",
        ):
            self.trump_suit = DIAMONDS
        elif choose_str in (
                "hearts",
                "h",
        ):
            self.trump_suit = HEARTS
        elif choose_str in (
                "spades",
                "s",
        ):
            self.trump_suit = SPADES
        else:
            self.tell_pre(player, "That's not a valid suit!\n")
            return

        # Success.  Declare it and finish the deal.
        self.bc_pre("^Y%s^~ has picked ^R%s^~ as trumps.\n" %
                    (player, self.trump_suit))
        self.finish_deal()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in (
                        "goal",
                        "score",
                        "sc",
                        "g",
                ):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in (
                        "players",
                        "pl",
                ):
                    if len(command_bits) == 2:
                        self.set_players(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid players command.\n")
                    handled = True

                elif primary in (
                        "short",
                        "sh",
                ):
                    if len(command_bits) == 2:
                        self.set_short(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid short command.\n")
                    handled = True

                elif primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):
                    self.state.set("setup")
                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    handled = True

            elif state == "choosing":

                if primary in (
                        "hand",
                        "inventory",
                        "inv",
                        "i",
                ):
                    if player == self.hakem.player:
                        self.show_hand(player)
                    else:
                        self.tell_pre(player,
                                      "You can't look at your cards yet!\n")
                    handled = True

                elif primary in (
                        "choose",
                        "trump",
                        "ch",
                        "tr",
                ):
                    if player == self.hakem.player:
                        if len(command_bits) == 2:
                            self.choose(player, command_bits[1])
                        else:
                            self.tell_pre(player, "Invalid choose command.\n")
                    else:
                        self.tell_pre(player, "You're not hakem!\n")
                    handled = True

            elif state == "playing":

                card_played = False
                if primary in (
                        "hand",
                        "inventory",
                        "inv",
                        "i",
                ):
                    self.show_hand(player)
                    handled = True

                elif primary in (
                        "play",
                        "move",
                        "pl",
                        "mv",
                ):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == self.mode:

                        # Finish the trick up.
                        self.finish_trick()

                        # Did that end the hand?
                        winner = self.find_hand_winner()

                        if winner:

                            # Yup.  Resolve the hand...
                            self.resolve_hand(winner)

                            # And look for a winner.
                            winner = self.find_winner()
                            if winner:

                                # Found a winner.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # No winner.  Redeal.
                                self.start_deal()

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(
                self.log_prefix +
                "Something went horribly awry; trick ended without a finish.")
            self.bc_pre(
                "Something went horribly wrong; no one won the trick!  Tell the admin.\n"
            )
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" %
                    (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # If there are four players, give the trick to the correct partnership.
        if self.mode == 4:
            if winning_seat == self.seats[0] or winning_seat == self.seats[2]:
                self.ns.tricks += 1
            else:
                self.ew.tricks += 1
        else:
            winning_seat.data.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def find_hand_winner(self):

        # In four-player mode, this is actually really simple; winning only
        # occurs when one side has more than 6 tricks.
        if self.mode == 4:
            if self.ns.tricks > 6:
                return self.ns
            elif self.ew.tricks > 6:
                return self.ew
        else:

            # In three-player mode, this is considerably less simple.  If
            # one player has more tricks than either other player can possibly
            # get, they win...
            tricks_remaining = len(self.west.data.hand)
            for seat in self.seats:
                our_tricks = seat.data.tricks
                prev_tricks = self.prev_seat(seat).data.tricks
                next_tricks = self.next_seat(seat).data.tricks
                if ((our_tricks > prev_tricks + tricks_remaining)
                        and (our_tricks > next_tricks + tricks_remaining)):
                    return seat

                # ...orrr if there are no tricks left and the other two players
                # tied for the number of tricks, we win as well.  3p Hokm, you
                # so crazy.
                if (not tricks_remaining) and prev_tricks == next_tricks:
                    return seat

                # There's also the case where one player gets the first seven;
                # this is handled already for the short deck by the first check
                # above, but has to have a specific check for the long-deck
                # game.
                if our_tricks == 7 and not prev_tricks and not next_tricks:
                    return seat

        # No winner yet.
        return None

    def resolve_hand(self, winner):

        # Assume the hakem won and there was no sweep; we'll adjust later.
        hakem_won = True
        swept = False

        # 4p mode shenanigans first.
        if self.mode == 4:

            if winner == self.ns:
                winning_str = "^RNorth/South^~"
                if self.hakem != self.seats[0] and self.hakem != self.seats[2]:
                    hakem_won = False
                loser = self.ew
            else:
                winning_str = "^MEast/West^~"
                if self.hakem != self.seats[1] and self.hakem != self.seats[3]:
                    hakem_won = False
                loser = self.ns

            # Did the loser get no tricks?  If so, the winner swept!
            if loser.tricks == 0:
                swept = True

        else:

            # 3P mode.  Check whether the hakem really won...
            if winner != self.hakem:
                hakem_won = False

            # ...and whether the winner swept.
            prev_tricks = self.prev_seat(winner).data.tricks
            next_tricks = self.next_seat(winner).data.tricks
            if not prev_tricks and not next_tricks:
                swept = True

            winning_str = self.get_sp_str(winner)

        if swept:
            action_str = "^Yswept^~"

            # 2 points if the hakem won, 3 if others did.
            if hakem_won:
                addend = 2
            else:
                addend = 3
        else:

            # Standard win.  One point.
            action_str = "^Wwon^~"
            addend = 1

        # Let everyone know.
        self.bc_pre("%s %s the hand and gains ^C%s^~.\n" %
                    (winning_str, action_str, get_plural_str(addend, "point")))

        # Apply the score.
        if self.mode == 4:
            winner.score += addend
        else:
            winner.data.score += addend

        # Show everyone's scores.
        self.bc_pre(self.get_score_str())

        # Did the hakem not win?  If so, we need to have a new hakem and dealer.
        if not hakem_won:

            # In 4p mode, it just rotates...
            if self.mode == 4:
                self.dealer = self.hakem
                self.hakem = self.next_seat(self.hakem)
            else:

                # In 3p mode, the winner becomes hakem.
                self.hakem = winner
                self.dealer = self.prev_seat(self.hakem)

            self.bc_pre(
                "The ^Yhakem^~ has been unseated!  The new hakem is %s.\n" %
                self.get_sp_str(self.hakem))
        else:
            self.bc_pre("%s remains the hakem.\n" %
                        self.get_sp_str(self.hakem))

    def find_winner(self):

        if self.mode == 4:

            # Easy: has one of the sides reached a winning score?
            if self.ns.score >= self.goal:
                return self.ns
            elif self.ew.score >= self.goal:
                return self.ew

        else:

            # Have any of the players reached a winning score?
            for seat in self.seats:
                if seat.data.score >= self.goal:
                    return seat

        return None

    def resolve(self, winner):

        if self.mode == 4:
            if self.ns == winner:
                name_one = self.seats[0].player_name
                name_two = self.seats[2].player_name
            else:
                name_one = self.seats[1].player_name
                name_two = self.seats[3].player_name
            self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))
        else:
            self.bc_pre("^G%s^~ wins!\n" % winner.player_name)
コード例 #38
0
ファイル: hokm.py プロジェクト: Cloudxtreme/giles
class Hokm(SeatedGame):
    """A Hokm game table implementation.  Hokm is a Persian trick-taking
    card game of unknown provenance.  This implementation doesn't
    currently rearrange the seats at the start, but does support both the
    standard 4p partnership game and the quirky 3p mode.  In addition, 3p
    mode can use both a short deck (13 cards per hand) or a long deck (17).
    """

    def __init__(self, server, table_name):

        super(Hokm, self).__init__(server, table_name)

        self.game_display_name = "Hokm"
        self.game_name = "hokm"

        self.state = State("need_players")
        self.prefix = "(^RHokm^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Hokm-specific stuff.
        self.goal = 7
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.hakem = None
        self.winner = None

        # Default to four-player mode.
        self.mode = 4
        self.short = True
        self.setup_mode()

    def setup_mode(self):

        # Sets up all of the structures that depend on the mode of Hokm
        # we're playing: seats, layouts, and (in 4p mode) partnerships.
        # Remember that seats in Hokm go the opposite direction of the
        # American/European standard.

        if self.mode == 4:

            self.seats = [
                Seat("North"),
                Seat("West"),
                Seat("South"),
                Seat("East"),
            ]

            self.seats[0].data.who = NORTH
            self.seats[1].data.who = WEST
            self.seats[2].data.who = SOUTH
            self.seats[3].data.who = EAST

            self.min_players = 4
            self.max_players = 4
            self.layout = FourPlayerCardGameLayout()

            # Set up the partnership structures.
            self.ns = Struct()
            self.ew = Struct()
            self.ns.score = 0
            self.ew.score = 0

        elif self.mode == 3:

            self.seats = [
                Seat("West"),
                Seat("South"),
                Seat("East"),
            ]

            self.seats[0].data.who = WEST
            self.seats[1].data.who = SOUTH
            self.seats[2].data.who = EAST

            self.west = self.seats[0]
            self.south = self.seats[1]
            self.east = self.seats[2]
            self.west.data.score = 0
            self.south.data.score = 0
            self.east.data.score = 0

            self.min_players = 3
            self.max_players = 3
            self.layout = ThreePlayerCardGameLayout()

        else:
            self.log_pre("MAJOR ERROR: Hokm initialization with invalid mode %s!" % self.mode)

    def show_help(self, player):

        super(Hokm, self).show_help(player)
        player.tell_cc("\nHOKM SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n")
        player.tell_cc("              ^!players^. 3|4, ^!pl^.     Set the number of players.\n")
        player.tell_cc("             ^!short^. on|off, ^!sh^.     Use a short deck (3p only).\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nHOKM PLAY:\n\n")
        player.tell_cc("            ^!choose^. <suit>, ^!ch^.     Declare <suit> as trumps.  Hakem only.\n")
        player.tell_cc("              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n")
        player.tell_cc("                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n")

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_color_code(self, seat):
        if self.mode == 4:
            if seat == self.seats[0] or seat == self.seats[2]:
                return "^R"
            else:
                return "^M"
        else:
            if seat == self.west:
                return "^M"
            elif seat == self.south:
                return "^R"
            else:
                return "^B"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name, self.get_color_code(seat), seat)

    def get_score_str(self):
        if self.mode == 4:
            return "          ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (self.ns.score, self.ew.score)
        else:
            return "          ^M%s^~: %d    ^R%s^~: %d    ^B%s^~: %d\n" % (self.west.player_name, self.west.data.score, self.south.player_name, self.south.data.score, self.east.player_name, self.east.data.score)

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            seat_color = self.get_color_code(self.turn)
            to_return += "%s is the hakem.\n" % (self.get_sp_str(self.hakem))
            if self.trump_suit:
                trump_str = "^C%s^~" % self.trump_suit
            else:
                trump_str = "^cwaiting to be chosen^~"

            to_return += "It is ^Y%s^~'s turn (%s%s^~).  Trumps are ^C%s^~.\n" % (self.turn.player_name, seat_color, self.turn, trump_str)
            if self.mode == 4:
                to_return += "Tricks:   ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (self.ns.tricks, self.ew.tricks)
            else:
                to_return += "Tricks:   ^M%s^~: %d    ^R%s^~: %d    ^B%s^~: %d\n" % (self.west.player_name, self.west.data.tricks, self.south.player_name, self.south.data.tricks, self.east.player_name, self.east.data.tricks)
        to_return += "The goal score for this game is ^C%s^~.\n" % get_plural_str(self.goal, "point")
        to_return += self.get_score_str()

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" % (player, get_plural_str(new_goal, "point")))

    def set_short(self, player, short_bits):

        if self.mode != 3:
            self.tell_pre(player, "Cannot set short mode when not in 3-player mode.\n")
            return False

        short_bool = booleanize(short_bits)
        if short_bool:
            if short_bool > 0:
                self.short = True
                display_str = "^Con^~"
            elif short_bool < 0:
                self.short = False
                display_str = "^coff^~"
            self.bc_pre("^R%s^~ has turned short suits %s.\n" % (player, display_str))
        else:
            self.tell_pre(player, "Not a valid boolean!\n")

    def set_players(self, player, player_str):

        if not player_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_mode = int(player_str)

        if new_mode == self.mode:
            self.tell_pre(player, "That is the current player count.\n")
            return False

        elif new_mode != 3 and new_mode != 4:
            self.tell_pre(player, "Only 3-player and 4-player Hokm is supported.\n")
            return False

        # Got a valid mode.
        self.mode = new_mode
        self.bc_pre("^M%s^~ has changed the number of players to ^G%s^~.\n" % (player, new_mode))
        self.setup_mode()

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deck(self):

        # In 4-player mode, it's a standard 52-card pack.
        if self.mode == 4:
            self.deck = new_deck()
        else:

            # If it's a short deck, 7-A are full; if a long deck, 3-A are.
            full_ranks = [ACE, KING, QUEEN, JACK, '10', '9', '8', '7', '6']
            if self.short:
                short_rank = '5'
            else:
                full_ranks.extend(['5', '4', '3'])
                short_rank = '2'

            # Build the deck, full ranks first.
            self.deck = Hand()
            for suit in (CLUBS, DIAMONDS, HEARTS, SPADES):
                for rank in full_ranks:
                    self.deck.add(PlayingCard(rank, suit))

            # We only want three of the short rank.  No hearts, because.
            for suit in (CLUBS, DIAMONDS, SPADES):
                self.deck.add(PlayingCard(short_rank, suit))

    def start_deal(self):

        # Set the trick counts to zero, appropriately for the mode.
        if self.mode == 4:
            self.ns.tricks = 0
            self.ew.tricks = 0
        else:
            self.west.data.tricks = 0
            self.south.data.tricks = 0
            self.east.data.tricks = 0

        dealer_name = self.dealer.player_name

        self.bc_pre("^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" % (dealer_name, self.get_color_code(self.dealer), self.dealer))
        self.new_deck()
        self.deck.shuffle()

        # Deal out five cards each.
        self.bc_pre("^R%s^~ deals five cards out to each of the players.\n" % dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for i in range(5):
            for seat in self.seats:
                seat.data.hand.add(self.deck.discard())

        # Clear the internal metadata about trumps.
        self.trump_suit = None

        # Sort the hakem's hand.
        self.hakem.data.hand = sorted_hand(self.hakem.data.hand)

        # Show the hakem their hand.
        if self.hakem.player:
            self.tell_pre(self.hakem.player, "Please choose a trump suit for this hand.\n")
            self.show_hand(self.hakem.player)

        # The hakem both chooses and, eventually, leads.
        self.turn = self.hakem
        self.layout.change_turn(self.hakem.data.who)

        # Shift into "choosing" mode.
        self.state.set("choosing")

    def finish_deal(self):

        self.bc_pre("^R%s^~ finishes dealing the cards out.\n" % self.dealer.player_name)
        while len(self.deck):
            for seat in self.seats:
                seat.data.hand.add(self.deck.discard())

        # Sort everyone's hands now that we have a trump suit.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)

        # Show everyone their completed hands.
        self.show_hands()

        # We're playing now.
        self.state.set("playing")

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if (this_suit != self.led_suit and
               hand_has_suit(seat.data.hand, self.led_suit)):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player, "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" % (self.get_sp_str(seat), action_str, card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        active_seats = [x for x in self.seats if x.player]
        if (self.state.get() == "need_players" and
           len(active_seats) == self.mode and self.active):
            self.state.set("playing")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Pick a hakem at random.
            self.hakem = random.choice(self.seats)
            self.bc_pre("Fate has spoken, and the starting hakem is %s!\n" % self.get_sp_str(self.hakem))

            # The dealer is always the player before the hakem.
            self.dealer = self.prev_seat(self.hakem)
            self.start_deal()

    def choose(self, player, choose_str):

        choose_str = choose_str.lower()

        if choose_str in ("clubs", "c",):
            self.trump_suit = CLUBS
        elif choose_str in ("diamonds", "d",):
            self.trump_suit = DIAMONDS
        elif choose_str in ("hearts", "h",):
            self.trump_suit = HEARTS
        elif choose_str in ("spades", "s",):
            self.trump_suit = SPADES
        else:
            self.tell_pre(player, "That's not a valid suit!\n")
            return

        # Success.  Declare it and finish the deal.
        self.bc_pre("^Y%s^~ has picked ^R%s^~ as trumps.\n" % (player, self.trump_suit))
        self.finish_deal()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in ("goal", "score", "sc", "g",):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in ("players", "pl",):
                    if len(command_bits) == 2:
                        self.set_players(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid players command.\n")
                    handled = True

                elif primary in ("short", "sh",):
                    if len(command_bits) == 2:
                        self.set_short(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid short command.\n")
                    handled = True

                elif primary in ("done", "ready", "d", "r",):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):
                    self.state.set("setup")
                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "choosing":

                if primary in ("hand", "inventory", "inv", "i",):
                    if player == self.hakem.player:
                        self.show_hand(player)
                    else:
                        self.tell_pre(player, "You can't look at your cards yet!\n")
                    handled = True

                elif primary in ("choose", "trump", "ch", "tr",):
                    if player == self.hakem.player:
                        if len(command_bits) == 2:
                            self.choose(player, command_bits[1])
                        else:
                            self.tell_pre(player, "Invalid choose command.\n")
                    else:
                        self.tell_pre(player, "You're not hakem!\n")
                    handled = True

            elif state == "playing":

                card_played = False
                if primary in ("hand", "inventory", "inv", "i",):
                    self.show_hand(player)
                    handled = True

                elif primary in ("play", "move", "pl", "mv",):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == self.mode:

                        # Finish the trick up.
                        self.finish_trick()

                        # Did that end the hand?
                        winner = self.find_hand_winner()

                        if winner:

                            # Yup.  Resolve the hand...
                            self.resolve_hand(winner)

                            # And look for a winner.
                            winner = self.find_winner()
                            if winner:

                                # Found a winner.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # No winner.  Redeal.
                                self.start_deal()

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(self.log_prefix + "Something went horribly awry; trick ended without a finish.")
            self.bc_pre("Something went horribly wrong; no one won the trick!  Tell the admin.\n")
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" % (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # If there are four players, give the trick to the correct partnership.
        if self.mode == 4:
            if winning_seat == self.seats[0] or winning_seat == self.seats[2]:
                self.ns.tricks += 1
            else:
                self.ew.tricks += 1
        else:
            winning_seat.data.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def find_hand_winner(self):

        # In four-player mode, this is actually really simple; winning only
        # occurs when one side has more than 6 tricks.
        if self.mode == 4:
            if self.ns.tricks > 6:
                return self.ns
            elif self.ew.tricks > 6:
                return self.ew
        else:

            # In three-player mode, this is considerably less simple.  If
            # one player has more tricks than either other player can possibly
            # get, they win...
            tricks_remaining = len(self.west.data.hand)
            for seat in self.seats:
                our_tricks = seat.data.tricks
                prev_tricks = self.prev_seat(seat).data.tricks
                next_tricks = self.next_seat(seat).data.tricks
                if ((our_tricks > prev_tricks + tricks_remaining) and
                   (our_tricks > next_tricks + tricks_remaining)):
                    return seat

                # ...orrr if there are no tricks left and the other two players
                # tied for the number of tricks, we win as well.  3p Hokm, you
                # so crazy.
                if (not tricks_remaining) and prev_tricks == next_tricks:
                    return seat

                # There's also the case where one player gets the first seven;
                # this is handled already for the short deck by the first check
                # above, but has to have a specific check for the long-deck
                # game.
                if our_tricks == 7 and not prev_tricks and not next_tricks:
                    return seat

        # No winner yet.
        return None

    def resolve_hand(self, winner):

        # Assume the hakem won and there was no sweep; we'll adjust later.
        hakem_won = True
        swept = False

        # 4p mode shenanigans first.
        if self.mode == 4:

            if winner == self.ns:
                winning_str = "^RNorth/South^~"
                if self.hakem != self.seats[0] and self.hakem != self.seats[2]:
                    hakem_won = False
                loser = self.ew
            else:
                winning_str = "^MEast/West^~"
                if self.hakem != self.seats[1] and self.hakem != self.seats[3]:
                    hakem_won = False
                loser = self.ns

            # Did the loser get no tricks?  If so, the winner swept!
            if loser.tricks == 0:
                swept = True

        else:

            # 3P mode.  Check whether the hakem really won...
            if winner != self.hakem:
                hakem_won = False

            # ...and whether the winner swept.
            prev_tricks = self.prev_seat(winner).data.tricks
            next_tricks = self.next_seat(winner).data.tricks
            if not prev_tricks and not next_tricks:
                swept = True

            winning_str = self.get_sp_str(winner)

        if swept:
            action_str = "^Yswept^~"

            # 2 points if the hakem won, 3 if others did.
            if hakem_won:
                addend = 2
            else:
                addend = 3
        else:

            # Standard win.  One point.
            action_str = "^Wwon^~"
            addend = 1

        # Let everyone know.
        self.bc_pre("%s %s the hand and gains ^C%s^~.\n" % (winning_str, action_str, get_plural_str(addend, "point")))

        # Apply the score.
        if self.mode == 4:
            winner.score += addend
        else:
            winner.data.score += addend

        # Show everyone's scores.
        self.bc_pre(self.get_score_str())

        # Did the hakem not win?  If so, we need to have a new hakem and dealer.
        if not hakem_won:

            # In 4p mode, it just rotates...
            if self.mode == 4:
                self.dealer = self.hakem
                self.hakem = self.next_seat(self.hakem)
            else:

                # In 3p mode, the winner becomes hakem.
                self.hakem = winner
                self.dealer = self.prev_seat(self.hakem)

            self.bc_pre("The ^Yhakem^~ has been unseated!  The new hakem is %s.\n" % self.get_sp_str(self.hakem))
        else:
            self.bc_pre("%s remains the hakem.\n" % self.get_sp_str(self.hakem))

    def find_winner(self):

        if self.mode == 4:

            # Easy: has one of the sides reached a winning score?
            if self.ns.score >= self.goal:
                return self.ns
            elif self.ew.score >= self.goal:
                return self.ew

        else:

            # Have any of the players reached a winning score?
            for seat in self.seats:
                if seat.data.score >= self.goal:
                    return seat

        return None

    def resolve(self, winner):

        if self.mode == 4:
            if self.ns == winner:
                name_one = self.seats[0].player_name
                name_two = self.seats[2].player_name
            else:
                name_one = self.seats[1].player_name
                name_two = self.seats[3].player_name
            self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))
        else:
            self.bc_pre("^G%s^~ wins!\n" % winner.player_name)
コード例 #39
0
ファイル: expeditions.py プロジェクト: Cloudxtreme/giles
class Expeditions(SeatedGame):
    """A Expeditions game table implementation.  Based on a game invented in
    1999 by Reiner Knizia.
    """

    def __init__(self, server, table_name):

        super(Expeditions, self).__init__(server, table_name)

        self.game_display_name = "Expeditions"
        self.game_name = "expeditions"
        self.seats = [
            Seat("Left"),
            Seat("Right")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RExpeditions^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Expeditions-specific stuff.
        self.suit_count = 5
        self.agreement_count = 3
        self.penalty = 20
        self.bonus = True
        self.bonus_length = 8
        self.bonus_points = 20
        self.hand_size = 8
        self.goal = 1

        self.turn = None
        self.draw_pile = None
        self.discards = []
        self.left = self.seats[0]
        self.right = self.seats[1]
        self.left.data.side = LEFT
        self.left.data.curr_score = 0
        self.left.data.overall_score = 0
        self.left.data.hand = None
        self.left.data.expeditions = []
        self.right.data.side = RIGHT
        self.right.data.curr_score = 0
        self.right.data.overall_score = 0
        self.right.data.hand = None
        self.right.data.expeditions = []
        self.resigner = None
        self.first_player = None
        self.just_discarded_to = None

        self.printable_layout = None
        self.init_hand()

    def init_hand(self):

        # Depending on the number of suits requested for play, we build
        # structures.
        suit_list = DEFAULT_SUITS[:]
        if self.suit_count >= 6:
            suit_list.append(CYAN)
        if self.suit_count == 7:
            suit_list.append(MAGENTA)

        # If, on the other hand, the number of suits is /less/ than five,
        # we use a subset of the default suits.
        if self.suit_count < 5:
            suit_list = DEFAULT_SUITS[:self.suit_count]

        # All right, we have a list of suits involved in this game.  Let's
        # build the various piles that are based on those suits.
        self.left.data.expeditions = []
        self.right.data.expeditions = []
        self.discards = []
        for suit in suit_list:
            discard_pile = Struct()
            left_expedition = Struct()
            right_expedition = Struct()
            for pile in (discard_pile, left_expedition, right_expedition):
                pile.suit = suit
                pile.hand = Hand()
                pile.value = 0
            self.left.data.expeditions.append(left_expedition)
            self.right.data.expeditions.append(right_expedition)
            self.discards.append(discard_pile)

        # We'll do a separate loop for generating the deck to minimize
        # confusion.
        self.draw_pile = Hand()
        for suit in suit_list:
            for rank in NUMERICAL_RANKS:
                self.draw_pile.add(ExpeditionsCard(rank, suit))

            # Add as many agreements as requested.
            for agreement in range(self.agreement_count):
                self.draw_pile.add(ExpeditionsCard(AGREEMENT, suit))

        # Lastly, shuffle the draw deck and initialize hands.
        self.draw_pile.shuffle()
        self.left.data.hand = Hand()
        self.right.data.hand = Hand()

    def get_discard_str(self, pos):

        discard_pile = self.discards[pos]
        if len(discard_pile.hand):
            return(value_to_str(discard_pile.hand[-1].value()))
        return "."

    def get_expedition_str(self, expedition):

        to_return = ""
        for card in expedition:
            to_return += value_to_str(card.value())

        return to_return

    def get_sp_str(self, seat):

        if seat == self.left:
            return "^C%s^~" % self.left.player_name
        else:
            return "^M%s^~" % self.right.player_name

    def update_printable_layout(self):

        self.printable_layout = []
        self.printable_layout.append("                   .---.\n")

        # Loop through all table rows.
        for row in range(self.suit_count):
            left = self.left.data.expeditions[row]
            right = self.right.data.expeditions[row]
            suit_char = left.suit[0].upper()
            left_suit_char = suit_char
            right_suit_char = suit_char
            expedition_str = get_color_code(left.suit)
            expedition_str += self.get_expedition_str(left.hand.reversed()).rjust(18)
            if self.bonus and len(left.hand) >= self.bonus_length:
                left_suit_char = "*"
            if self.bonus and len(right.hand) >= self.bonus_length:
                right_suit_char = "*"
            expedition_str += " %s %s %s " % (left_suit_char, self.get_discard_str(row), right_suit_char)
            expedition_str += self.get_expedition_str(right.hand)
            expedition_str += "^~\n"
            self.printable_layout.append(expedition_str)
            self.printable_layout.append("                   |   |\n")

        # Replace the last unnecessary separator row with the end of the board.
        self.printable_layout[-1] = "                   `---'\n"

    def get_metadata_str(self):

        to_return = "^Y%s^~ remain in the draw pile.\n" % get_plural_str(len(self.draw_pile), "card")
        if not self.turn:
            to_return += "The game has not started yet.\n"
        else:
            to_return += "It is %s's turn to " % self.get_sp_str(self.turn)
            sub = self.state.get_sub()
            if sub == "play":
                to_return += "^cplay a card^~.\n"
            else:
                to_return += "^cdraw a card^~.\n"
        to_return += "The goal score for this game is ^Y%s^~.\n" % get_plural_str(self.goal, "point")
        to_return += "Overall:      %s: %s     %s: %s\n" % (self.get_sp_str(self.left), self.left.data.overall_score, self.get_sp_str(self.right), self.right.data.overall_score)

        return to_return

    def show(self, player, show_metadata=True):

        if not self.printable_layout:
            self.update_printable_layout()
        player.tell_cc("%s         %s\n" % (self.get_sp_str(self.left).rjust(21), self.get_sp_str(self.right)))
        for line in self.printable_layout:
            player.tell_cc(line)
        if show_metadata:
            player.tell_cc("\n" + self.get_metadata_str())

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def send_layout(self, show_metadata=True):

        for player in self.channel.listeners:
            self.show(player, show_metadata)
        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def deal(self):

        # Deal cards until each player has hand_size cards.
        self.bc_pre("A fresh hand is dealt to both players.\n")
        for i in range(self.hand_size):
            self.left.data.hand.add(self.draw_pile.discard())
            self.right.data.hand.add(self.draw_pile.discard())

        # Sort hands.
        self.left.data.hand = sorted_hand(self.left.data.hand)
        self.right.data.hand = sorted_hand(self.right.data.hand)

        # Clear scores.
        self.left.data.curr_score = 0
        self.right.data.curr_score = 0

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
           and self.seats[1].player and self.active):
            self.state.set("playing")
            self.state.set_sub("play")
            self.bc_pre("^CLeft^~: ^Y%s^~; ^MRight^~: ^Y%s^~\n" %
               (self.left.player_name, self.right.player_name))
            self.turn = self.left
            self.first_player = self.left
            self.deal()
            self.send_layout()

    def calculate_deck_size(self, suits, agrees):

        # Eventually this will depend on just what cards are in the deck,
        # but for now there are 9 point cards per suit plus the agreements.
        return (9 + agrees) * suits

    def set_suits(self, player, suit_str):

        if not suit_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_suit_count = int(suit_str)
        if new_suit_count < MIN_SUITS or new_suit_count > MAX_SUITS:
            self.tell_pre(player, "The number of suits must be between %d and %d inclusive.\n" % (MIN_SUITS, MAX_SUITS))
            return False

        # Does this give too few cards for the hand size?
        if self.calculate_deck_size(new_suit_count, self.agreement_count) <= self.hand_size * 2:
            self.tell_pre(player, "That number of suits is too small for the hand size.\n")
            return False

        # Valid.
        self.suit_count = new_suit_count
        self.bc_pre("^M%s^~ has changed the suit count to ^G%s^~.\n" % (player, new_suit_count))
        self.init_hand()
        self.update_printable_layout()

    def set_agreements(self, player, agree_str):

        if not agree_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_agree_count = int(agree_str)
        if new_agree_count < MIN_AGREEMENTS or new_agree_count > MAX_AGREEMENTS:
            self.tell_pre(player, "The number of agreements must be between %d and %d inclusive.\n" % (MIN_AGREEMENTS, MAX_AGREEMENTS))
            return False

        # Does this give too few cards for the hand size?
        if self.calculate_deck_size(self.suit_count, new_agree_count) <= self.hand_size * 2:
            self.tell_pre(player, "That number of agreements is too small for the hand size.\n")
            return False

        # Valid.
        self.agreement_count = new_agree_count
        self.bc_pre("^M%s^~ has changed the agreement count to ^G%s^~.\n" % (player, new_agree_count))
        self.init_hand()
        self.update_printable_layout()

    def set_hand(self, player, hand_str):

        if not hand_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_hand_size = int(hand_str)
        if new_hand_size < MIN_HAND_SIZE or new_hand_size > MAX_HAND_SIZE:
            self.tell_pre(player, "The hand size must be between %d and %d inclusive.\n" % (MIN_HAND_SIZE, MAX_HAND_SIZE))
            return False

        # If the drawn hands are greater than or equal to the actual card
        # count, that doesn't work either.
        if (new_hand_size * 2) >= len(self.draw_pile):
            self.tell_pre(player, "The hand size is too large for the number of cards in play.\n")
            return False

        # Valid.
        self.hand_size = new_hand_size
        self.bc_pre("^M%s^~ has changed the hand size to ^G%s^~.\n" % (player, new_hand_size))

    def set_penalty(self, player, penalty_str):

        if not penalty_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_penalty = int(penalty_str)
        if new_penalty < MIN_PENALTY or new_penalty > MAX_PENALTY:
            self.tell_pre(player, "The penalty must be between %d and %d inclusive.\n" % (MIN_PENALTY, MAX_PENALTY))
            return False

        # Valid.
        self.penalty = new_penalty
        self.bc_pre("^M%s^~ has changed the penalty to ^G%s^~.\n" % (player, new_penalty))

    def set_bonus(self, player, bonus_bits):

        if len(bonus_bits) == 1:

            bonus = bonus_bits[0]
            # Gotta be 'none' or 0.
            if bonus in ("none", "n", "0",):
                self.bonus = False
                self.bc_pre("^M%s^~ has disabled the expedition bonuses.\n" % player)
                return True
            else:
                self.tell_pre(player, "Invalid bonus command.\n")
                return False

        elif len(bonus_bits) == 2:

            points, length = bonus_bits

            if not points.isdigit() or not length.isdigit():
                self.tell_pre(player, "Invalid bonus command.\n")
                return False

            points = int(points)
            length = int(length)

            if not points or not length:
                self.bonus = False
                self.bc_pre("^M%s^~ has disabled the expedition bonuses.\n" % player)
                return True
            else:
                self.bonus = True
                self.bonus_points = points
                self.bonus_length = length
                self.bc_pre("^M%s^~ has set the expedition bonuses to ^C%s^~ at length ^R%s^~.\n" % (player, get_plural_str(points, "point"), length))
                return True

        else:
            self.tell_pre(player, "Invalid bonus command.\n")
            return False

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" % (player, get_plural_str(new_goal, "point")))

    def suit_to_loc(self, suit):

        if suit in DEFAULT_SUITS:
            return DEFAULT_SUITS.index(suit)
        elif suit == CYAN:
            return 5
        elif suit == MAGENTA:
            return 6

        return None

    def evaluate(self, player):

        for seat in self.seats:
            score_str = "%s: " % seat.player_name
            score_str += " + ".join(["%s%s^~" % (get_color_code(x.suit), x.value) for x in seat.data.expeditions])
            score_str += " = %s\n" % (get_plural_str(seat.data.curr_score, "point"))
            self.tell_pre(player, score_str)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        substate = self.state.get_sub()
        if substate != "play":
            self.tell_pre(player, "You should be drawing or retrieving, not playing!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # All right.  Grab the hand for that expedition.
        exp_hand = seat.data.expeditions[self.suit_to_loc(potential_card.suit)].hand

        # If this card is a lower value than the top card of the hand, nope.
        if len(exp_hand) and potential_card < exp_hand[-1]:
            self.tell_pre(player, "You can no longer play this card on this expedition.\n")
            return False

        # If it's the same value and not an agreement, nope.
        elif (len(exp_hand) and potential_card == exp_hand[-1] and
           potential_card.rank != AGREEMENT):
            self.tell_pre(player, "You cannot play same-valued point cards on an expedition.\n")
            return False

        # Passed the tests.  Play it and clear the discard tracker.
        exp_hand.add(seat.data.hand.discard_specific(potential_card))
        self.just_discarded_to = None

        self.bc_pre("%s played %s.\n" % (self.get_sp_str(seat),
                       card_to_str(potential_card, mode=LONG)))
        return True

    def discard(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        substate = self.state.get_sub()
        if substate != "play":
            self.tell_pre(player, "You should be drawing or retrieving, not discarding!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # All right, they can discard it.  Get the appropriate discard pile...
        discard_pile = self.discards[self.suit_to_loc(potential_card.suit)].hand

        discard_pile.add(seat.data.hand.discard_specific(potential_card))

        # Note the pile we just discarded to, so the player can't just pick it
        # back up as their next play.
        self.just_discarded_to = potential_card.suit

        self.bc_pre("%s discarded %s.\n" % (self.get_sp_str(seat),
                          card_to_str(potential_card, mode=LONG)))
        return True

    def draw(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        substate = self.state.get_sub()
        if substate != "draw":
            self.tell_pre(player, "You should be playing or discarding, not drawing!\n")
            return False

        # Draw a card.  This one's easy!
        draw_card = self.draw_pile.discard()
        seat.data.hand.add(draw_card)

        # Resort the hand.
        seat.data.hand = sorted_hand(seat.data.hand)

        self.bc_pre("%s drew a card.\n" % (self.get_sp_str(seat)))
        self.tell_pre(player, "You drew %s.\n" % card_to_str(draw_card, mode=LONG))
        return True

    def retrieve(self, player, retrieve_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        substate = self.state.get_sub()
        if substate != "draw":
            self.tell_pre(player, "You should be playing or discarding, not retrieving!\n")
            return False

        # Turn the retrieve string into an actual suit.
        suit = str_to_suit(retrieve_str)
        if not suit:
            self.tell_pre(player, "That's not a valid suit!\n")
            return False

        # Is that a valid location in this game?
        loc = self.suit_to_loc(suit)
        if loc >= self.suit_count:
            self.tell_pre(player, "That suit isn't in play this game.\n")
            return False

        # Is there actually a card there /to/ draw?
        discard_pile = self.discards[loc].hand
        if not len(discard_pile):
            self.tell_pre(player, "There are no discards of that suit.\n")
            return False

        # Is it the card they just discarded?
        if suit == self.just_discarded_to:
            self.tell_pre(player, "You just discarded that card!\n")
            return False

        # Phew.  All tests passed.  Give them the card.
        dis_card = discard_pile.discard()
        seat.data.hand.add(dis_card)
        seat.data.hand = sorted_hand(seat.data.hand)

        self.bc_pre("%s retrieved %s from the discards.\n" % (self.get_sp_str(seat),
                                                  card_to_str(dis_card, mode=LONG)))
        return True

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("%s is resigning from the game.\n" % self.get_sp_str(seat))
        return True

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.lower().split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("suits",):

                    if len(command_bits) == 2:
                        self.set_suits(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid suits command.\n")
                    handled = True

                elif primary in ("agreements", "agree",):

                    if len(command_bits) == 2:
                        self.set_agreements(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid agree command.\n")
                    handled = True

                elif primary in ("hand",):

                    if len(command_bits) == 2:
                        self.set_hand(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid hand command.\n")
                    handled = True

                elif primary in ("penalty",):

                    if len(command_bits) == 2:
                        self.set_penalty(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid penalty command.\n")
                    handled = True

                elif primary in ("bonus",):

                    if len(command_bits) >= 2:
                        self.set_bonus(player, command_bits[1:])
                    else:
                        self.tell_pre(player, "Invalid bonus command.\n")
                    handled = True

                elif primary in ("goal", "score",):

                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in ("done", "ready", "d", "r",):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):

                    self.state.set("setup")
                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("hand", "inventory", "inv", "i",):
                    self.show_hand(player)
                    handled = True

                elif primary in ("evaluate", "eval", "score", "e", "s",):
                    self.evaluate(player)
                    handled = True

                elif primary in ("move", "play", "mv", "pl",):

                    if len(command_bits) == 2:
                        made_move = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                elif primary in ("discard", "toss", "di", "to",):
                    if len(command_bits) == 2:
                        made_move = self.discard(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid discard command.\n")
                    handled = True

                elif primary in ("draw", "dr",):
                    made_move = self.draw(player)
                    handled = True

                elif primary in ("retrieve", "re",):
                    if len(command_bits) == 2:
                        made_move = self.retrieve(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid retrieve command.\n")
                    handled = True

                elif primary in ("resign",):

                    if self.resign(player):
                        made_move = True
                    handled = True

                if made_move:

                    substate = self.state.get_sub()

                    # Okay, something happened on the layout.  Update scores
                    # and the layout.
                    self.update_scores()
                    self.update_printable_layout()

                    # Is the game over?
                    if not len(self.draw_pile) or self.resigner:

                        # Yup.  Resolve the game.
                        self.resolve_hand()

                        # Is there an overall winner?
                        winner = self.find_winner()

                        if winner:
                            self.resolve(winner)
                            self.finish()

                        else:

                            # Hand over, but not the game itself.  New deal.
                            self.bc_pre("The cards are collected for another hand.\n")
                            self.init_hand()

                            # Switch dealers.
                            self.first_player = self.next_seat(self.first_player)
                            self.turn = self.first_player
                            self.state.set("playing")
                            self.state.set_sub("play")
                            self.deal()
                            self.update_printable_layout()
                            self.send_layout()

                    else:

                        # If we're in the play substate, switch to the draw.
                        if substate == "play":
                            self.state.set_sub("draw")

                        else:

                            # After draw, switch turns and resend the board.
                            self.state.set_sub("play")
                            self.turn = self.next_seat(self.turn)
                            self.send_layout(show_metadata=False)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def update_scores(self):

        for seat in self.left, self.right:

            # Start at 0.
            total = 0

            # For each expedition, if they're even on it...
            for exp in seat.data.expeditions:

                curr = 0
                multiplier = 1

                if len(exp.hand):

                    # Immediately assign the penalty.
                    curr -= self.penalty

                    # Now loop through the cards.
                    for card in exp.hand:

                        value = card.value()
                        if value == 1:

                            # Agreement; adjust multiplier.
                            multiplier += 1

                        else:

                            # Scoring card; increase current score.
                            curr += value

                    # Adjust the current score by the multiplier.
                    curr *= multiplier

                    # If bonuses are active, and this meets it, add it.
                    if self.bonus and len(exp.hand) >= self.bonus_length:
                        curr += self.bonus_points

                # No matter what, add curr to total and set it on the
                # pile.
                total += curr
                exp.value = curr

            # Set the current score for the seat.
            seat.data.curr_score = total

    def resolve_hand(self):

        for seat in self.left, self.right:

            addend = seat.data.curr_score
            if addend > 0:
                adj_str = "^Ygains ^C%s^~" % get_plural_str(addend, "point")
            elif addend < 0:
                adj_str = "^yloses ^c%s^~" % get_plural_str(-addend, "point")
            else:
                adj_str = "^Wsomehow manages to score precisely zero points^~"

            # Actually adjust the scores by the proper amounts, and inform
            # everyone of the result.
            seat.data.overall_score += addend

            # If someone resigned, scores don't matter, so don't show them.
            if not self.resigner:
                self.bc_pre("%s %s, giving them ^G%s^~.\n" % (self.get_sp_str(seat), adj_str, seat.data.overall_score))

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.
        if self.resigner == self.left:
            return self.right
        elif self.resigner == self.right:
            return self.left

        # If one player has a higher score than the other and that score
        # is higher than the goal, they win.
        if (self.left.data.overall_score > self.right.data.overall_score and
           self.left.data.overall_score >= self.goal):
            return self.left
        elif (self.right.data.overall_score > self.left.data.overall_score and
           self.right.data.overall_score >= self.goal):
            return self.right

        # Either we haven't reached the goal or there's a tie.  We'll print a
        # special message if there's a tie, because that's kinda crazy.
        if self.left.data.overall_score == self.right.data.overall_score:
            self.bc_pre("The players are tied!\n")

        # No matter what, there's no winner.
        return None

    def resolve(self, winner):
        self.bc_pre("%s wins!\n" % self.get_sp_str(winner))

    def show_help(self, player):

        super(Expeditions, self).show_help(player)
        player.tell_cc("\nEXPEDITIONS SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("                  ^!suits^. <num>     Play with <num> suits.\n")
        player.tell_cc("                  ^!agree^. <num>     Suits have <num> agreements.\n")
        player.tell_cc("                   ^!hand^. <num>     Hands have <num> cards.\n")
        player.tell_cc("                ^!penalty^. <num>     Expeditions start down <num> points.\n")
        player.tell_cc("     ^!bonus^. <pts> <len> | none     Bonus is <pts> at length <len>/none.\n")
        player.tell_cc("            ^!goal^. <num>, ^!score^.     Play until <num> points.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nEXPEDITIONS PLAY:\n\n")
        player.tell_cc("              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n")
        player.tell_cc("         ^!discard^. <card>, ^!toss^.     Discard <card> from your hand.\n")
        player.tell_cc("                     ^!draw^., ^!dr^.     Draw from the draw pile.\n")
        player.tell_cc("          ^!retrieve^. <suit>, ^!re^.     Retrieve top discard of <suit>.\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
        player.tell_cc("                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n")
        player.tell_cc("               ^!evaluate^., ^!eval^.     Evaluate the current scores.\n")
コード例 #40
0
ファイル: talpa.py プロジェクト: sunfall/giles
class Talpa(SeatedGame):
    """A Talpa game table implementation.  Invented in 2010 by Arty Sandler.
    """

    def __init__(self, server, table_name):

        super(Talpa, self).__init__(server, table_name)

        self.game_display_name = "Talpa"
        self.game_name = "talpa"
        self.seats = [Seat("Red"), Seat("Blue")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RTalpa^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Talpa-specific stuff.
        self.size = 8
        self.turn = None
        self.red = self.seats[0]
        self.red.data.seat_str = "^RRed/Vertical^~"
        self.blue = self.seats[1]
        self.blue.data.seat_str = "^BBlue/Horizontal^~"
        self.resigner = None
        self.layout = None

        # Like in most connection games, there is no difference between pieces
        # of a given color, so we save time and create our singleton pieces
        # here.
        self.rp = Piece("^R", "x", "X")
        self.rp.data.owner = self.red
        self.bp = Piece("^B", "o", "O")
        self.bp.data.owner = self.blue

        # Initialize the starting layout.
        self.init_layout()

    def init_layout(self):

        # Create the layout and fill it with pieces.
        self.layout = SquareGridLayout(highlight_color="^I")
        self.layout.resize(self.size)

        for i in range(self.size):
            for j in range(self.size):
                if (i + j) % 2:
                    self.layout.place(self.rp, i, j, update=False)
                else:
                    self.layout.place(self.bp, i, j, update=False)

        self.layout.update()

    def get_sp_str(self, seat):

        return "^C%s^~ (%s)" % (seat.player_name, seat.data.seat_str)

    def get_turn_str(self):

        if not self.turn:
            return "The game has not yet started.\n"

        return "It is ^C%s^~'s turn (%s).\n" % (self.turn.player_name, self.turn.data.seat_str)

    def show(self, player):

        player.tell_cc(self.layout)
        player.tell_cc(self.get_turn_str())

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def set_size(self, player, size_str):

        if not size_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        size = int(size_str)

        if size < MIN_SIZE or size > MAX_SIZE:
            self.tell_pre(player, "Size must be between %d and %d inclusive.\n" % (MIN_SIZE, MAX_SIZE))
            return False

        # Size must be even.
        if size % 2:
            self.tell_pre(player, "Size must be even.\n")
            return False

        # Valid!
        self.size = size
        self.bc_pre("^R%s^~ has set the board size to ^C%d^~.\n" % (player, size))
        self.init_layout()

    def move(self, player, src_bits, dst_bits):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        src_c, src_r = src_bits
        dst_c, dst_r = dst_bits

        # Are they valid?
        if not self.layout.is_valid(src_r, src_c) or not self.layout.is_valid(dst_r, dst_c):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Is it an orthogonal move?
        c_delta = abs(src_c - dst_c)
        r_delta = abs(src_r - dst_r)
        if c_delta > 1 or r_delta > 1 or (c_delta and r_delta):
            self.tell_pre(player, "You can only make a single-space orthogonal move.\n")
            return False

        # Is there a piece for this player in the source location?
        src_loc = self.layout.grid[src_r][src_c]
        if not src_loc or src_loc.data.owner != seat:
            self.tell_pre(player, "You must have a piece in the source location.\n")
            return False

        # Is there a piece for the other player in the destination location?
        dst_loc = self.layout.grid[dst_r][dst_c]
        if not dst_loc or dst_loc.data.owner == seat:
            self.tell_pre(player, "Your opponent must have a piece in the destination location.\n")
            return False

        # Phew.  Success.  Make the capture.
        src_str = "%s%s" % (COLS[src_c], src_r + 1)
        dst_str = "%s%s" % (COLS[dst_c], dst_r + 1)
        self.bc_pre("%s moves a piece from ^C%s^~ to ^G%s^~.\n" % (self.get_sp_str(seat), src_str, dst_str))
        self.layout.move(src_r, src_c, dst_r, dst_c, True)

        return True

    def has_capture(self, seat):

        # We loop through the board, checking each piece to see if it's for
        # this player.  If so, we check its four adjacencies to see if one
        # of them is for the other player.  If so, there's still a valid
        # capture this player can make.
        for r in range(self.size):
            for c in range(self.size):
                loc = self.layout.grid[r][c]
                if loc and loc.data.owner == seat:
                    for r_delta, c_delta in CONNECTION_DELTAS:
                        new_r = r + r_delta
                        new_c = c + c_delta
                        if self.layout.is_valid(new_r, new_c):
                            dst = self.layout.grid[new_r][new_c]
                            if dst and dst.data.owner != seat:
                                return True

        # We never found a valid capture.
        return False

    def remove(self, player, remove_bits):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't remove; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to remove.\n")
            return False

        c, r = remove_bits

        # Is it within bounds?
        if not self.layout.is_valid(r, c):
            self.tell_pre(player, "Your remove is out of bounds.\n")
            return False

        # Does this player have a piece there?
        loc = self.layout.grid[r][c]
        if not loc or loc.data.owner != seat:
            self.tell_pre(player, "You must have a piece there to remove.\n")
            return False

        # Do they have a valid capture instead?
        if self.has_capture(seat):
            self.tell_pre(player, "You have a capture left.\n")
            return False

        # All right, remove the piece.
        loc_str = "%s%s" % (COLS[c], r + 1)
        self.bc_pre("%s removes a piece from ^R%s^~.\n" % (self.get_sp_str(seat), loc_str))
        self.layout.remove(r, c, True)

        return True

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("%s is resigning from the game.\n" % self.get_sp_str(seat))
        return True

    def tick(self):

        # If both seats are occupied and the game is active, start.
        if self.state.get() == "need_players" and self.red.player and self.blue.player and self.active:
            self.bc_pre(
                "%s: ^C%s^~; %s: ^C%s^~\n"
                % (self.red.data.seat_str, self.red.player_name, self.blue.data.seat_str, self.blue.player_name)
            )
            self.state.set("playing")
            self.turn = self.red
            self.send_board()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.lower().split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz"):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid size command.\n")
                    handled = True

                elif primary in ("done", "ready", "d", "r"):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf"):

                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    self.state.set("setup")
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("move", "play", "mv", "pl"):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 2:
                        made_move = self.move(player, move_bits[0], move_bits[1])
                    else:
                        self.tell_pre(player, "Invalid move command.\n")
                    handled = True

                elif primary in ("remove", "re"):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.remove(player, move_bits[0])
                    else:
                        self.tell_pre(player, "Invalid remove command.\n")
                    handled = True

                elif primary in ("resign",):

                    made_move = self.resign(player)
                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()

                    if winner:

                        # Yup!
                        self.resolve(winner)
                        self.finish()
                    else:

                        # No.  Change turns and send the board to listeners.
                        self.turn = self.next_seat(self.turn)
                        self.send_board()

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def find_winner(self):

        # Did someone resign?
        if self.resigner == self.red:
            return self.blue
        elif self.resigner == self.blue:
            return self.red

        # Like most connection games, we will do a recursive check.  Unlike
        # most connection games, we're looking for a lack of pieces, not
        # their existence.  In addition, if both players won at the same
        # time, the mover loses.  We need two distinct adjacency maps since
        # we're looking at blank spaces, not pieces of a given color, and
        # those blank spaces can be used by either side.
        self.blue.data.adjacency_map = []
        self.red.data.adjacency_map = []
        for i in range(self.size):
            self.blue.data.adjacency_map.append([None] * self.size)
            self.red.data.adjacency_map.append([None] * self.size)

        self.red.data.won = False
        self.blue.data.won = False

        for i in range(self.size):
            if not self.red.data.won and not self.layout.grid[0][i]:
                self.recurse(self.red, 0, i)
            if not self.blue.data.won and not self.layout.grid[i][0]:
                self.recurse(self.blue, i, 0)

        # Handle the double-win state (mover loses) first.
        if self.red.data.won and self.blue.data.won:
            if self.turn == self.red:
                return self.blue
            else:
                return self.red

        # Now, normal winning states.
        elif self.red.data.won:
            return self.red
        elif self.blue.data.won:
            return self.blue

        # No winner.
        return None

    def recurse(self, seat, row, col):

        # Bail if this seat's already won.
        if seat.data.won:
            return

        # Bail if we're off the board.
        if not self.layout.is_valid(row, col):
            return

        # Bail if we've been here.
        if seat.data.adjacency_map[row][col]:
            return

        # Bail if there's a piece here.
        if self.layout.grid[row][col]:
            return

        # All right.  Empty and we haven't been here.  Mark.
        seat.data.adjacency_map[row][col] = True

        # Did we hit the winning side for this player?
        if seat == self.blue and col == self.size - 1:
            seat.data.won = True
            return
        elif seat == self.red and row == self.size - 1:
            seat.data.won = True
            return

        # Not a win yet.  Recurse over adjacencies.
        for r_delta, c_delta in CONNECTION_DELTAS:
            self.recurse(seat, row + r_delta, col + c_delta)

    def resolve(self, winner):

        self.send_board()
        self.bc_pre("%s wins!\n" % self.get_sp_str(winner))

    def show_help(self, player):

        super(Talpa, self).show_help(player)
        player.tell_cc("\nTALPA SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nTALPA PLAY:\n\n")
        player.tell_cc("          ^!move^. <ln> <ln2>, ^!mv^.     Move from <ln> to <ln2> (letter number).\n")
        player.tell_cc("               ^!remove^. <ln> ^!re^.     Remove piece at <ln> (letter number).\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #41
0
class SquareOust(SeatedGame):
    """A Square Oust game table implementation.  Invented in 2007 by Mark Steere.
    """
    def __init__(self, server, table_name):

        super(SquareOust, self).__init__(server, table_name)

        self.game_display_name = "Square Oust"
        self.game_name = "square_oust"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RSquare Oust^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Square Oust-specific stuff.
        self.height = 11
        self.width = 11
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.groups = []
        self.black.data.made_move = False
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.groups = []
        self.white.data.made_move = False
        self.resigner = None
        self.layout = None
        self.move_was_capture = False

        # Initialize the starting layout.
        self.init_layout()

    def init_layout(self):

        # Create the layout.  Empty, so easy.
        self.layout = SquareGridLayout(highlight_color="^I")
        self.layout.resize(self.width, self.height)

    def get_sp_str(self, seat):

        return "^C%s^~ (%s)" % (seat.player_name, seat.data.seat_str)

    def get_turn_str(self):

        if not self.turn:
            return "The game has not yet started.\n"

        return "It is ^C%s^~'s turn (%s).\n" % (self.turn.player_name,
                                                self.turn.data.seat_str)

    def show(self, player):

        player.tell_cc(self.layout)
        player.tell_cc(self.get_turn_str())

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def set_size(self, player, size_bits):

        # Is there an 'x' in the middle of a single argument?
        if len(size_bits) == 1:
            size_bits[0] = size_bits[0].lower()
            if "x" in size_bits[0]:
                size_bits = size_bits[0].split("x")

        width = size_bits[0]
        # If there's a single element, height == width.
        if len(size_bits) == 1:
            height = width
        elif len(size_bits) == 2:
            width = size_bits[0]
            height = size_bits[1]
        else:
            self.tell_pre(player, "Invalid size command.\n")
            return

        if not width.isdigit() or not height.isdigit():
            self.tell_pre(player, "You didn't even send numbers!\n")
            return

        w = int(width)
        h = int(height)

        if w < MIN_SIZE or w > MAX_SIZE or h < MIN_SIZE or h > MAX_SIZE:
            self.tell_pre(
                player,
                "Width and height must be between %d and %d inclusive.\n" %
                (MIN_SIZE, MAX_SIZE))
            return

        # Valid!
        self.width = w
        self.height = h
        self.bc_pre("^R%s^~ has set the board size to ^C%d^Gx^C%d^~.\n" %
                    (player, w, h))
        self.init_layout()

    def get_new_piece(self, seat):

        if seat == self.black:
            p = Piece("^K", "x", "X")
        else:
            p = Piece("^W", "o", "O")
        p.data.owner = seat
        p.data.adjacencies = []
        p.data.size = 1
        p.data.num = id(p)

        return p

    def replace(self, old, new):

        # First step: Replace the actual pieces on the board.
        for r in range(self.height):
            for c in range(self.width):
                if self.layout.grid[r][c] == old:
                    self.layout.place(new, r, c, update=False)
        self.layout.update()

        # Second step: Get rid of it from the group list of its owner.
        owner = new.data.owner
        owner.data.groups.remove(old)

        # Third step: Replace instances of it in the other player's group
        # adjacencies.
        for group in self.next_seat(owner).data.groups:
            if old in group.data.adjacencies:
                group.data.adjacencies.remove(old)
                if new not in group.data.adjacencies:
                    group.data.adjacencies.append(new)

    def remove(self, dead_group):

        # Like above, except removing this time.
        for r in range(self.height):
            for c in range(self.width):
                if self.layout.grid[r][c] == dead_group:
                    self.layout.remove(r, c, update=False)
        self.layout.update()

        owner = dead_group.data.owner
        owner.data.groups.remove(dead_group)

        for group in self.next_seat(owner).data.groups:
            if dead_group in group.data.adjacencies:
                group.data.adjacencies.remove(dead_group)

    def update_board(self, row, col):

        # We just put a fresh piece at this location; it will have to be
        # incorporated into everything else that's on the board.
        this_piece = self.layout.grid[row][col]

        # Look at all of the adjacencies and collapse the same-color groups
        # into one.  Collate the unique enemy groups as well, as we may be
        # capturing them.
        other_adjacencies = []
        potential_capture = False
        for r_delta, c_delta in CONNECTION_DELTAS:
            new_r = row + r_delta
            new_c = col + c_delta
            if self.layout.is_valid(new_r, new_c):
                loc = self.layout.grid[new_r][new_c]
                if loc and loc.data.owner == this_piece.data.owner:
                    potential_capture = True
                    if loc != this_piece:

                        # New same-color group to collapse.
                        other_adjacencies.extend([
                            x for x in loc.data.adjacencies
                            if x not in other_adjacencies
                        ])
                        new_size = this_piece.data.size + loc.data.size
                        if this_piece.data.num < loc.data.num:
                            self.replace(loc, this_piece)
                        else:
                            self.replace(this_piece, loc)
                            this_piece = loc

                        # Whichever "won," set the new size.
                        this_piece.data.size = new_size
                elif loc:

                    # A group of the other player.  Add to other adjacencies if
                    # it's not already there.
                    if loc not in other_adjacencies:
                        other_adjacencies.append(loc)

        # After having collapsed all of the same-colored groups, we look to see
        # if this is a potential capture.  If not, we can't affect the opponent's
        # groups, other than to become adjacent to them.  If so, all groups in the
        # list of other adjacencies must be removed from the board.
        if potential_capture:
            for group in other_adjacencies:
                self.remove(group)

            # By definition, a capturing group has no enemy adjacencies.
            this_piece.data.adjacencies = []

            # Return the number of groups we captured.
            return len(other_adjacencies)

        else:

            # Set the adjacency list.
            this_piece.data.adjacencies = other_adjacencies

            # Add ourselves to those pieces' adjacency lists.
            for group in other_adjacencies:
                if this_piece not in group.data.adjacencies:
                    group.data.adjacencies.append(this_piece)

            # No captures.
            return 0

    def move(self, player, move_bits):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        col, row = move_bits

        # Is the move on the board?
        if not self.layout.is_valid(row, col):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Is this move a valid play?
        if not self.is_valid_play(seat, row, col):
            self.tell_pre(player, "That move is not valid.\n")
            return False

        # Valid.  Put a piece there.
        move_str = "%s%s" % (COLS[col], row + 1)
        piece = self.get_new_piece(seat)
        seat.data.groups.append(piece)
        self.layout.place(piece, row, col, True)

        # Update the board, making any captures.
        capture_str = ""
        self.move_was_capture = False
        capture_count = self.update_board(row, col)
        if capture_count:
            capture_str = ", ^Ycapturing %s^~" % (get_plural_str(
                capture_count, "group"))
            self.move_was_capture = True
        self.bc_pre("%s places a piece at ^C%s^~%s.\n" %
                    (self.get_sp_str(seat), move_str, capture_str))

        seat.data.made_move = True
        return True

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("%s is resigning from the game.\n" % self.get_sp_str(seat))
        return True

    def tick(self):

        # If both seats are occupied and the game is active, start.
        if (self.state.get() == "need_players" and self.black.player
                and self.white.player and self.active):
            self.bc_pre("%s: ^C%s^~; %s: ^C%s^~\n" %
                        (self.black.data.seat_str, self.black.player_name,
                         self.white.data.seat_str, self.white.player_name))
            self.state.set("playing")
            self.turn = self.black
            self.send_board()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.lower().split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz"):

                    self.set_size(player, command_bits[1:])
                    handled = True

                elif primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):

                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    self.state.set("setup")
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in (
                        "move",
                        "play",
                        "mv",
                        "pl",
                ):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        self.tell_pre(player, "Invalid move command.\n")
                    handled = True

                elif primary in ("resign", ):

                    made_move = self.resign(player)
                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()

                    if winner:

                        # Yup!
                        self.resolve(winner)
                        self.finish()
                    else:

                        # No.  If the move was not a capturing move, see if the
                        # next player has a move; if so, change turns.  If not,
                        # print a message and stay here.
                        other = self.next_seat(self.turn)
                        if not self.move_was_capture:
                            if not self.has_move(other):
                                self.bc_pre(
                                    "%s has no valid move; ^Rskipping their turn^~.\n"
                                    % self.get_sp_str(other))
                            else:
                                self.turn = other

                        elif not self.has_move(self.turn):
                            self.bc_pre("%s has no further valid moves.\n" %
                                        self.get_sp_str(self.turn))
                            self.turn = other

                        else:
                            self.bc_pre("%s continues their turn.\n" %
                                        self.get_sp_str(self.turn))

                        # No matter what, send the board again.
                        self.send_board()

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def is_valid_play(self, seat, row, col):

        # Obviously we can't place a piece if there's already one here or we're
        # out of bounds.
        if not self.layout.is_valid(row, col) or self.layout.grid[row][col]:
            return False

        # Okay; can we place a piece here?  Check the adjacent spaces; if there
        # are any pieces owned by this seat, the sum total of their sizes must
        # be equal to the largest enemy group adjacent either to them or this
        # new piece.  If there are no pieces owned by this seat, it's valid.
        same_list = []
        same_total = 0
        largest_other = 0

        for r_delta, c_delta in CONNECTION_DELTAS:
            new_r = row + r_delta
            new_c = col + c_delta
            if self.layout.is_valid(new_r, new_c):
                loc = self.layout.grid[new_r][new_c]
                if loc and loc.data.owner == seat:
                    if loc not in same_list:
                        same_list.append(loc)
                        same_total += loc.data.size
                        for other in loc.data.adjacencies:
                            if other.data.size > largest_other:
                                largest_other = other.data.size
                elif loc:
                    if loc.data.size > largest_other:
                        largest_other = loc.data.size

        # If we didn't find an adjacent same-colored piece, it is immediately
        # valid.
        if not same_total:
            return True

        # If we found same-colored pieces but no other groups, this is not a
        # valid play.
        if not largest_other:
            return False

        # Otherwise, check the sum from the same_list and see if that equals
        # or is greater than the largest other group.
        if same_total >= largest_other:
            return True

        # Not a valid play.
        return False

    def has_move(self, seat):

        for r in range(self.height):
            for c in range(self.width):
                if self.is_valid_play(seat, r, c):
                    return True

        # We checked every location and found no legitimate location.  No move.
        return False

    def find_winner(self):

        # Did someone resign?
        if self.resigner == self.white:
            return self.black
        elif self.resigner == self.black:
            return self.white

        # If one player has no pieces left, the other player won.
        if not len(self.white.data.groups) and self.white.data.made_move:
            return self.black
        elif not len(self.black.data.groups) and self.black.data.made_move:
            return self.white

        # No winner.
        return None

    def resolve(self, winner):

        self.send_board()
        self.bc_pre("%s wins!\n" % self.get_sp_str(winner))

    def show_help(self, player):

        super(SquareOust, self).show_help(player)
        player.tell_cc("\nSQUARE OUST SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nSQUARE OUST PLAY:\n\n")
        player.tell_cc(
            "      ^!move^. <ln>, ^!play^., ^!mv^., ^!pl^.     Make move <ln> (letter number).\n"
        )
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #42
0
ファイル: crossway.py プロジェクト: sunfall/giles
class Crossway(SeatedGame):
    """A Crossway game table implementation.  Invented in 2007 by Mark Steere.
    """

    def __init__(self, server, table_name):

        super(Crossway, self).__init__(server, table_name)

        self.game_display_name = "Crossway"
        self.game_name = "crossway"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RCrossway^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Crossway-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 19
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[1].data.side = WHITE
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()

    def init_board(self):

        self.board = []

        # Generate a new empty board.
        for r in range(self.size):
            self.board.append([None] * self.size)

    def update_printable_board(self):

        self.printable_board = []
        col_str = "    " + "".join([" " + COLS[i] for i in range(self.size)])
        self.printable_board.append(col_str + "\n")
        self.printable_board.append("   ^m.=" + "".join(["=="] * self.size) + ".^~\n")
        for r in range(self.size):
            this_str = "%2d ^m|^~ " % (r + 1)
            for c in range(self.size):
                if r == self.last_r and c == self.last_c:
                    this_str += "^5"
                loc = self.board[r][c]
                if loc == WHITE:
                    this_str += "^Wo^~ "
                elif loc == BLACK:
                    this_str += "^Kx^~ "
                else:
                    this_str += "^M.^~ "
            this_str += "^m|^~ %d" % (r + 1)
            self.printable_board.append(this_str + "\n")
        self.printable_board.append("   ^m`=" + "".join(["=="] * self.size) + "'^~\n")
        self.printable_board.append(col_str + "\n")

    def show(self, player):

        if not self.printable_board:
            self.update_printable_board()
        for line in self.printable_board:
            player.tell_cc(line)
        player.tell_cc(self.get_turn_str() + "\n")

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def get_turn_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        if self.turn == BLACK:
            player = self.seats[0].player_name
            color_msg = "^KBlack/Vertical^~"
        else:
            player = self.seats[1].player_name
            color_msg = "^WWhite/Horizontal^~"

        return ("It is ^Y%s^~'s turn (%s)." % (player, color_msg))

    def is_valid(self, row, col):

        if row < 0 or row >= self.size or col < 0 or col >= self.size:
            return False
        return True

    def is_checkerboard(self, color, row, col):

        # Bail immediately if we're given bad input.
        if not self.is_valid(row, col):
            return False

        if self.board[row][col]:
            return False

        # Okay.  Let's check all four checkerboard deltas.
        found = False
        for r_delta, c_delta in CHECKERBOARD_DELTAS:

            # Only bother if this is on the board or we already found it.
            if not found and self.is_valid(row + r_delta, col + c_delta):

                # If the delta space is this color, and the two adjacent spaces
                # in that direction are the other color, it's a checkerboard.
                if self.board[row + r_delta][col + c_delta] == color:
                    corner_one = self.board[row + r_delta][col]
                    corner_two = self.board[row][col + c_delta]
                    if (corner_one and corner_one == corner_two and
                       corner_one != color):

                        # Not empty, is another color.  Checkerboard.
                        found = True

        return found

    def move(self, player, play):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't move; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to move.\n")
            return False

        col, row = play

        # Make sure they're all in range.
        if not self.is_valid(row, col):
            player.tell_cc(self.prefix + "Your move is out of bounds.\n")
            return False

        # Is the space empty?
        if self.board[row][col]:
            player.tell_cc(self.prefix + "That space is already occupied.\n")
            return False

        # Does the move violate the no-checkerboard rule?
        if self.is_checkerboard(self.turn, row, col):
            player.tell_cc(self.prefix + "That move creates a checkerboard.\n")
            return False

        # This is a valid move.  Apply, announce.
        self.board[row][col] = self.turn
        play_str = "%s%s" % (COLS[col], row + 1)
        self.channel.broadcast_cc(self.prefix + "^Y%s^~ places a piece at ^C%s^~.\n" % (seat.player, play_str))
        self.last_r = row
        self.last_c = col
        self.turn_number += 1

        return True

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
           and self.seats[1].player and self.active):
            self.state.set("playing")
            self.channel.broadcast_cc(self.prefix + "^KBlack/Vertical^~: ^R%s^~; ^WWhite/Horizontal^~: ^Y%s^~\n" %
               (self.seats[0].player, self.seats[1].player))
            self.turn = BLACK
            self.turn_number = 1
            self.send_board()

    def set_size(self, player, size_bits):

        if not size_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        size = int(size_bits)

        if size < MIN_SIZE or size > MAX_SIZE:
            player.tell_cc(self.prefix + "Size must be between %d and %d inclusive.\n" % (MIN_SIZE, MAX_SIZE))
            return

        # Valid!
        self.size = size
        self.channel.broadcast_cc(self.prefix + "^R%s^~ has set the board size to ^C%d^~.\n" % (player, size))
        self.init_board()
        self.update_printable_board()

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat.data.side
        self.channel.broadcast_cc(self.prefix + "^R%s^~ is resigning from the game.\n" % player)
        return True

    def swap(self, player):

        # Like Hex, a swap in Crossway requires a translation to make it the
        # equivalent move for the other player.

        self.board[self.last_r][self.last_c] = None
        self.board[self.last_c][self.last_r] = WHITE
        self.last_c, self.last_r = self.last_r, self.last_c

        self.channel.broadcast_cc("^Y%s^~ has swapped ^KBlack^~'s first move.\n" % (player))
        self.turn_number += 1

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.lower().split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz",):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid size command.\n")
                    handled = True

                if primary in ("done", "ready", "d", "r",):

                    self.channel.broadcast_cc(self.prefix + "The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):

                    self.state.set("setup")
                    self.channel.broadcast_cc(self.prefix + "^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("move", "play", "mv", "pl",):

                    invalid = False
                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        invalid = True

                    if invalid:
                        player.tell_cc(self.prefix + "Invalid move command.\n")
                    handled = True

                elif primary in ("swap",):

                    if self.seats[1].player == player and self.turn_number == 2:
                        self.swap(player)
                        made_move = True
                    else:
                        player.tell_cc(self.prefix + "Invalid swap command.\n")
                    handled = True

                elif primary in ("resign",):

                    if self.resign(player):
                        made_move = True

                    handled = True

                if made_move:

                    # Okay, something happened on the board.  Update.
                    self.update_printable_board()

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()
                    else:

                        # Nope.  Switch turns...
                        if self.turn == BLACK:
                            self.turn = WHITE
                        else:
                            self.turn = BLACK

                        # ...show everyone the board, and keep on.
                        self.send_board()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.
        if self.resigner == WHITE:
            return self.seats[0].player_name
        elif self.resigner == BLACK:
            return self.seats[1].player_name

        # This is like most connection games; we check recursively from the
        # top and left edges to see whether a player has won.
        self.found_winner = False
        self.adjacency_map = []
        for i in range(self.size):
            self.adjacency_map.append([None] * self.size)

        for i in range(self.size):
            if self.board[i][0] == WHITE:
                self.recurse_adjacency(WHITE, i, 0)
            if self.board[0][i] == BLACK:
                self.recurse_adjacency(BLACK, 0, i)

        if self.found_winner == BLACK:
            return self.seats[0].player_name
        elif self.found_winner == WHITE:
            return self.seats[1].player_name

        # No winner yet.
        return None

    def recurse_adjacency(self, color, row, col):

        # Bail if we found a winner already.
        if self.found_winner:
            return

        # Bail if we're off the board.
        if not self.is_valid(row, col):
            return

        # Bail if we've been here.
        if self.adjacency_map[row][col]:
            return

        # Bail if this is the wrong color.
        if self.board[row][col] != color:
            return

        # Okay.  Occupied and it's this player's.  Mark.
        self.adjacency_map[row][col] = True

        # Have we hit the winning side for this player?
        if ((color == WHITE and col == self.size - 1) or
           (color == BLACK and row == self.size - 1)):

            # Success!
            self.found_winner = color
            return

        # Not a win yet.  Recurse over adjacencies.
        for r_delta, c_delta in CONNECTION_DELTAS:
            self.recurse_adjacency(color, row + r_delta, col + c_delta)

    def resolve(self, winner):
        self.send_board()
        self.channel.broadcast_cc(self.prefix + "^C%s^~ wins!\n" % winner)

    def show_help(self, player):

        super(Crossway, self).show_help(player)
        player.tell_cc("\nCROSSWAY SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nCROSSWAY PLAY:\n\n")
        player.tell_cc("      ^!move^. <ln>, ^!play^., ^!mv^., ^!pl^.     Make move <ln> (letter number).\n")
        player.tell_cc("                         ^!swap^.     Swap the first move (only White, only their first).\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #43
0
ファイル: gonnect.py プロジェクト: sunfall/giles
class Gonnect(SeatedGame):
    """A Gonnect table implementation.  Gonnect was invented by Joao Pedro
    Neto in 2000.
    """

    def __init__(self, server, table_name):

        super(Gonnect, self).__init__(server, table_name)

        self.game_display_name = "Gonnect"
        self.game_name = "gonnect"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RGonnect^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Gonnect-specific stuff.
        self.turn = None
        self.seats[0].data.side = BLACK
        self.seats[0].data.dir_str = "/Vertical"
        self.seats[1].data.side = WHITE
        self.seats[1].data.dir_str = "/Horizontal"
        self.directional = False
        self.resigner = None
        self.turn_number = 0
        self.goban = giles.games.goban.Goban()
        self.adjacency_map = None
        self.found_winner = False

        # A traditional Gonnect board is 13x13.
        self.goban.resize(13, 13)

    def show(self, player):

        if not self.goban.printable_board:
            self.goban.update_printable_board()
        for line in self.goban.printable_board:
            player.tell_cc(line)
        player.tell_cc(self.get_supplemental_str())

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def get_stone_str(self, count):

        if count == 1:
            return "1 stone"
        return "%d stones" % count

    def get_supplemental_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        dir_str = ""
        if self.turn == BLACK:
            player = self.seats[0].player_name
            if self.directional:
                dir_str = self.seats[0].data.dir_str
            color_msg = "^KBlack" + dir_str + "^~"
        else:
            player = self.seats[1].player_name
            if self.directional:
                dir_str = self.seats[1].data.dir_str
            color_msg = "^WWhite" + dir_str + "^~"

        to_return = "It is ^Y%s^~'s turn (%s).\n" % (player, color_msg)
        return(to_return)

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
           and self.seats[1].player and self.active):
            self.state.set("playing")
            if self.directional:
                black_dir_str = self.seats[0].data.dir_str
                white_dir_str = self.seats[1].data.dir_str
            else:
                black_dir_str = ""
                white_dir_str = ""
            self.channel.broadcast_cc(self.prefix + "^KBlack%s^~: ^R%s^~; ^WWhite%s^~: ^Y%s^~\n" %
               (black_dir_str, self.seats[0].player, white_dir_str, self.seats[1].player))
            self.turn = BLACK
            self.turn_number = 1
            self.send_board()

    def set_size(self, player, size_bits):

        # Is there an 'x' in the middle of a single argument?
        if len(size_bits) == 1:
            size_bits[0] = size_bits[0].lower()
            if "x" in size_bits[0]:
                size_bits = size_bits[0].split("x")

        width = size_bits[0]
        # If there's a single element, height == width.
        if len(size_bits) == 1:
            height = width
        elif len(size_bits) == 2:
            width = size_bits[0]
            height = size_bits[1]
        else:
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        if not width.isdigit() or not height.isdigit():
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        w = int(width)
        h = int(height)

        if w < MIN_SIZE or w > MAX_SIZE or h < MIN_SIZE or h > MAX_SIZE:
            player.tell_cc(self.prefix + "Width and height must be between %d and %d inclusive.\n" % (MIN_SIZE, MAX_SIZE))
            return

        # We disallow uneven boards if we have directional goals.
        if self.directional and w != h:
            player.tell_cc(self.prefix + "Directional games must have square boards.\n")
            return

        # Valid!
        self.goban.resize(w, h)
        self.channel.broadcast_cc(self.prefix + "^R%s^~ has set the board size to ^C%d^Gx^C%d^~.\n" % (player, w, h))

    def set_directional(self, player, dir_bits):

        dir_bool = booleanize(dir_bits)
        if dir_bool:
            if dir_bool > 0:
                if self.goban.height != self.goban.width:
                    player.tell_cc(self.prefix + "Cannot change to directional with uneven sides.  Resize first.\n")
                    return
                self.directional = True
                display_str = "^Con^~"
            elif dir_bool < 0:
                self.directional = False
                display_str = "^coff^~"
            self.channel.broadcast_cc(self.prefix + "^R%s^~ has turned directional goals %s.\n" % (player, display_str))
        else:
            player.tell_cc(self.prefix + "Not a valid boolean!\n")

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat.data.side
        self.channel.broadcast_cc(self.prefix + "^R%s^~ is resigning from the game.\n" % player)
        return True

    def move(self, player, move):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't move; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to move.\n")
            return False

        # Check bounds.
        col, row = move
        if row < 0 or row >= self.goban.height or col < 0 or col >= self.goban.width:
            player.tell_cc(self.prefix + "Your move is out of bounds.\n")
            return False

        # Check that the space is empty.
        if self.goban.board[row][col]:
            player.tell_cc(self.prefix + "That space is already occupied.\n")
            return False

        # Is this move suicidal?  If so, it can't be played.
        if self.goban.move_is_suicidal(seat.data.side, row, col):
            player.tell_cc(self.prefix + "That move is suicidal.\n")
            return False

        # Does this move cause a repeat of a previous board?
        if self.goban.move_causes_repeat(seat.data.side, row, col):
            player.tell_cc(self.prefix + "That move causes a repeat of a previous board.\n")
            return False

        # Okay, this looks like a legitimate move.
        move_return = self.goban.go_play(seat.data.side, row, col,
                                         suicide_is_valid=False)

        if not move_return:
            player.tell_cc(self.prefix + "That move was unsuccessful.  Weird.\n")
            return False

        else:
            coords, capture_color, capture_list = move_return
            move_str = "%s%s" % (LETTERS[col], row + 1)
            capture_str = ""
            if capture_color:

                # Captured opponent pieces!
                capture_str += ", ^!capturing %s^." % (self.get_stone_str(len(capture_list)))

            # And no matter what, print information about the move.
            self.channel.broadcast_cc(self.prefix + "^Y%s^~ places a stone at ^C%s^~%s.\n" % (player, move_str, capture_str))

            self.turn_number += 1

            return True

    def swap(self, player):

        self.goban.invert(self.directional)
        self.channel.broadcast_cc(self.prefix + "^Y%s^~ has swapped ^KBlack^~'s first move.\n" % (player))
        self.turn_number += 1

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz",):

                    self.set_size(player, command_bits[1:])
                    handled = True

                if primary in ("directional", "goals", "dir", "goal",):
                    self.set_directional(player, command_bits[1])
                    handled = True

                if primary in ("done", "ready", "d", "r",):

                    self.channel.broadcast_cc(self.prefix + "The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):

                    self.state.set("setup")
                    self.channel.broadcast_cc(self.prefix + "^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("move", "play", "mv", "pl",):

                    invalid = False
                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        invalid = True

                    if invalid:
                        player.tell_cc(self.prefix + "Invalid move command.\n")
                    handled = True

                elif primary in ("swap",):
                    if self.turn_number == 2 and self.seats[1].player == player:
                        self.swap(player)
                        made_move = True
                    else:
                        player.tell_cc(self.prefix + "Unsuccessful swap.\n")
                    handled = True

                elif primary in ("resign",):

                    if self.resign(player):
                        made_move = True

                    handled = True

                if made_move:

                    if self.turn == BLACK:
                        self.turn = WHITE
                    else:
                        self.turn = BLACK

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()
                    else:
                        # Nope.  show everyone the board, and keep on.
                        self.send_board()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def reset_adjacency(self):

        self.adjacency_map = []
        for r in range(self.goban.height):
            self.adjacency_map.append([None] * self.goban.width)

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.
        if self.resigner == WHITE:
            return self.seats[0].player_name
        elif self.resigner == BLACK:
            return self.seats[1].player_name

        # Okay, we have to check the board.  First, determine which
        # checks we need to make.  In a directional game, we only
        # need to test the left and top edges for White and Black
        # respectively; otherwise we need to test both edges for
        # both players.

        self.found_winner = False
        self.reset_adjacency()

        for r in range(self.goban.height):
            self.recurse_adjacencies(WHITE, r, 0, TEST_RIGHT)
        if not self.found_winner:
            for c in range(self.goban.width):
                self.recurse_adjacencies(BLACK, 0, c, TEST_DOWN)

        if not self.found_winner and not self.directional:

            # Gotta test both edges with the other colors.  Reset the
            # adjacency graph, as the previous entries will now conflict.
            self.reset_adjacency()

            for r in range(self.goban.height):
                self.recurse_adjacencies(BLACK, r, 0, TEST_RIGHT)
            if not self.found_winner:
                for c in range(self.goban.width):
                    self.recurse_adjacencies(WHITE, 0, c, TEST_DOWN)

        if self.found_winner == BLACK:
            return self.seats[0].player_name
        elif self.found_winner == WHITE:
            return self.seats[1].player_name

        # Blarg, still no winner.  See if the next player (we've already
        # switched turns) has no valid moves.  If so, the current player
        # wins.
        for r in range(self.goban.height):
            for c in range(self.goban.width):
                if (not self.goban.board[r][c] and
                   not self.goban.move_is_suicidal(self.turn, r, c) and
                   not self.goban.move_causes_repeat(self.turn, r, c)):

                    # Player has a non-suicidal move.  No winner.
                    return None

        # Checked all valid moves for the next player, and they're all
        # suicidal.  This player wins.
        if self.turn == WHITE:
            return self.seats[0].player_name
        else:
            return self.seats[1].player_name

    def recurse_adjacencies(self, color, row, col, test_dir):

        # Bail if a winner's been found.
        if self.found_winner:
            return

        # Bail if we're off the board.
        if (row < 0 or row >= self.goban.height or
           col < 0 or col >= self.goban.width):
            return

        # Bail if we've visited this location.
        if self.adjacency_map[row][col]:
            return

        # Bail if it's the wrong color.
        if self.goban.board[row][col] != color:
            return

        # Okay, it's the right color.  Mark it visited...
        self.adjacency_map[row][col] = True

        # Have we reached the proper side?
        if ((test_dir == TEST_RIGHT and col == self.goban.width - 1) or
           (test_dir == TEST_DOWN and row == self.goban.height - 1)):

            # Winner!
            self.found_winner = color
            return

        # Not a win yet... so we need to test the four adjacencies.
        for r_delta, c_delta in SQUARE_DELTAS:
            self.recurse_adjacencies(color, row + r_delta, col + c_delta,
               test_dir)

    def resolve(self, winner):
        self.send_board()
        self.channel.broadcast_cc(self.prefix + "^C%s^~ wins!\n" % winner)

    def show_help(self, player):

        super(Gonnect, self).show_help(player)
        player.tell_cc("\nGONNECT SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("    ^!size^. <size> | <w> <h>, ^!sz^.     Set board to <size>x<size>/<w>x<h>.\n")
        player.tell_cc("      ^!directional^. off|on, ^!dir^.     Turn directional goals off|on.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nGONNECT PLAY:\n\n")
        player.tell_cc("                ^!move^. <ln>, ^!mv^.     Place stone at <ln> (letter number).\n")
        player.tell_cc("                         ^!swap^.     Swap first move (White only, first only).\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #44
0
ファイル: ataxx.py プロジェクト: Cloudxtreme/giles
class Ataxx(SeatedGame):
    """An Ataxx game table implementation.  Invented in 1988 by Dave Crummack
    and Craig Galley.
    """
    def __init__(self, server, table_name):

        super(Ataxx, self).__init__(server, table_name)

        self.game_display_name = "Ataxx"
        self.game_name = "ataxx"
        self.seats = [
            Seat("Red"),
            Seat("Blue"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RAtaxx^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Ataxx-specific stuff.
        self.board = None
        self.printable_board = None
        self.sides = {}
        self.size = 7
        self.player_mode = 2
        self.turn = None
        self.last_r = None
        self.last_c = None

        self.init_seats()
        self.init_board()

    def init_board(self):

        self.board = []
        for r in range(self.size):
            self.board.append([None] * self.size)

        # Place starting pieces, depending on the number of players.
        bottom_left = BLUE
        bottom_right = RED
        if self.player_mode == 4:
            bottom_left = YELLOW
            bottom_right = GREEN

        self.board[0][0] = RED
        self.board[0][self.size - 1] = BLUE
        self.board[self.size - 1][0] = bottom_left
        self.board[self.size - 1][self.size - 1] = bottom_right

        self.update_printable_board()

    def init_seats(self):

        # If we're in 2-player mode, and there are 4 seats, delete the
        # extras.
        if self.player_mode == 2 and len(self.seats) == 4:
            del self.seats[2]
            del self.seats[2]

        self.sides = {}
        # Set the sides and data for players one and two.
        self.seats[0].data.side = RED
        self.seats[0].data.count = 2
        self.seats[0].data.resigned = False
        self.seats[1].data.side = BLUE
        self.seats[1].data.count = 2
        self.seats[1].data.resigned = False
        self.sides[RED] = self.seats[0]
        self.sides[BLUE] = self.seats[1]

        # If there are four players...
        if self.player_mode == 4:

            # ...and only two seats, create them.
            if len(self.seats) == 2:
                self.seats.append(Seat("Green"))
                self.seats.append(Seat("Yellow"))

            # Either way, set the sides and data.
            self.seats[2].data.side = GREEN
            self.seats[2].data.resigned = False
            self.sides[GREEN] = self.seats[2]
            self.seats[3].data.side = YELLOW
            self.seats[3].data.resigned = False
            self.sides[YELLOW] = self.seats[3]

            self.seats[0].data.count = 1
            self.seats[1].data.count = 1
            self.seats[2].data.count = 1
            self.seats[3].data.count = 1

    def change_player_mode(self, count):

        # Don't bother if it's the mode we're already in.
        if count == self.player_mode:
            return False

        # Don't bother if it's not a valid option either.
        if count != 2 and count != 4:
            return False

        # Okay.  Set values...
        self.player_mode = count
        self.min_players = count
        self.max_players = count

        # ...initialize the seats...
        self.init_seats()

        # ...and reinitialize the board.
        self.init_board()

    def update_printable_board(self):

        self.printable_board = []
        col_str = "    " + "".join([" " + COLS[i] for i in range(self.size)])
        self.printable_board.append(col_str + "\n")
        self.printable_board.append("   ^m.=" + "".join(["=="] * self.size) +
                                    ".^~\n")
        for r in range(self.size):
            this_str = "%2d ^m|^~ " % (r + 1)
            for c in range(self.size):
                if r == self.last_r and c == self.last_c:
                    this_str += "^I"
                loc = self.board[r][c]
                if loc == RED:
                    this_str += "^RR^~ "
                elif loc == BLUE:
                    this_str += "^BB^~ "
                elif loc == GREEN:
                    this_str += "^GG^~ "
                elif loc == YELLOW:
                    this_str += "^YY^~ "
                elif loc == PIT:
                    this_str += "^Ko^~ "
                else:
                    this_str += "^M.^~ "
            this_str += "^m|^~ %d" % (r + 1)
            self.printable_board.append(this_str + "\n")
        self.printable_board.append("   ^m`=" + "".join(["=="] * self.size) +
                                    "'^~\n")
        self.printable_board.append(col_str + "\n")

    def get_info_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        if self.turn == RED:
            name = self.seats[0].player_name
            turn_str = "^RRed^~"
        elif self.turn == BLUE:
            name = self.seats[1].player_name
            turn_str = "^BBlue^~"
        elif self.turn == GREEN:
            name = self.seats[2].player_name
            turn_str = "^GGreen^~"
        else:
            name = self.seats[3].player_name
            turn_str = "^YYellow^~"

        info_str = "It is %s's turn (%s).\n" % (name, turn_str)
        info_str += "^RRed^~: %d  ^BBlue^~: %d" % (self.seats[0].data.count,
                                                   self.seats[1].data.count)
        if self.player_mode == 4:
            info_str += "  ^GGreen^~: %d  ^YYellow^~: %d" % (
                self.seats[2].data.count, self.seats[3].data.count)
        info_str += "\n"
        return (info_str)

    def show(self, player):

        if not self.printable_board:
            self.update_printable_board()
        for line in self.printable_board:
            player.tell_cc(line)
        player.tell_cc(self.get_info_str())

    def send_board(self):

        for listener in self.channel.listeners:
            self.show(listener)

    def is_valid(self, row, col):

        # Note that this does /not/ care about pits, just about the proper
        # ranges for coordinates.
        if row < 0 or row >= self.size or col < 0 or col >= self.size:
            return False
        return True

    def piece_has_move(self, row, col):

        # Returns whether or not a given piece has a potential move.

        # Bail on dud data.
        if not self.is_valid(row, col) or not self.board[row][col]:
            return False

        # Okay.  A piece can potentially move anywhere in a 5x5 area centered
        # on its location.
        found_move = False
        for r_d in range(-2, 3):  # <--- why I hate range syntax.
            for c_d in range(-2, 3):
                if not found_move and (self.is_valid(row + r_d, col + c_d) and
                                       not self.board[row + r_d][col + c_d]):
                    found_move = True

        # Return whether we found a move or not.
        return found_move

    def color_has_move(self, color):

        # Returns whether or not a given side has a potential move.

        # Bail immediately if green or yellow and we're in 2p mode.
        if self.player_mode == 2 and (color == YELLOW or color == GREEN):
            return False

        # Bail if this player has resigned.
        if ((color == RED and self.seats[0].data.resigned)
                or (color == BLUE and self.seats[1].data.resigned)
                or (color == GREEN and self.seats[2].data.resigned)
                or (color == YELLOW and self.seats[3].data.resigned)):
            return False

        # Okay.  Scan the board for pieces...
        for r in range(self.size):
            for c in range(self.size):
                if self.board[r][c] == color and self.piece_has_move(r, c):
                    return True

        # Found no moves.  This color has no valid moves.
        return False

    def loc_to_str(self, row, col):
        return "%s%s" % (COLS[col], row + 1)

    def move(self, player, src_loc, dst_loc):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix +
                           "You can't move; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix +
                           "You must wait for your turn to move.\n")
            return False

        if src_loc == dst_loc:
            player.tell_cc(self.prefix + "You can't make a non-move move!\n")
            return False

        src_c, src_r = src_loc
        dst_c, dst_r = dst_loc
        if not self.is_valid(src_c, src_r) or not self.is_valid(dst_c, dst_r):
            player.tell_cc(self.prefix + "Your move is out of bounds.\n")
            return False

        src_str = self.loc_to_str(src_r, src_c)
        dst_str = self.loc_to_str(dst_r, dst_c)

        # Do they have a piece at the source?
        color = seat.data.side
        if self.board[src_r][src_c] != color:
            player.tell_cc(self.prefix +
                           "You don't have a piece at ^C%s^~.\n" % src_str)
            return False

        # Is the destination within range?
        if abs(src_r - dst_r) > 2 or abs(src_c - dst_c) > 2:
            player.tell_cc(self.prefix + "That move is too far.\n")
            return False

        # Is the destination empty?
        if self.board[dst_r][dst_c]:
            player.tell_cc(self.prefix +
                           "^C%s^~ is already occupied.\n" % dst_str)
            return False

        # In range, to an empty cell.  It's a valid move.  Mark it.
        self.last_r = dst_r
        self.last_c = dst_c

        # Now, is it a split or a leap?
        if abs(src_r - dst_r) < 2 and abs(src_c - dst_c) < 2:

            # Split.  Add a new piece, increase the count.
            action_str = "^Mgrew^~ into"
            self.board[dst_r][dst_c] = color
            seat.data.count += 1
        else:

            # Leap.  Move the piece, don't increase the count.
            action_str = "^Cjumped^~ to"
            self.board[src_r][src_c] = None
            self.board[dst_r][dst_c] = color

        # Whichever action occurred, check all cells surrounding the
        # destination.  If they are opponents, transform them.
        change_count = 0
        change_str = ""
        for r_d in range(-1, 2):
            for c_d in range(-1, 2):
                if self.is_valid(dst_r + r_d, dst_c + c_d):
                    occupier = self.board[dst_r + r_d][dst_c + c_d]
                    if occupier and occupier != color and occupier != PIT:

                        # Another player.  Uh oh!  Flip it and decrement that
                        # player's count.
                        self.board[dst_r + r_d][dst_c + c_d] = color
                        seat.data.count += 1
                        self.sides[occupier].data.count -= 1
                        change_count += 1

        if change_count:
            change_str = ", ^!converting %d piece" % change_count
            if change_count != 1:
                change_str += "s"

        # Tell everyone what just happened.
        self.channel.broadcast_cc(
            self.prefix + "From ^c%s^~, %s %s ^C%s^~%s^~.\n" %
            (src_str, player, action_str, dst_str, change_str))

        self.update_printable_board()
        return True

    def toggle_pits(self, player, loc_list):

        # Undocumented bonus feature: handles multiple locations, but if
        # any of them are invalid, it'll bail halfway through.  Useful for
        # prepping a particular cool layout with a single cut-and-pasted
        # string, though.
        for loc in loc_list:

            col, row = loc

            # Bail if out of bounds.
            if not self.is_valid(row, col):
                player.tell_cc(self.prefix + "Pit out of bounds.\n")
                return

            # Bail if a starting piece is there.
            thing_there = self.board[row][col]
            if thing_there and not (thing_there == PIT):
                player.tell_cc(self.prefix +
                               "Cannot put a pit on a starting piece.\n")
                return

            # Since it's a toggle, figure out what we're toggling to.
            if thing_there:
                new_thing = None
                action_str = "^cremoved^~"
            else:
                new_thing = PIT
                action_str = "^Cadded^~"

            # Tentative place the thing.
            self.board[row][col] = new_thing

            # Does it keep red or blue (which, in a 4p game, is equivalent to
            # all four players) from being able to make a move?  If so, it's
            # invalid.  Put the board back the way it was.
            if not self.color_has_move(RED) or not self.color_has_move(BLUE):
                player.tell_cc(self.prefix +
                               "Players must have a valid move.\n")
                self.board[row][col] = thing_there
                return

            loc_list = [(row, col)]

            edge = self.size - 1

            # In either mode, we place another pit across the center line,
            # but not if that's the same location as the one we just placed
            # (on the center line on odd-sized boards).
            if (edge - row) != row:
                self.board[edge - row][col] = new_thing
                loc_list.append((edge - row, col))

                # Handle the 4p down-reflection if necessary.
                if self.player_mode == 4 and (edge - col) != col:
                    self.board[edge - row][edge - col] = new_thing
                    loc_list.append((edge - row, edge - col))

            # Handle the 4p right-reflection if necessary.
            if self.player_mode == 4 and (edge - col) != col:
                self.board[row][edge - col] = new_thing
                loc_list.append((row, edge - col))

            # Generate the list of locations.
            loc_str = ", ".join(
                ["^C%s^~" % self.loc_to_str(x[0], x[1]) for x in loc_list])

            # Finally, send the string detailing what just happened.
            self.channel.broadcast_cc(self.prefix +
                                      "^Y%s^~ has %s a pit at: %s\n" %
                                      (player, action_str, loc_str))
            self.update_printable_board()

    def set_size(self, player, size_bits):

        if not size_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        size = int(size_bits)

        if size < MIN_SIZE or size > MAX_SIZE:
            player.tell_cc(self.prefix +
                           "Size must be between %d and %d inclusive.\n" %
                           (MIN_SIZE, MAX_SIZE))
            return

        # Valid!
        self.size = size
        self.channel.broadcast_cc(
            self.prefix + "^R%s^~ has set the board size to ^C%d^~.\n" %
            (player, size))
        self.init_board()
        self.update_printable_board()

    def set_player_mode(self, player, mode_bits):

        if not mode_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid player mode command.\n")
            return

        mode = int(mode_bits)

        if mode != 2 and mode != 4:
            player.tell_cc(self.prefix +
                           "This game only supports two or four players.\n")
            return

        elif mode == self.player_mode:
            player.tell_cc(self.prefix +
                           "This table is already in that mode.\n")
            return

        else:
            self.change_player_mode(mode)
            self.channel.broadcast_cc(
                self.prefix +
                "^Y%s^~ has changed the game to ^C%d-player^~ mode.\n" %
                (player, mode))

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix +
                           "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix +
                           "You must wait for your turn to resign.\n")
            return False

        if seat.data.resigned:
            player.tell_cc(self.prefix + "You've already resigned.\n")
            return False

        # They've passed the tests and can resign.
        seat.data.resigned = True
        self.channel.broadcast_cc(self.prefix +
                                  "^R%s^~ is resigning from the game.\n" %
                                  player)

    def tick(self):

        # If all seats are full and the game is active, autostart.
        active_seats = [x for x in self.seats if x.player]
        if (self.state.get() == "need_players"
                and len(active_seats) == self.player_mode and self.active):
            self.state.set("playing")

            send_str = "^RRed^~: %s; ^BBlue^~: %s" % (
                self.seats[0].player_name, self.seats[1].player_name)
            if self.player_mode == 4:
                send_str += "; ^GGreen^~: %s; ^YYellow^~: %s" % (
                    self.seats[2].player_name, self.seats[3].player_name)
            self.channel.broadcast_cc(self.prefix + send_str + "\n")
            self.turn = RED
            self.send_board()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in (
                        "size",
                        "sz",
                ):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid size command.\n")
                    handled = True

                elif primary in (
                        "players",
                        "player",
                        "pl",
                ):
                    if len(command_bits) == 2:
                        self.set_player_mode(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix +
                                       "Invalid player mode command.\n")
                    handled = True

                elif primary in (
                        "pit",
                        "hole",
                ):
                    loc_list = demangle_move(command_bits[1:])
                    if loc_list:
                        self.toggle_pits(player, loc_list)
                    else:
                        player.tell_cc(self.prefix + "Invalid pit command.\n")
                    handled = True

                elif primary in (
                        "ready",
                        "done",
                        "r",
                        "d",
                ):
                    self.channel.broadcast_cc(
                        self.prefix + "The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):

                    self.state.set("setup")
                    self.channel.broadcast_cc(
                        self.prefix +
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in (
                        "move",
                        "play",
                        "mv",
                        "pl",
                ):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 2:
                        made_move = self.move(player, move_bits[0],
                                              move_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid move command.\n")
                    handled = True

                elif primary in ("resign", ):

                    self.resign(player)
                    made_move = True
                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()

                    else:
                        # Okay, well, let's see whose turn it is.  If it comes
                        # back around to us, the game is over anyway.
                        curr_turn = self.turn
                        done = False
                        while not done:
                            if self.turn == RED:
                                self.turn = BLUE
                            elif self.turn == BLUE:

                                # The only tough one; switch depending on mode.
                                if self.player_mode == 2:
                                    self.turn = RED
                                else:
                                    self.turn = GREEN
                            elif self.turn == GREEN:
                                self.turn = YELLOW
                            elif self.turn == YELLOW:
                                self.turn = RED

                            # Now see if this player even has a move.
                            if self.color_has_move(self.turn):
                                done = True
                            elif self.turn == curr_turn:

                                # If we've wrapped back around to the current
                                # turn, no one had a move.  Bail as well.
                                done = True

                        # Check to see if we're back at the mover.
                        if curr_turn == self.turn:

                            # No one had a valid move.  Game's over.
                            self.no_move_resolve()
                            self.finish()

                        else:

                            # Otherwise it's some other player's turn; game on.
                            self.send_board()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def find_winner(self):

        # Get the list of players that haven't resigned and have at least one
        # piece left on the board.  If that list is only one long, we have a
        # winner.  Otherwise, the game continues.
        live_players = [
            x for x in self.seats if ((not x.data.resigned) and x.data.count)
        ]
        if len(live_players) == 1:
            return live_players[0].player_name
        else:
            return None

    def resolve(self, winner):
        self.send_board()
        self.channel.broadcast_cc(self.prefix + "^C%s^~ wins!\n" % winner)

    def no_move_resolve(self):

        self.send_board()

        # We look at the number of pieces each player has.  Highest wins.
        high_count = -1
        high_list = None
        for seat in self.seats:
            if seat.data.count > high_count:
                high_count = seat.data.count
                high_list = ["^C%s^~" % seat.player_name]
            elif seat.data.count == high_count:

                # Potential tie.
                high_list.append("^C%s^~" % seat.player_name)

        # If a single player has the highest count, they win; otherwise, tie.
        if len(high_list) == 1:
            self.channel.broadcast_cc(self.prefix +
                                      "%s wins with ^Y%d^~ pieces!\n" %
                                      (high_list[0], high_count))
        else:
            self.channel.broadcast_cc(
                self.prefix +
                "These players ^Rtied^~ for first with ^Y%d^~ pieces: %s\n" %
                (", ".join(high_list)))

    def show_help(self, player):

        super(Ataxx, self).show_help(player)
        player.tell_cc("\nATAXX SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc(
            "             ^!players^. 2|4,  ^!pl^.     Set number of players.\n"
        )
        player.tell_cc(
            "                     ^!pit^. <ln>     Add or remove pit at <ln>.\n"
        )
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nATAXX PLAY:\n\n")
        player.tell_cc(
            "          ^!move^. <ln> <ln2>, ^!mv^.     Move from <ln> to <ln2> (letter number).\n"
        )
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #45
0
ファイル: whist.py プロジェクト: Cloudxtreme/giles
class Whist(SeatedGame):
    """A Whist game table implementation.  Whist came about sometime in the
    18th century.  This implementation does not (currently) score honours,
    because honours are boring.
    """
    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()

    def show_help(self, player):

        super(Whist, self).show_help(player)
        player.tell_cc("\nWHIST SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n"
        )
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nWHIST PLAY:\n\n")
        player.tell_cc(
            "              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n"
        )
        player.tell_cc(
            "                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n"
        )

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_score_str(self):
        return "          ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (
            self.ns.score, self.ew.score)

    def get_color_code(self, seat):

        if seat == self.seats[0] or seat == self.seats[2]:
            return "^R"
        else:
            return "^M"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name,
                                    self.get_color_code(seat), seat)

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            to_return += "It is ^Y%s^~'s turn (%s%s^~).  Trumps are ^C%s^~.\n" % (
                self.turn.player_name, self.get_color_code(
                    self.turn), self.turn, self.trump_suit)
            to_return += "Tricks:   ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (
                self.ns.tricks, self.ew.tricks)
        to_return += "The goal score for this game is ^C%s^~.\n" % get_plural_str(
            self.goal, "point")
        to_return += self.get_score_str()

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" %
                    (player, get_plural_str(new_goal, "point")))

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deal(self):

        dealer_name = self.dealer.player_name

        self.bc_pre(
            "^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" %
            (dealer_name, self.get_color_code(self.dealer), self.dealer))
        deck = new_deck()
        deck.shuffle()

        # Deal out all of the cards.  We'll flip the last one; that determines
        # the trump suit for the hand.
        self.bc_pre("^R%s^~ deals the cards out to all the players.\n" %
                    dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for i in range(13):
            for seat in self.seats:
                seat.data.hand.add(deck.discard())

        # Flip the dealer's last card; it determines the trump suit.
        last_card = self.dealer.data.hand[-1]
        self.bc_pre("^R%s^~ flips their last card; it is ^C%s^~.\n" %
                    (dealer_name, card_to_str(last_card, LONG)))
        self.trump_suit = last_card.suit

        # Sort everyone's hands.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)

        # Show everyone their hands.
        self.show_hands()

        # Set the trick counts to zero.
        self.ns.tricks = 0
        self.ew.tricks = 0

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if (this_suit != self.led_suit
                    and hand_has_suit(seat.data.hand, self.led_suit)):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player,
                              "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" %
                    (self.get_sp_str(seat), action_str,
                     card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
                and self.seats[1].player and self.seats[2].player
                and self.seats[3].player and self.active):
            self.state.set("playing")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Make a new deal.
            self.dealer = self.seats[0]
            self.new_deal()

            # Eldest leads to the first trick.
            self.turn = self.next_seat(self.dealer)
            self.layout.change_turn(self.turn.data.who)

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in (
                        "goal",
                        "score",
                        "sc",
                        "g",
                ):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):
                    self.state.set("setup")
                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    handled = True

            elif state == "playing":

                card_played = False
                if primary in (
                        "hand",
                        "inventory",
                        "inv",
                        "i",
                ):
                    self.show_hand(player)
                    handled = True

                elif primary in (
                        "play",
                        "move",
                        "pl",
                        "mv",
                ):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == 4:

                        # Finish the trick up.
                        self.finish_trick()

                        # Is that the last trick of this hand?
                        if self.ns.tricks + self.ew.tricks == 13:

                            # Yup.  Finish the hand up.
                            self.finish_hand()

                            # Did someone win the overall game?
                            winner = self.find_winner()
                            if winner:

                                # Yup.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # Nope.  Pass the deal to the next dealer...
                                self.dealer = self.next_seat(self.dealer)

                                # Deal and set up the first player.
                                self.new_deal()
                                self.turn = self.next_seat(self.dealer)
                                self.layout.change_turn(self.turn.data.who)

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(
                self.log_prefix +
                "Something went horribly awry; trick ended without a finish.")
            self.bc_pre(
                "Something went horribly wrong; no one won the trick!  Tell the admin.\n"
            )
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" %
                    (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # Give the trick to the correct partnership.
        if winning_seat == self.seats[0] or winning_seat == self.seats[2]:
            self.ns.tricks += 1
        else:
            self.ew.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def finish_hand(self):

        # Which side won more than 6 tricks?
        if self.ns.tricks > 6:
            winning_side = "^RNorth/South^~"
            addend = self.ns.tricks - 6
            self.ns.score += addend
        else:
            winning_side = "^MEast/West^~"
            addend = self.ew.tricks - 6
            self.ew.score += addend

        # Let everyone know.
        self.bc_pre("%s wins the hand and gains ^C%s^~.\n" %
                    (winning_side, get_plural_str(addend, "point")))
        self.bc_pre(self.get_score_str())

    def find_winner(self):

        # Easy: has one of the sides reached a winning score?
        if self.ns.score >= self.goal:
            return self.ns
        elif self.ew.score >= self.goal:
            return self.ew

        return None

    def resolve(self, winning_partnership):

        if self.ns == winning_partnership:
            name_one = self.seats[0].player_name
            name_two = self.seats[2].player_name
        else:
            name_one = self.seats[1].player_name
            name_two = self.seats[3].player_name
        self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))
コード例 #46
0
ファイル: metamorphosis.py プロジェクト: sunfall/giles
class Metamorphosis(SeatedGame):
    """A Metamorphosis game table implementation.  Invented in 2009 by Gregory
    Keith Van Patten.  Play seems to show that ko fight mode is definitely
    superior to the alternative, so we set it as default.
    """

    def __init__(self, server, table_name):

        super(Metamorphosis, self).__init__(server, table_name)

        self.game_display_name = "Metamorphosis"
        self.game_name = "metamorphosis"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RMetamorphosis^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Metamorphosis-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 12
        self.ko_fight = True
        self.group_count = None
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[0].data.last_was_ko = False
        self.seats[1].data.side = WHITE
        self.seats[1].data.last_was_ko = False
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()

    def init_board(self):

        self.board = []

        # Generate a new empty board.  Boards alternate the starting checkered
        # layout, which we can pregen.
        white_first_row = []
        black_first_row = []
        for c in range(self.size):
            if c % 2:
                white_first_row.append(BLACK)
                black_first_row.append(WHITE)
            else:
                white_first_row.append(WHITE)
                black_first_row.append(BLACK)

        # Then we just add the appropriate rows, two at a time.
        for r in range(self.size / 2):
            self.board.append(white_first_row[:])
            self.board.append(black_first_row[:])

        # Count the number of groups on the board.  Should be size^2.
        self.group_count = self.get_group_count()

    def update_printable_board(self):

        self.printable_board = []
        col_str = "    " + "".join([" " + COLS[i] for i in range(self.size)])
        self.printable_board.append(col_str + "\n")
        self.printable_board.append("   ^m.=" + "".join(["=="] * self.size) + ".^~\n")
        for r in range(self.size):
            this_str = "%2d ^m|^~ " % (r + 1)
            for c in range(self.size):
                if r == self.last_r and c == self.last_c:
                    this_str += "^5"
                loc = self.board[r][c]
                if loc == WHITE:
                    this_str += "^Wo^~ "
                elif loc == BLACK:
                    this_str += "^Kx^~ "
                else:
                    this_str += "^M.^~ "
            this_str += "^m|^~ %d" % (r + 1)
            self.printable_board.append(this_str + "\n")
        self.printable_board.append("   ^m`=" + "".join(["=="] * self.size) + "'^~\n")
        self.printable_board.append(col_str + "\n")

    def show(self, player):

        if not self.printable_board:
            self.update_printable_board()
        for line in self.printable_board:
            player.tell_cc(line)
        player.tell_cc(self.get_turn_str() + "\n")

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def get_turn_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        if self.turn == BLACK:
            player = self.seats[0].player_name
            color_msg = "^KBlack/Vertical^~"
        else:
            player = self.seats[1].player_name
            color_msg = "^WWhite/Horizontal^~"

        return ("It is ^Y%s^~'s turn (%s)." % (player, color_msg))

    def is_valid(self, row, col):

        if row < 0 or row >= self.size or col < 0 or col >= self.size:
            return False
        return True

    def get_group_count(self):

        # Counting groups can be done in a single pass, even if the groups
        # are complicated.  It is done as follows:
        # - Check the color in a location and the locations above and to the
        #   left.
        # - If it's the same color as only one of them, assign ourselves the
        #   group ID of that piece;
        # - If it's the same color as /both/, and they have distinct group IDs,
        #   reassign the higher-numbered group ID to the lower group ID, as this
        #   piece joins those two groups;
        # - If it's the same color as neither, add a new group ID.
        # At the end, we simply count the number of distinct group IDs.

        # Build a temporary board tracking the IDs of each spot.
        id_board = []
        for r in range(self.size):
            id_board.append([None] * self.size)

        id_dict = {}
        curr_id = 0
        for r in range(self.size):
            for c in range(self.size):
                self_color = self.board[r][c]
                above_color = None
                left_color = None
                if r - 1 >= 0:
                    above_color = self.board[r - 1][c]
                if c - 1 >= 0:
                    left_color = self.board[r][c - 1]

                if self_color == above_color:

                    # Definitely a group above.  Check to see if it's the same
                    # to the left too.
                    group_id = id_dict[id_board[r - 1][c]]
                    if self_color == left_color:

                        # Same color.  Are they the same group ID?
                        left_id = id_dict[id_board[r][c - 1]]
                        if left_id == group_id:

                            # Yup.  Use it.
                            id_board[r][c] = group_id

                        else:

                            # This piece joins two previously-distinct groups.
                            # Update the ID dict to collapse those two groups
                            # into one and use the lower-valued one for this
                            # space.
                            if left_id < group_id:
                                id_dict[group_id] = left_id
                                id_board[r][c] = left_id
                            else:
                                id_dict[left_id] = group_id
                                id_board[r][c] = group_id

                    else:

                        # Different color left, same color above.  Use the
                        # above ID.
                        id_board[r][c] = group_id

                elif self_color == left_color:

                    # Group to the left, but not above.  Use the left ID.
                    id_board[r][c] = id_dict[id_board[r][c - 1]]

                else:

                    # No group in either direction.  New group.
                    id_board[r][c] = curr_id
                    id_dict[curr_id] = curr_id
                    curr_id += 1

        # Now that we're done with those shenanigans, count the number of unique
        # groups on the board.  We may have to chase pointers; imagine a case
        # where group C coalesced into group B, and later on group B coalesced
        # into group A.  In that case, C will still refer to group B, which no
        # longer exists.  At the end of any coalesce chain, though, a group must
        # point to itself.
        unique_set = set()
        for group_id in id_dict:
            chase_id = id_dict[group_id]
            while chase_id != id_dict[chase_id]:
                chase_id = id_dict[chase_id]
            if id_dict[chase_id] not in unique_set:
                unique_set.add(id_dict[chase_id])

        return len(unique_set)

    def flip(self, row, col):

        curr = self.board[row][col]
        if curr == BLACK:
            self.board[row][col] = WHITE
        else:
            self.board[row][col] = BLACK

    def move(self, player, play):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't move; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to move.\n")
            return False

        col, row = play

        # Make sure they're all in range.
        if not self.is_valid(row, col):
            player.tell_cc(self.prefix + "Your move is out of bounds.\n")
            return False

        # Does this move increase the number of groups on the board?
        self.flip(row, col)
        new_group_count = self.get_group_count()
        if new_group_count > self.group_count:

            # Yup.  Flip it back and inform the player.
            self.flip(row, col)
            player.tell_cc(self.prefix + "That move increases the group count.\n")
            return False

        move_is_ko = False
        ko_str = ""
        # If we're in ko fight mode, check to see if a ko move is valid.
        if new_group_count == self.group_count:
            move_is_ko = True
            ko_str = ", a ko move"

            if not self.ko_fight:

                # Flip it back; we're not in ko fight mode.
                self.flip(row, col)
                player.tell_cc(self.prefix + "That is a ko move and does not decrease the group count.\n")
                return False

            elif seat.data.last_was_ko:

                # Flip it back; two kos in a row is not allowed.
                self.flip(row, col)
                player.tell_cc(self.prefix + "That is a ko move and you made a ko move last turn.\n")
                return False

            elif row == self.last_r and col == self.last_c:

                # Flip it back; this is the same move their opponent just made.
                self.flip(row, col)
                player.tell_cc(self.prefix + "You cannot repeat your opponent's last move.\n")
                return False

        # This is a valid move.  Apply, announce.
        play_str = "%s%s" % (COLS[col], row + 1)
        self.channel.broadcast_cc(self.prefix + "^Y%s^~ flips the piece at ^C%s^~%s.\n" % (seat.player, play_str, ko_str))
        self.last_r = row
        self.last_c = col
        self.turn_number += 1
        self.group_count = new_group_count

        # If it was a ko move, mark the player as having made one, so they
        # can't make another the next turn.  Otherwise clear that bit.
        if move_is_ko:
            seat.data.last_was_ko = True
        else:
            seat.data.last_was_ko = False

        return True

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
           and self.seats[1].player and self.active):
            self.state.set("playing")
            self.channel.broadcast_cc(self.prefix + "^KBlack/Vertical^~: ^R%s^~; ^WWhite/Horizontal^~: ^Y%s^~\n" %
               (self.seats[0].player, self.seats[1].player))
            self.turn = BLACK
            self.turn_number = 1
            self.send_board()

    def set_size(self, player, size_bits):

        if not size_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        size = int(size_bits)

        if size < MIN_SIZE or size > MAX_SIZE:
            player.tell_cc(self.prefix + "Size must be between %d and %d inclusive.\n" % (MIN_SIZE, MAX_SIZE))
            return

        if size % 2:
            player.tell_cc(self.prefix + "Size must be even.\n")
            return

        # Valid!
        self.size = size
        self.channel.broadcast_cc(self.prefix + "^R%s^~ has set the board size to ^C%d^~.\n" % (player, size))
        self.init_board()
        self.update_printable_board()

    def set_ko_fight(self, player, ko_str):

        ko_bool = booleanize(ko_str)
        if ko_bool:
            if ko_bool > 0:
                self.ko_fight = True
                display_str = "^Con^~"
            else:
                self.ko_fight = False
                display_str = "^coff^~"
            self.channel.broadcast_cc(self.prefix + "^R%s^~ has turned ^Gko fight^~ mode %s.\n" % (player, display_str))

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat.data.side
        self.channel.broadcast_cc(self.prefix + "^R%s^~ is resigning from the game.\n" % player)
        return True

    def swap(self, player):

        # Like Hex, a swap in Metamorphosis requires a translation to make it the
        # equivalent move for the other player.

        self.flip(self.last_r, self.last_c)
        self.flip(self.last_c, self.last_r)
        self.last_c, self.last_r = self.last_r, self.last_c

        self.channel.broadcast_cc("^Y%s^~ has swapped ^KBlack^~'s first move.\n" % (player))
        self.turn_number += 1

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz",):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid size command.\n")
                    handled = True

                elif primary in ("ko",):

                    if len(command_bits) == 2:
                        self.set_ko_fight(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid ko command.\n")
                    handled = True

                if primary in ("done", "ready", "d", "r",):

                    self.channel.broadcast_cc(self.prefix + "The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):

                    self.state.set("setup")
                    self.channel.broadcast_cc(self.prefix + "^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("move", "play", "mv", "pl",):

                    invalid = False
                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        invalid = True

                    if invalid:
                        player.tell_cc(self.prefix + "Invalid move command.\n")
                    handled = True

                elif primary in ("swap",):

                    if self.seats[1].player == player and self.turn_number == 2:
                        self.swap(player)
                        made_move = True
                    else:
                        player.tell_cc(self.prefix + "Invalid swap command.\n")
                    handled = True

                elif primary in ("resign",):

                    if self.resign(player):
                        made_move = True

                    handled = True

                if made_move:

                    # Okay, something happened on the board.  Update.
                    self.update_printable_board()

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()
                    else:

                        # Nope.  Switch turns...
                        if self.turn == BLACK:
                            self.turn = WHITE
                        else:
                            self.turn = BLACK

                        # ...show everyone the board, and keep on.
                        self.send_board()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.
        if self.resigner == WHITE:
            return self.seats[0].player_name
        elif self.resigner == BLACK:
            return self.seats[1].player_name

        # This is like most connection games; we check recursively from the
        # top and left edges to see whether a player has won.
        self.found_winner = False
        self.adjacency_map = []
        for i in range(self.size):
            self.adjacency_map.append([None] * self.size)

        for i in range(self.size):
            if self.board[i][0] == WHITE:
                self.recurse_adjacency(WHITE, i, 0)
            if self.board[0][i] == BLACK:
                self.recurse_adjacency(BLACK, 0, i)

        # ...except that it has to be at the end of the OTHER player's turn!
        if self.found_winner == BLACK and self.turn == WHITE:
            return self.seats[0].player_name
        elif self.found_winner == WHITE and self.turn == BLACK:
            return self.seats[1].player_name

        # No winner yet.
        return None

    def recurse_adjacency(self, color, row, col):

        # Bail if we found a winner already.
        if self.found_winner:
            return

        # Bail if we're off the board.
        if not self.is_valid(row, col):
            return

        # Bail if we've been here.
        if self.adjacency_map[row][col]:
            return

        # Bail if this is the wrong color.
        if self.board[row][col] != color:
            return

        # Okay.  Occupied and it's this player's.  Mark.
        self.adjacency_map[row][col] = True

        # Have we hit the winning side for this player?
        if ((color == WHITE and col == self.size - 1) or
           (color == BLACK and row == self.size - 1)):

            # Success!
            self.found_winner = color
            return

        # Not a win yet.  Recurse over adjacencies.
        for r_delta, c_delta in CONNECTION_DELTAS:
            self.recurse_adjacency(color, row + r_delta, col + c_delta)

    def resolve(self, winner):
        self.send_board()
        self.channel.broadcast_cc(self.prefix + "^C%s^~ wins!\n" % winner)

    def show_help(self, player):

        super(Metamorphosis, self).show_help(player)
        player.tell_cc("\nMETAMORPHOSIS SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("                    ^!ko^. on|off     Enable/disable ko fight mode.\n")
        player.tell_cc("             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nMETAMORPHOSIS PLAY:\n\n")
        player.tell_cc("      ^!move^. <ln>, ^!play^., ^!mv^., ^!pl^.     Make move <ln> (letter number).\n")
        player.tell_cc("                         ^!swap^.     Swap the first move (only White, only their first).\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #47
0
ファイル: ataxx.py プロジェクト: Cloudxtreme/giles
class Ataxx(SeatedGame):
    """An Ataxx game table implementation.  Invented in 1988 by Dave Crummack
    and Craig Galley.
    """

    def __init__(self, server, table_name):

        super(Ataxx, self).__init__(server, table_name)

        self.game_display_name = "Ataxx"
        self.game_name = "ataxx"
        self.seats = [
            Seat("Red"),
            Seat("Blue"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RAtaxx^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Ataxx-specific stuff.
        self.board = None
        self.printable_board = None
        self.sides = {}
        self.size = 7
        self.player_mode = 2
        self.turn = None
        self.last_r = None
        self.last_c = None

        self.init_seats()
        self.init_board()

    def init_board(self):

        self.board = []
        for r in range(self.size):
            self.board.append([None] * self.size)

        # Place starting pieces, depending on the number of players.
        bottom_left = BLUE
        bottom_right = RED
        if self.player_mode == 4:
            bottom_left = YELLOW
            bottom_right = GREEN

        self.board[0][0] = RED
        self.board[0][self.size - 1] = BLUE
        self.board[self.size - 1][0] = bottom_left
        self.board[self.size - 1][self.size - 1] = bottom_right

        self.update_printable_board()

    def init_seats(self):

        # If we're in 2-player mode, and there are 4 seats, delete the
        # extras.
        if self.player_mode == 2 and len(self.seats) == 4:
            del self.seats[2]
            del self.seats[2]

        self.sides = {}
        # Set the sides and data for players one and two.
        self.seats[0].data.side = RED
        self.seats[0].data.count = 2
        self.seats[0].data.resigned = False
        self.seats[1].data.side = BLUE
        self.seats[1].data.count = 2
        self.seats[1].data.resigned = False
        self.sides[RED] = self.seats[0]
        self.sides[BLUE] = self.seats[1]

        # If there are four players...
        if self.player_mode == 4:

            # ...and only two seats, create them.
            if len(self.seats) == 2:
                self.seats.append(Seat("Green"))
                self.seats.append(Seat("Yellow"))

            # Either way, set the sides and data.
            self.seats[2].data.side = GREEN
            self.seats[2].data.resigned = False
            self.sides[GREEN] = self.seats[2]
            self.seats[3].data.side = YELLOW
            self.seats[3].data.resigned = False
            self.sides[YELLOW] = self.seats[3]

            self.seats[0].data.count = 1
            self.seats[1].data.count = 1
            self.seats[2].data.count = 1
            self.seats[3].data.count = 1

    def change_player_mode(self, count):

        # Don't bother if it's the mode we're already in.
        if count == self.player_mode:
            return False

        # Don't bother if it's not a valid option either.
        if count != 2 and count != 4:
            return False

        # Okay.  Set values...
        self.player_mode = count
        self.min_players = count
        self.max_players = count

        # ...initialize the seats...
        self.init_seats()

        # ...and reinitialize the board.
        self.init_board()

    def update_printable_board(self):

        self.printable_board = []
        col_str = "    " + "".join([" " + COLS[i] for i in range(self.size)])
        self.printable_board.append(col_str + "\n")
        self.printable_board.append("   ^m.=" + "".join(["=="] * self.size) + ".^~\n")
        for r in range(self.size):
            this_str = "%2d ^m|^~ " % (r + 1)
            for c in range(self.size):
                if r == self.last_r and c == self.last_c:
                    this_str += "^I"
                loc = self.board[r][c]
                if loc == RED:
                    this_str += "^RR^~ "
                elif loc == BLUE:
                    this_str += "^BB^~ "
                elif loc == GREEN:
                    this_str += "^GG^~ "
                elif loc == YELLOW:
                    this_str += "^YY^~ "
                elif loc == PIT:
                    this_str += "^Ko^~ "
                else:
                    this_str += "^M.^~ "
            this_str += "^m|^~ %d" % (r + 1)
            self.printable_board.append(this_str + "\n")
        self.printable_board.append("   ^m`=" + "".join(["=="] * self.size) + "'^~\n")
        self.printable_board.append(col_str + "\n")

    def get_info_str(self):

        if not self.turn:
            return("The game has not yet started.\n")

        if self.turn == RED:
            name = self.seats[0].player_name
            turn_str = "^RRed^~"
        elif self.turn == BLUE:
            name = self.seats[1].player_name
            turn_str = "^BBlue^~"
        elif self.turn == GREEN:
            name = self.seats[2].player_name
            turn_str = "^GGreen^~"
        else:
            name = self.seats[3].player_name
            turn_str = "^YYellow^~"

        info_str = "It is %s's turn (%s).\n" % (name, turn_str)
        info_str += "^RRed^~: %d  ^BBlue^~: %d" % (self.seats[0].data.count, self.seats[1].data.count)
        if self.player_mode == 4:
            info_str += "  ^GGreen^~: %d  ^YYellow^~: %d" % (self.seats[2].data.count, self.seats[3].data.count)
        info_str += "\n"
        return(info_str)

    def show(self, player):

        if not self.printable_board:
            self.update_printable_board()
        for line in self.printable_board:
            player.tell_cc(line)
        player.tell_cc(self.get_info_str())

    def send_board(self):

        for listener in self.channel.listeners:
            self.show(listener)

    def is_valid(self, row, col):

        # Note that this does /not/ care about pits, just about the proper
        # ranges for coordinates.
        if row < 0 or row >= self.size or col < 0 or col >= self.size:
            return False
        return True

    def piece_has_move(self, row, col):

        # Returns whether or not a given piece has a potential move.

        # Bail on dud data.
        if not self.is_valid(row, col) or not self.board[row][col]:
            return False

        # Okay.  A piece can potentially move anywhere in a 5x5 area centered
        # on its location.
        found_move = False
        for r_d in range(-2, 3): # <--- why I hate range syntax.
            for c_d in range(-2, 3):
                if not found_move and (self.is_valid(row + r_d, col + c_d) and
                   not self.board[row + r_d][col + c_d]):
                    found_move = True

        # Return whether we found a move or not.
        return found_move

    def color_has_move(self, color):

        # Returns whether or not a given side has a potential move.

        # Bail immediately if green or yellow and we're in 2p mode.
        if self.player_mode == 2 and (color == YELLOW or color == GREEN):
            return False

        # Bail if this player has resigned.
        if ((color == RED and self.seats[0].data.resigned) or
           (color == BLUE and self.seats[1].data.resigned) or
           (color == GREEN and self.seats[2].data.resigned) or
           (color == YELLOW and self.seats[3].data.resigned)):
            return False

        # Okay.  Scan the board for pieces...
        for r in range(self.size):
            for c in range(self.size):
                if self.board[r][c] == color and self.piece_has_move(r, c):
                    return True

        # Found no moves.  This color has no valid moves.
        return False

    def loc_to_str(self, row, col):
        return "%s%s" % (COLS[col], row + 1)

    def move(self, player, src_loc, dst_loc):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't move; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to move.\n")
            return False

        if src_loc == dst_loc:
            player.tell_cc(self.prefix + "You can't make a non-move move!\n")
            return False

        src_c, src_r = src_loc
        dst_c, dst_r = dst_loc
        if not self.is_valid(src_c, src_r) or not self.is_valid(dst_c, dst_r):
            player.tell_cc(self.prefix + "Your move is out of bounds.\n")
            return False

        src_str = self.loc_to_str(src_r, src_c)
        dst_str = self.loc_to_str(dst_r, dst_c)

        # Do they have a piece at the source?
        color = seat.data.side
        if self.board[src_r][src_c] != color:
            player.tell_cc(self.prefix + "You don't have a piece at ^C%s^~.\n" % src_str)
            return False

        # Is the destination within range?
        if abs(src_r - dst_r) > 2 or abs(src_c - dst_c) > 2:
            player.tell_cc(self.prefix + "That move is too far.\n")
            return False

        # Is the destination empty?
        if self.board[dst_r][dst_c]:
            player.tell_cc(self.prefix + "^C%s^~ is already occupied.\n" % dst_str)
            return False

        # In range, to an empty cell.  It's a valid move.  Mark it.
        self.last_r = dst_r
        self.last_c = dst_c

        # Now, is it a split or a leap?
        if abs(src_r - dst_r) < 2 and abs(src_c - dst_c) < 2:

            # Split.  Add a new piece, increase the count.
            action_str = "^Mgrew^~ into"
            self.board[dst_r][dst_c] = color
            seat.data.count += 1
        else:

            # Leap.  Move the piece, don't increase the count.
            action_str = "^Cjumped^~ to"
            self.board[src_r][src_c] = None
            self.board[dst_r][dst_c] = color

        # Whichever action occurred, check all cells surrounding the
        # destination.  If they are opponents, transform them.
        change_count = 0
        change_str = ""
        for r_d in range(-1, 2):
            for c_d in range(-1, 2):
                if self.is_valid(dst_r + r_d, dst_c + c_d):
                    occupier = self.board[dst_r + r_d][dst_c + c_d]
                    if occupier and occupier != color and occupier != PIT:

                        # Another player.  Uh oh!  Flip it and decrement that
                        # player's count.
                        self.board[dst_r + r_d][dst_c + c_d] = color
                        seat.data.count += 1
                        self.sides[occupier].data.count -= 1
                        change_count += 1

        if change_count:
            change_str = ", ^!converting %d piece" % change_count
            if change_count != 1:
                change_str += "s"

        # Tell everyone what just happened.
        self.channel.broadcast_cc(self.prefix + "From ^c%s^~, %s %s ^C%s^~%s^~.\n" % (src_str, player, action_str, dst_str, change_str))

        self.update_printable_board()
        return True

    def toggle_pits(self, player, loc_list):

        # Undocumented bonus feature: handles multiple locations, but if
        # any of them are invalid, it'll bail halfway through.  Useful for
        # prepping a particular cool layout with a single cut-and-pasted
        # string, though.
        for loc in loc_list:

            col, row = loc

            # Bail if out of bounds.
            if not self.is_valid(row, col):
                player.tell_cc(self.prefix + "Pit out of bounds.\n")
                return

            # Bail if a starting piece is there.
            thing_there = self.board[row][col]
            if thing_there and not (thing_there == PIT):
                player.tell_cc(self.prefix + "Cannot put a pit on a starting piece.\n")
                return

            # Since it's a toggle, figure out what we're toggling to.
            if thing_there:
                new_thing = None
                action_str = "^cremoved^~"
            else:
                new_thing = PIT
                action_str = "^Cadded^~"

            # Tentative place the thing.
            self.board[row][col] = new_thing

            # Does it keep red or blue (which, in a 4p game, is equivalent to
            # all four players) from being able to make a move?  If so, it's
            # invalid.  Put the board back the way it was.
            if not self.color_has_move(RED) or not self.color_has_move(BLUE):
                player.tell_cc(self.prefix + "Players must have a valid move.\n")
                self.board[row][col] = thing_there
                return

            loc_list = [(row, col)]

            edge = self.size - 1

            # In either mode, we place another pit across the center line,
            # but not if that's the same location as the one we just placed
            # (on the center line on odd-sized boards).
            if (edge - row) != row:
                self.board[edge - row][col] = new_thing
                loc_list.append((edge - row, col))

                # Handle the 4p down-reflection if necessary.
                if self.player_mode == 4 and (edge - col) != col:
                    self.board[edge - row][edge - col] = new_thing
                    loc_list.append((edge - row, edge - col))

            # Handle the 4p right-reflection if necessary.
            if self.player_mode == 4 and (edge - col) != col:
                self.board[row][edge - col] = new_thing
                loc_list.append((row, edge - col))

            # Generate the list of locations.
            loc_str = ", ".join(["^C%s^~" % self.loc_to_str(x[0], x[1]) for x in loc_list])

            # Finally, send the string detailing what just happened.
            self.channel.broadcast_cc(self.prefix + "^Y%s^~ has %s a pit at: %s\n" % (player, action_str, loc_str))
            self.update_printable_board()

    def set_size(self, player, size_bits):

        if not size_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        size = int(size_bits)

        if size < MIN_SIZE or size > MAX_SIZE:
            player.tell_cc(self.prefix + "Size must be between %d and %d inclusive.\n" % (MIN_SIZE, MAX_SIZE))
            return

        # Valid!
        self.size = size
        self.channel.broadcast_cc(self.prefix + "^R%s^~ has set the board size to ^C%d^~.\n" % (player, size))
        self.init_board()
        self.update_printable_board()

    def set_player_mode(self, player, mode_bits):

        if not mode_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid player mode command.\n")
            return

        mode = int(mode_bits)

        if mode != 2 and mode != 4:
            player.tell_cc(self.prefix + "This game only supports two or four players.\n")
            return

        elif mode == self.player_mode:
            player.tell_cc(self.prefix + "This table is already in that mode.\n")
            return

        else:
            self.change_player_mode(mode)
            self.channel.broadcast_cc(self.prefix + "^Y%s^~ has changed the game to ^C%d-player^~ mode.\n" % (player, mode))

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix + "You must wait for your turn to resign.\n")
            return False

        if seat.data.resigned:
            player.tell_cc(self.prefix + "You've already resigned.\n")
            return False

        # They've passed the tests and can resign.
        seat.data.resigned = True
        self.channel.broadcast_cc(self.prefix + "^R%s^~ is resigning from the game.\n" % player)

    def tick(self):

        # If all seats are full and the game is active, autostart.
        active_seats = [x for x in self.seats if x.player]
        if (self.state.get() == "need_players" and
           len(active_seats) == self.player_mode and self.active):
            self.state.set("playing")

            send_str = "^RRed^~: %s; ^BBlue^~: %s" % (self.seats[0].player_name, self.seats[1].player_name)
            if self.player_mode == 4:
                send_str += "; ^GGreen^~: %s; ^YYellow^~: %s" % (self.seats[2].player_name, self.seats[3].player_name)
            self.channel.broadcast_cc(self.prefix + send_str + "\n")
            self.turn = RED
            self.send_board()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in ("size", "sz",):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid size command.\n")
                    handled = True

                elif primary in ("players", "player", "pl",):
                    if len(command_bits) == 2:
                        self.set_player_mode(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid player mode command.\n")
                    handled = True

                elif primary in ("pit", "hole",):
                    loc_list = demangle_move(command_bits[1:])
                    if loc_list:
                        self.toggle_pits(player, loc_list)
                    else:
                        player.tell_cc(self.prefix + "Invalid pit command.\n")
                    handled = True

                elif primary in ("ready", "done", "r", "d",):
                    self.channel.broadcast_cc(self.prefix + "The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):

                    self.state.set("setup")
                    self.channel.broadcast_cc(self.prefix + "^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("move", "play", "mv", "pl",):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 2:
                        made_move = self.move(player, move_bits[0], move_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid move command.\n")
                    handled = True

                elif primary in ("resign",):

                    self.resign(player)
                    made_move = True
                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()

                    else:
                        # Okay, well, let's see whose turn it is.  If it comes
                        # back around to us, the game is over anyway.
                        curr_turn = self.turn
                        done = False
                        while not done:
                            if self.turn == RED:
                                self.turn = BLUE
                            elif self.turn == BLUE:

                                # The only tough one; switch depending on mode.
                                if self.player_mode == 2:
                                    self.turn = RED
                                else:
                                    self.turn = GREEN
                            elif self.turn == GREEN:
                                self.turn = YELLOW
                            elif self.turn == YELLOW:
                                self.turn = RED

                            # Now see if this player even has a move.
                            if self.color_has_move(self.turn):
                                done = True
                            elif self.turn == curr_turn:

                                # If we've wrapped back around to the current
                                # turn, no one had a move.  Bail as well.
                                done = True

                        # Check to see if we're back at the mover.
                        if curr_turn == self.turn:

                            # No one had a valid move.  Game's over.
                            self.no_move_resolve()
                            self.finish()

                        else:

                            # Otherwise it's some other player's turn; game on.
                            self.send_board()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def find_winner(self):

        # Get the list of players that haven't resigned and have at least one
        # piece left on the board.  If that list is only one long, we have a
        # winner.  Otherwise, the game continues.
        live_players = [x for x in self.seats if ((not x.data.resigned) and
           x.data.count)]
        if len(live_players) == 1:
            return live_players[0].player_name
        else:
            return None

    def resolve(self, winner):
        self.send_board()
        self.channel.broadcast_cc(self.prefix + "^C%s^~ wins!\n" % winner)

    def no_move_resolve(self):

        self.send_board()

        # We look at the number of pieces each player has.  Highest wins.
        high_count = -1
        high_list = None
        for seat in self.seats:
            if seat.data.count > high_count:
                high_count = seat.data.count
                high_list = ["^C%s^~" % seat.player_name]
            elif seat.data.count == high_count:

                # Potential tie.
                high_list.append("^C%s^~" % seat.player_name)

        # If a single player has the highest count, they win; otherwise, tie.
        if len(high_list) == 1:
            self.channel.broadcast_cc(self.prefix + "%s wins with ^Y%d^~ pieces!\n" % (high_list[0], high_count))
        else:
            self.channel.broadcast_cc(self.prefix + "These players ^Rtied^~ for first with ^Y%d^~ pieces: %s\n" % (", ".join(high_list)))

    def show_help(self, player):

        super(Ataxx, self).show_help(player)
        player.tell_cc("\nATAXX SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc("             ^!players^. 2|4,  ^!pl^.     Set number of players.\n")
        player.tell_cc("                     ^!pit^. <ln>     Add or remove pit at <ln>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nATAXX PLAY:\n\n")
        player.tell_cc("          ^!move^. <ln> <ln2>, ^!mv^.     Move from <ln> to <ln2> (letter number).\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #48
0
class Set(SeatedGame):
    """A Set game table implementation.  Invented in 1974 by Marsha Jean Falco.
    """
    def __init__(self, server, table_name):

        super(Set, self).__init__(server, table_name)

        self.game_display_name = "Set"
        self.game_name = "set"
        self.seats = []
        self.min_players = 1
        self.max_players = 32767  # We don't even use this.
        self.state = State("need_players")
        self.prefix = "(^RSet^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Set-specific stuff.
        self.max_cards_on_table = DEFAULT_MAX_CARDS
        self.deal_delay = DEFAULT_DEAL_DELAY
        self.layout = None
        self.printable_layout = None
        self.deck = None
        self.last_play_time = None
        self.max_card_count = 81
        self.has_borders = True

    def build_deck(self):

        # Generate the deck...
        self.deck = []
        for count in (ONE, TWO, THREE):
            fill_list = (SMOOTH, )
            if self.has_borders:
                fill_list = (SMOOTH, WAVY, CHUNKY)
            for fill in fill_list:
                for color in (MAGENTA, RED, GREEN):
                    for shape in (BLOB, LOZENGE, SQUIGGLE):
                        self.deck.append((count, fill, color, shape))

        # ...and shuffle it.
        random.shuffle(self.deck)

        # Trim it to at most the max count.
        self.deck = self.deck[:self.max_card_count]

    def build_layout(self):

        # Put the first twelve cards on the table.
        self.layout = self.deck[:12]
        self.deck = self.deck[12:]

    def update_layout(self):

        # If the size of the layout is 12 or smaller, this is easy;
        # we just draw cards left (if any) to fill gaps in the board.
        # If it's larger than 12, it's easy too; we rebuild the
        # layout without any gaps (and then, juuust in case, add
        # blank cards if it somehow got smaller than 12.)

        layout_len = len(self.layout)
        if len(self.layout) <= 12:
            for i in range(layout_len):
                if not self.layout[i] and self.deck:
                    self.layout[i] = self.deck[0]
                    self.deck = self.deck[1:]

        else:
            new_layout = [x for x in self.layout if x]
            while len(new_layout) < 12:
                new_layout.append(None)
            self.layout = new_layout

    def get_card_art_bits(self, card, line_number):
        # .----. 1 /~~~~\ |=||=|
        # |2or3| 2 {    } =    =
        # |1or3| 3 {    } |    |
        # |2or3| 4 {    } =    =
        # `----' 5 \~~~~/ |=||=|

        # Just in case...
        if line_number < 1 or line_number > 5:
            return

        # At the end of the game, we will sometimes print blank
        # spaces where cards should go.  Handle that.
        if not card:
            return "      "
        count, fill, color, shape = card

        # If a line has a piece of art, it looks like this...
        art_bit = "%s%s^~" % (color.code, shape.art)
        # ...otherwise this.
        blank_bit = "    "

        if line_number == 1:
            return fill.edge_art[0]
        elif line_number == 2 or line_number == 4:
            center = blank_bit
            if count == TWO or count == THREE:
                center = art_bit
            return fill.edge_art[1] % center
        elif line_number == 3:
            center = blank_bit
            if count == ONE or count == THREE:
                center = art_bit
            return fill.edge_art[2] % center
        elif line_number == 5:
            return fill.edge_art[3]

        # Dunno how we got here...
        return "ERROR"

    def update_printable_layout(self):

        self.printable_layout = []
        if not self.layout:
            self.printable_layout.append(
                "The layout is currently ^cempty^~.\n")
            return

        # If the layout doesn't have a number of card spaces divisible
        # by 3, something is horribly wrong, and we should bail.
        if len(self.layout) % 3 != 0:
            self.printable_layout.append(
                "Something is ^Rhorribly wrong^~ with the layout.  Alert an admin.\n"
            )
            return

        # Okay, we have a usable layout.  Generate it!
        cards_per_row = len(self.layout) / 3
        self.printable_layout.append("=======" * cards_per_row + "=\n")
        for row in range(3):
            if row == 0:
                row_char = "A"
            elif row == 1:
                row_char = "B"
            else:
                row_char = "C"
            for card_line in range(1, 6):
                this_line = ""
                for col in range(cards_per_row):
                    this_line += (" %s" % self.get_card_art_bits(
                        self.layout[col * 3 + row], card_line))
                self.printable_layout.append(this_line + "\n")

            # Now we print the codes for each card under the cards.
            this_line = ""
            for col in range(1, cards_per_row + 1):
                this_line += ("   %s%s  " % (row_char, col))
            self.printable_layout.append(this_line + "\n\n")

    def show(self, player):

        if not self.printable_layout:
            self.update_printable_layout()
        for line in self.printable_layout:
            player.tell_cc(line)

    def send_layout(self):
        for listener in self.channel.listeners:
            self.show(listener)

    def join(self, player, join_bits):

        # We have to override join for two reasons: one, we allow players
        # to join during the game (!), and two, we have to create seats
        # for players on the fly, as we have no idea how many there might
        # end up being.

        state = self.state.get()
        if state == "need_players" or state == "playing":
            if len(join_bits) != 0:
                player.tell_cc(self.prefix +
                               "Cannot request a specific seat in Set.\n")
            elif self.get_seat_of_player(player):
                player.tell_cc(self.prefix + "You're already playing!\n")
            else:
                seat = Seat("%s" % str(len(self.seats) + 1))
                seat.data.score = 0
                self.seats.append(seat)
                seat.sit(player)
                player.tell_cc(self.prefix +
                               "You are now sitting in seat %s.\n" % seat)
                if not self.channel.is_connected(player):
                    self.channel.connect(player)
                self.channel.broadcast_cc(
                    self.prefix + "^Y%s^~ is now playing in seat ^C%s^~.\n" %
                    (player, seat))
                self.num_players += 1
        else:
            player.tell_cc(self.prefix + "Not looking for players.\n")

        return True

    def set_max_columns(self, player, column_str):

        if not column_str.isdigit():
            player.tell_cc(self.prefix + "Must provide numbers.\n")
            return

        column_val = int(column_str)
        if column_val < 7 or column_val > 9:
            player.tell_cc(self.prefix +
                           "Must provide a value between 7 and 9 inclusive.\n")
            return

        self.max_cards_on_table = column_val * 3
        self.channel.broadcast_cc(
            self.prefix + "^Y%s^~ set the maximum columns to ^C%s^~.\n" %
            (player, str(column_val)))

    def set_delay(self, player, delay_str):

        if not delay_str.isdigit():
            player.tell_cc(self.prefix + "Must provide numbers.\n")
            return

        delay_val = int(delay_str)
        if delay_val < 5 or delay_val > 300:
            player.tell_cc(
                self.prefix +
                "Must provide a value between 5 and 300 inclusive.\n")
            return

        self.deal_delay = delay_val
        self.channel.broadcast_cc(
            self.prefix + "^Y%s^~ set the deal delay to ^C%s^~ seconds.\n" %
            (player, str(delay_val)))

    def set_max_count(self, player, count_str):

        if not count_str.isdigit():
            player.tell_cc(self.prefix + "Must provide numbers.\n")
            return

        count_val = int(count_str)
        if count_val < 21 or count_val > 81 or count_val % 3 != 0:
            player.tell_cc(
                self.prefix +
                "Must provide a value between 21 and 81 inclusive, divisible by 3.\n"
            )
            return

        self.max_card_count = count_val
        self.channel.broadcast_cc(
            self.prefix +
            "^Y%s^~ set the maximum card count to ^C%s^~ cards.\n" %
            (player, str(count_val)))

    def set_border(self, player, border_str):

        border_bool = booleanize(border_str)
        if border_bool:
            if border_bool > 0:
                self.has_borders = True
                display_str = "^Con^~"
            else:
                self.has_borders = False
                display_str = "^coff^~"
            self.channel.broadcast_cc(self.prefix +
                                      "^R%s^~ has turned borders %s.\n" %
                                      (player, display_str))
        else:
            player.tell_cc(self.prefix + "Not a valid boolean!\n")

    def handle(self, player, command_str):

        # Handle common commands first.
        handled = self.handle_common_commands(player, command_str)

        if not handled:
            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if primary in ("score", "scores"):
                self.show_scores(player)
                handled = True

            elif state == "need_players":
                if primary in ("column", "columns"):
                    if len(command_bits) == 2:
                        self.set_max_columns(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix +
                                       "Invalid columns command.\n")
                    handled = True

                elif primary in ("delay", ):
                    if len(command_bits) == 2:
                        self.set_delay(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix +
                                       "Invalid delay command.\n")
                    handled = True

                elif primary in ("cards", "count"):
                    if len(command_bits) == 2:
                        self.set_max_count(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix +
                                       "Invalid cards command.\n")
                    handled = True

                elif primary in ("borders", "border"):
                    if len(command_bits) == 2:
                        self.set_border(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix +
                                       "Invalid border command.\n")
                    handled = True

                elif primary in ("start", ):
                    if not len(self.seats):
                        player.tell_cc(self.prefix +
                                       "Need at least one player!\n")
                    else:
                        self.state.set("playing")
                        self.channel.broadcast_cc(self.prefix + "Game on!\n")
                        self.build_deck()
                        self.build_layout()
                        self.update_printable_layout()
                        self.send_layout()
                        self.last_play_time = time.time()
                    handled = True

            elif state == "playing":

                # Everything at this point should be a move, which consists
                # of a list of 3 card choices.  As always, do the polite thing
                # for players who do the play/pl/move/mv/thing.
                if primary in ('play', 'move', 'pl', 'mv'):
                    play_bits = demangle_move(command_bits[1:])
                else:
                    play_bits = demangle_move(command_bits)

                if play_bits and len(play_bits) == 3:
                    self.declare(player, play_bits)
                    handled = True

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def tick(self):

        # If the game hasn't started or is finished, don't bother.
        if not self.last_play_time or self.state.get() == "finished":
            return

        # Also don't bother if the maximum number of cards are already
        # on the table.
        if len(self.layout) >= self.max_cards_on_table:
            return

        # Also don't bother if the deck is empty.
        if not self.deck:
            return

        # Okay, so, we're playing.  See if too much time has passed.
        curr_time = time.time()
        if curr_time - self.last_play_time < self.deal_delay:
            return

        # Yup.  Deal out three new cards.
        for i in range(3):
            if self.deck:
                self.layout.append(self.deck[0])
                self.deck = self.deck[1:]

        self.update_printable_layout()
        self.send_layout()
        self.channel.broadcast_cc(self.prefix +
                                  "New cards have automatically been dealt.\n")

        # Update the last play time.
        self.last_play_time = time.time()

    def declare(self, player, declare_bits):

        if not self.get_seat_of_player(player):
            player.tell_cc(
                self.prefix +
                "You're not playing in this game!  (But you should join.)\n")
            return

        # Check the three cards for validity and convert them to locations
        # in our linear array.
        valid = True
        card_locations = []
        cards_per_row = len(self.layout) / 3
        for bit in declare_bits:
            # Letter first.
            if bit[0] < 0 or bit[0] > 2:
                valid = False
            else:
                addend = bit[0]

            # And the number.
            multer = bit[1]
            if multer < 0 or multer >= cards_per_row:
                valid = False
            else:

                # Phew.  Passed all the tests.
                card_locations.append(multer * 3 + addend)

        if not valid:
            player.tell_cc(self.prefix + "You declared an invalid card.\n")
            return

        elif ((card_locations[0] == card_locations[1])
              or (card_locations[1] == card_locations[2])
              or (card_locations[0] == card_locations[2])):
            player.tell_cc(self.prefix +
                           "You can't declare duplicate cards.\n")
            return

        # Okay, so, we potentially have valid cards...
        cards = [self.layout[x] for x in card_locations]

        # Bail if any of these are empty locations.
        if not cards[0] or not cards[1] or not cards[2]:
            player.tell_cc(self.prefix + "You can't pick empty spaces.\n")
            return

        # All right.  Three valid, actual cards.  Now let's see if they're
        # actually a set!
        if self.is_a_set(cards):
            seat = self.get_seat_of_player(player)
            seat.data.score += 1
            # zomg.  Is an actual set!  Notify the press.  Update the layout
            # and send it out.
            for i in card_locations:
                self.layout[i] = None
            self.update_layout()
            self.update_printable_layout()
            self.send_layout()
            self.channel.broadcast_cc(self.prefix +
                                      "^Y%s^~ found a set! (%s)\n" %
                                      (player, self.make_set_str(cards)))

            # Determine if the game is over.  If so, we're done!
            if self.no_more_sets():
                self.resolve()
                self.finish()

            # Lastly, mark this as the time of the last valid play.
            self.last_play_time = time.time()

        else:
            player.tell_cc(self.prefix + self.make_set_str(cards) +
                           " is not a set!\n")

    def third_card(self, one, two):

        # For any two cards, the third card to make it a set can be
        # determined easily.  For each element, if the two cards are
        # the same, the third must have the same one as well; if
        # different, use BITFIELDS to find the missing one.  Since
        # there are 3 possible values, subtract 7 from the summation
        # of the two's bit values to get the third one.
        three = []
        for k in range(4):
            if one[k] == two[k]:
                three.append(one[k])
            else:
                three.append(BITFIELDS[k][7 - (BITFIELDS[k][one[k]] +
                                               BITFIELDS[k][two[k]])])
        return tuple(three)

    def is_a_set(self, cards):

        return self.third_card(cards[0], cards[1]) == cards[2]

    def make_set_str(self, cards):

        card_str_list = []
        for card in cards:
            card_str = card[2].code + " ".join([x.display for x in card])
            if card[0] != ONE:
                card_str += "s"
            card_str_list.append(card_str + "^~")

        return ", ".join(card_str_list)

    def no_more_sets(self):

        # First, bail if the deck still has any cards whatsoever, as we can't
        # possibly know that there aren't any sets left until the deck is
        # depleted.
        if self.deck:
            return False

        # Okay, now, get a list of cards on the layout.
        cards_left = [x for x in self.layout if x]

        count_left = len(cards_left)
        # If there are more than 20 cards on the table, we know for a fact
        # that there has to be a set left.
        if count_left > 20:
            return False

        # Take every unique pair of cards on the layout and determine what
        # the third card would be that makes them a set.  If that card is
        # still on the table, we have a valid set left.
        for i in range(count_left):
            for j in range(i + 1, count_left):
                if self.third_card(cards_left[i], cards_left[j]) in cards_left:
                    return False

        # No sets found.
        return True

    def resolve(self):

        winner_dict = {}
        for seat in self.seats:
            score = seat.data.score
            if score in winner_dict:
                winner_dict[score].append(seat.player_name)
            else:
                winner_dict[score] = [seat.player_name]

        winner_score_list = sorted(winner_dict.keys(), reverse=True)

        winner_score = winner_score_list[0]
        self.send_scores()
        if len(winner_dict[winner_score]) == 1:
            self.channel.broadcast_cc(self.prefix + "^Y%s^~ is the winner!\n" %
                                      (winner_dict[winner_score][0]))
        else:
            self.channel.broadcast_cc(
                self.prefix + "These players tied for first: ^Y%s^~\n" %
                (", ".join(winner_dict[winner_score])))

    def show_scores(self, player):

        player.tell_cc("\nSCORES:\n\n")
        state = "yellow"
        for seat in self.seats:
            player_str = seat.player_name
            if state == "yellow":
                name_color_code = "^Y"
                score_color_code = "^C"
                state = "magenta"
            elif state == "magenta":
                name_color_code = "^M"
                score_color_code = "^G"
                state = "yellow"
            tell_string = "   ^R%s^~: %s%s^~, %s%s^~" % (
                seat, name_color_code, player_str, score_color_code,
                get_plural_str(seat.data.score, "point"))
            player.tell_cc(tell_string + "\n")
        player.tell_cc("\n")

    def send_scores(self):
        for player in self.channel.listeners:
            self.show_scores(player)

    def show_help(self, player):

        super(Set, self).show_help(player)
        player.tell_cc("\nSET SETUP PHASE:\n\n")
        player.tell_cc(
            "                ^!columns^. <num>     Set the maximum columns to <num> (7-9).\n"
        )
        player.tell_cc(
            "                  ^!delay^. <sec>     Set the autodeal delay to <sec> secs.\n"
        )
        player.tell_cc(
            "                  ^!cards^. <num>     Set the maximum card count to <num>.\n"
        )
        player.tell_cc(
            "               ^!borders^. on|off     Set the borders on or off.\n"
        )
        player.tell_cc(
            "                        ^!start^.     Start the game.\n")
        player.tell_cc("\nSET PLAY:\n\n")
        player.tell_cc(
            "                   ^!l1^., ^!l2^., ^!l3^.     Declare <l1>, <l2>, <l3> a set.\n"
        )
        player.tell_cc(
            "                       ^!scores^.     See the current scores.\n")
コード例 #49
0
ファイル: rock_paper_scissors.py プロジェクト: sunfall/giles
class RockPaperScissors(SeatedGame):
    """A Rock-Paper-Scissors game table implementation.
    """

    def __init__(self, server, table_name):

        super(RockPaperScissors, self).__init__(server, table_name)

        self.game_display_name = "Rock-Paper-Scissors"
        self.game_name = "rps"
        self.seats = [
            Seat("Left"),
            Seat("Right"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.plays = [None, None]
        self.prefix = "(^RRPS^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # RPS requires both seats, so may as well mark them active.
        self.seats[0].active = True
        self.seats[1].active = True

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        state = self.state.get()

        if state == "need_moves":

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            # If this player is used to prefacing plays with 'move'/'play',
            # let's be polite and just chomp that away.  Also allow 'throw'
            # and 'th', even though they're undocumented, because they seem
            # like an obvious sort of command to try.  (Read that as: I kept
            # typing it.)
            if primary in ('move', 'play', 'throw', 'mv', 'pl', 'th') and len(command_bits) > 1:
                primary = command_bits[1].lower()
            if primary in ('r', 'p', 's', 'rock', 'paper', 'scissors'):
                self.move(player, primary)
                handled = True

            if self.plays[0] and self.plays[1] and self.active:

                # Got the moves!
                self.resolve()
                self.finish()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def tick(self):

        # If we were looking for players, check to see if both
        # seats are full and the game is active.  If so, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player and
           self.seats[1].player and self.active):
            self.state.set("need_moves")
            self.channel.broadcast_cc(self.prefix + "Left: ^Y%s^~; Right: ^Y%s^~\n" %
               (self.seats[0].player, self.seats[1].player))
            self.channel.broadcast_cc(self.prefix + "Players, make your moves!\n")

    def show(self, player):

        state = self.state.get()
        if state == "need_players":
            player.tell_cc(self.prefix + "Everyone is hovering around the table, waiting for players.\n")
        elif state == "need_moves":
            for loc, color in ((0, "^Y"), (1, "^M")):
                if self.seats[loc].player:
                    name = repr(self.seats[loc].player)
                    if self.plays[loc]:
                        player.tell_cc(self.prefix + color + name + "^~'s hand is trembling with anticipation.\n")
                    else:
                        player.tell_cc(self.prefix + color + name + "^~ seems to be deep in thought.\n")
                else:
                    player.tell_cc(self.prefix + "^C%s^~ is strangely empty.\n" % self.seats[loc])
        else:
            player.tell_cc(self.prefix + "Nothing to see here.  Move along.\n")

    def show_help(self, player):

        super(RockPaperScissors, self).show_help(player)
        player.tell_cc("\nROCK-PAPER-SCISSORS:\n\n")
        player.tell_cc("                      ^!rock^., ^!r^.     Throw rock.\n")
        player.tell_cc("                     ^!paper^., ^!p^.     Throw paper.\n")
        player.tell_cc("                  ^!scissors^., ^!s^.     Throw scissors.\n")

    def move(self, player, play):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You're not playing in this game!\n")
            return

        if play in ('r', 'rock'):
            this_move = "rock"
        elif play in ('p', 'paper'):
            this_move = "paper"
        elif play in ('s', 'scissors'):
            this_move = "scissors"
        else:
            player.tell_cc(self.prefix + "Invalid play.\n")
            return

        self.channel.broadcast_cc(self.prefix + "%s's hand twitches.\n" % player)

        if seat == self.seats[0]:
            self.plays[0] = this_move
        else:
            self.plays[1] = this_move

    def resolve(self):

        one = self.plays[0]
        two = self.plays[1]
        one_name = "^Y" + repr(self.seats[0].player) + "^~"
        two_name = "^M" + repr(self.seats[1].player) + "^~"
        self.channel.broadcast_cc(self.prefix + "Jan... ken... pon... Throwdown time!\n")
        self.channel.broadcast_cc(self.prefix + "%s throws ^!%s^.; %s throws ^!%s^.!\n" % (one_name, one, two_name, two))
        if one == two:
            msg = "It's a tie!\n"
        elif ((one == "rock" and two == "paper") or
           (one == "paper" and two == "scissors") or
           (one == "scissors" and two == "rock")):
            msg = two_name + " wins!\n"
        else:
            msg = one_name + " wins!\n"
        self.channel.broadcast_cc(msg)

    def remove_player(self, player):

        # Not only do we want to do the standard things, but if this person
        # really is a player, we want to invalidate their throw.  That way
        # you're not stuck with another player's throw mid-game.
        if self.seats[0].player == player:
            self.plays[0] = None
        elif self.seats[1].player == player:
            self.plays[1] = None
        super(RockPaperScissors, self).remove_player(player)
コード例 #50
0
ファイル: metamorphosis.py プロジェクト: Cloudxtreme/giles
class Metamorphosis(SeatedGame):
    """A Metamorphosis game table implementation.  Invented in 2009 by Gregory
    Keith Van Patten.  Play seems to show that ko fight mode is definitely
    superior to the alternative, so we set it as default.
    """
    def __init__(self, server, table_name):

        super(Metamorphosis, self).__init__(server, table_name)

        self.game_display_name = "Metamorphosis"
        self.game_name = "metamorphosis"
        self.seats = [Seat("Black"), Seat("White")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RMetamorphosis^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Metamorphosis-specific stuff.
        self.board = None
        self.printable_board = None
        self.size = 12
        self.ko_fight = True
        self.group_count = None
        self.turn = None
        self.turn_number = 0
        self.seats[0].data.side = BLACK
        self.seats[0].data.last_was_ko = False
        self.seats[1].data.side = WHITE
        self.seats[1].data.last_was_ko = False
        self.last_r = None
        self.last_c = None
        self.resigner = None
        self.adjacency_map = None
        self.found_winner = False

        self.init_board()

    def init_board(self):

        self.board = []

        # Generate a new empty board.  Boards alternate the starting checkered
        # layout, which we can pregen.
        white_first_row = []
        black_first_row = []
        for c in range(self.size):
            if c % 2:
                white_first_row.append(BLACK)
                black_first_row.append(WHITE)
            else:
                white_first_row.append(WHITE)
                black_first_row.append(BLACK)

        # Then we just add the appropriate rows, two at a time.
        for r in range(self.size / 2):
            self.board.append(white_first_row[:])
            self.board.append(black_first_row[:])

        # Count the number of groups on the board.  Should be size^2.
        self.group_count = self.get_group_count()

    def update_printable_board(self):

        self.printable_board = []
        col_str = "    " + "".join([" " + COLS[i] for i in range(self.size)])
        self.printable_board.append(col_str + "\n")
        self.printable_board.append("   ^m.=" + "".join(["=="] * self.size) +
                                    ".^~\n")
        for r in range(self.size):
            this_str = "%2d ^m|^~ " % (r + 1)
            for c in range(self.size):
                if r == self.last_r and c == self.last_c:
                    this_str += "^5"
                loc = self.board[r][c]
                if loc == WHITE:
                    this_str += "^Wo^~ "
                elif loc == BLACK:
                    this_str += "^Kx^~ "
                else:
                    this_str += "^M.^~ "
            this_str += "^m|^~ %d" % (r + 1)
            self.printable_board.append(this_str + "\n")
        self.printable_board.append("   ^m`=" + "".join(["=="] * self.size) +
                                    "'^~\n")
        self.printable_board.append(col_str + "\n")

    def show(self, player):

        if not self.printable_board:
            self.update_printable_board()
        for line in self.printable_board:
            player.tell_cc(line)
        player.tell_cc(self.get_turn_str() + "\n")

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def get_turn_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        if self.turn == BLACK:
            player = self.seats[0].player_name
            color_msg = "^KBlack/Vertical^~"
        else:
            player = self.seats[1].player_name
            color_msg = "^WWhite/Horizontal^~"

        return ("It is ^Y%s^~'s turn (%s)." % (player, color_msg))

    def is_valid(self, row, col):

        if row < 0 or row >= self.size or col < 0 or col >= self.size:
            return False
        return True

    def get_group_count(self):

        # Counting groups can be done in a single pass, even if the groups
        # are complicated.  It is done as follows:
        # - Check the color in a location and the locations above and to the
        #   left.
        # - If it's the same color as only one of them, assign ourselves the
        #   group ID of that piece;
        # - If it's the same color as /both/, and they have distinct group IDs,
        #   reassign the higher-numbered group ID to the lower group ID, as this
        #   piece joins those two groups;
        # - If it's the same color as neither, add a new group ID.
        # At the end, we simply count the number of distinct group IDs.

        # Build a temporary board tracking the IDs of each spot.
        id_board = []
        for r in range(self.size):
            id_board.append([None] * self.size)

        id_dict = {}
        curr_id = 0
        for r in range(self.size):
            for c in range(self.size):
                self_color = self.board[r][c]
                above_color = None
                left_color = None
                if r - 1 >= 0:
                    above_color = self.board[r - 1][c]
                if c - 1 >= 0:
                    left_color = self.board[r][c - 1]

                if self_color == above_color:

                    # Definitely a group above.  Check to see if it's the same
                    # to the left too.
                    group_id = id_dict[id_board[r - 1][c]]
                    if self_color == left_color:

                        # Same color.  Are they the same group ID?
                        left_id = id_dict[id_board[r][c - 1]]
                        if left_id == group_id:

                            # Yup.  Use it.
                            id_board[r][c] = group_id

                        else:

                            # This piece joins two previously-distinct groups.
                            # Update the ID dict to collapse those two groups
                            # into one and use the lower-valued one for this
                            # space.
                            if left_id < group_id:
                                id_dict[group_id] = left_id
                                id_board[r][c] = left_id
                            else:
                                id_dict[left_id] = group_id
                                id_board[r][c] = group_id

                    else:

                        # Different color left, same color above.  Use the
                        # above ID.
                        id_board[r][c] = group_id

                elif self_color == left_color:

                    # Group to the left, but not above.  Use the left ID.
                    id_board[r][c] = id_dict[id_board[r][c - 1]]

                else:

                    # No group in either direction.  New group.
                    id_board[r][c] = curr_id
                    id_dict[curr_id] = curr_id
                    curr_id += 1

        # Now that we're done with those shenanigans, count the number of unique
        # groups on the board.
        unique_set = set()
        for group_id in id_dict:
            if id_dict[group_id] not in unique_set:
                unique_set.add(id_dict[group_id])

        return len(unique_set)

    def flip(self, row, col):

        curr = self.board[row][col]
        if curr == BLACK:
            self.board[row][col] = WHITE
        else:
            self.board[row][col] = BLACK

    def move(self, player, play):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix +
                           "You can't move; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix +
                           "You must wait for your turn to move.\n")
            return False

        col, row = play

        # Make sure they're all in range.
        if not self.is_valid(row, col):
            player.tell_cc(self.prefix + "Your move is out of bounds.\n")
            return False

        # Does this move increase the number of groups on the board?
        self.flip(row, col)
        new_group_count = self.get_group_count()
        if new_group_count > self.group_count:

            # Yup.  Flip it back and inform the player.
            self.flip(row, col)
            player.tell_cc(self.prefix +
                           "That move increases the group count.\n")
            return False

        move_is_ko = False
        ko_str = ""
        # If we're in ko fight mode, check to see if a ko move is valid.
        if new_group_count == self.group_count:
            move_is_ko = True
            ko_str = ", a ko move"

            if not self.ko_fight:

                # Flip it back; we're not in ko fight mode.
                self.flip(row, col)
                player.tell_cc(
                    self.prefix +
                    "That is a ko move and does not decrease the group count.\n"
                )
                return False

            elif seat.data.last_was_ko:

                # Flip it back; two kos in a row is not allowed.
                self.flip(row, col)
                player.tell_cc(
                    self.prefix +
                    "That is a ko move and you made a ko move last turn.\n")
                return False

            elif row == self.last_r and col == self.last_c:

                # Flip it back; this is the same move their opponent just made.
                self.flip(row, col)
                player.tell_cc(
                    self.prefix +
                    "You cannot repeat your opponent's last move.\n")
                return False

        # This is a valid move.  Apply, announce.
        play_str = "%s%s" % (COLS[col], row + 1)
        self.channel.broadcast_cc(self.prefix +
                                  "^Y%s^~ flips the piece at ^C%s^~%s.\n" %
                                  (seat.player, play_str, ko_str))
        self.last_r = row
        self.last_c = col
        self.turn_number += 1
        self.group_count = new_group_count

        # If it was a ko move, mark the player as having made one, so they
        # can't make another the next turn.  Otherwise clear that bit.
        if move_is_ko:
            seat.data.last_was_ko = True
        else:
            seat.data.last_was_ko = False

        return True

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
                and self.seats[1].player and self.active):
            self.state.set("playing")
            self.channel.broadcast_cc(
                self.prefix +
                "^KBlack/Vertical^~: ^R%s^~; ^WWhite/Horizontal^~: ^Y%s^~\n" %
                (self.seats[0].player, self.seats[1].player))
            self.turn = BLACK
            self.turn_number = 1
            self.send_board()

    def set_size(self, player, size_bits):

        if not size_bits.isdigit():
            player.tell_cc(self.prefix + "Invalid size command.\n")
            return

        size = int(size_bits)

        if size < MIN_SIZE or size > MAX_SIZE:
            player.tell_cc(self.prefix +
                           "Size must be between %d and %d inclusive.\n" %
                           (MIN_SIZE, MAX_SIZE))
            return

        if size % 2:
            player.tell_cc(self.prefix + "Size must be even.\n")
            return

        # Valid!
        self.size = size
        self.channel.broadcast_cc(
            self.prefix + "^R%s^~ has set the board size to ^C%d^~.\n" %
            (player, size))
        self.init_board()
        self.update_printable_board()

    def set_ko_fight(self, player, ko_str):

        ko_bool = booleanize(ko_str)
        if ko_bool:
            if ko_bool > 0:
                self.ko_fight = True
                display_str = "^Con^~"
            else:
                self.ko_fight = False
                display_str = "^coff^~"
            self.channel.broadcast_cc(
                self.prefix + "^R%s^~ has turned ^Gko fight^~ mode %s.\n" %
                (player, display_str))

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix +
                           "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat.data.side:
            player.tell_cc(self.prefix +
                           "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat.data.side
        self.channel.broadcast_cc(self.prefix +
                                  "^R%s^~ is resigning from the game.\n" %
                                  player)
        return True

    def swap(self, player):

        # Like Hex, a swap in Metamorphosis requires a translation to make it the
        # equivalent move for the other player.

        self.flip(self.last_r, self.last_c)
        self.flip(self.last_c, self.last_r)
        self.last_c, self.last_r = self.last_r, self.last_c

        self.channel.broadcast_cc(
            "^Y%s^~ has swapped ^KBlack^~'s first move.\n" % (player))
        self.turn_number += 1

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0]

            if state == "setup":

                if primary in (
                        "size",
                        "sz",
                ):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid size command.\n")
                    handled = True

                elif primary in ("ko", ):

                    if len(command_bits) == 2:
                        self.set_ko_fight(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid ko command.\n")
                    handled = True

                if primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):

                    self.channel.broadcast_cc(
                        self.prefix + "The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):

                    self.state.set("setup")
                    self.channel.broadcast_cc(
                        self.prefix +
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in (
                        "move",
                        "play",
                        "mv",
                        "pl",
                ):

                    invalid = False
                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        invalid = True

                    if invalid:
                        player.tell_cc(self.prefix + "Invalid move command.\n")
                    handled = True

                elif primary in ("swap", ):

                    if self.seats[1].player == player and self.turn_number == 2:
                        self.swap(player)
                        made_move = True
                    else:
                        player.tell_cc(self.prefix + "Invalid swap command.\n")
                    handled = True

                elif primary in ("resign", ):

                    if self.resign(player):
                        made_move = True

                    handled = True

                if made_move:

                    # Okay, something happened on the board.  Update.
                    self.update_printable_board()

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()
                    else:

                        # Nope.  Switch turns...
                        if self.turn == BLACK:
                            self.turn = WHITE
                        else:
                            self.turn = BLACK

                        # ...show everyone the board, and keep on.
                        self.send_board()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.
        if self.resigner == WHITE:
            return self.seats[0].player_name
        elif self.resigner == BLACK:
            return self.seats[1].player_name

        # This is like most connection games; we check recursively from the
        # top and left edges to see whether a player has won.
        self.found_winner = False
        self.adjacency_map = []
        for i in range(self.size):
            self.adjacency_map.append([None] * self.size)

        for i in range(self.size):
            if self.board[i][0] == WHITE:
                self.recurse_adjacency(WHITE, i, 0)
            if self.board[0][i] == BLACK:
                self.recurse_adjacency(BLACK, 0, i)

        # ...except that it has to be at the end of the OTHER player's turn!
        if self.found_winner == BLACK and self.turn == WHITE:
            return self.seats[0].player_name
        elif self.found_winner == WHITE and self.turn == BLACK:
            return self.seats[1].player_name

        # No winner yet.
        return None

    def recurse_adjacency(self, color, row, col):

        # Bail if we found a winner already.
        if self.found_winner:
            return

        # Bail if we're off the board.
        if not self.is_valid(row, col):
            return

        # Bail if we've been here.
        if self.adjacency_map[row][col]:
            return

        # Bail if this is the wrong color.
        if self.board[row][col] != color:
            return

        # Okay.  Occupied and it's this player's.  Mark.
        self.adjacency_map[row][col] = True

        # Have we hit the winning side for this player?
        if ((color == WHITE and col == self.size - 1)
                or (color == BLACK and row == self.size - 1)):

            # Success!
            self.found_winner = color
            return

        # Not a win yet.  Recurse over adjacencies.
        for r_delta, c_delta in CONNECTION_DELTAS:
            self.recurse_adjacency(color, row + r_delta, col + c_delta)

    def resolve(self, winner):
        self.send_board()
        self.channel.broadcast_cc(self.prefix + "^C%s^~ wins!\n" % winner)

    def show_help(self, player):

        super(Metamorphosis, self).show_help(player)
        player.tell_cc("\nMETAMORPHOSIS SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "                    ^!ko^. on|off     Enable/disable ko fight mode.\n"
        )
        player.tell_cc(
            "             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nMETAMORPHOSIS PLAY:\n\n")
        player.tell_cc(
            "      ^!move^. <ln>, ^!play^., ^!mv^., ^!pl^.     Make move <ln> (letter number).\n"
        )
        player.tell_cc(
            "                         ^!swap^.     Swap the first move (only White, only their first).\n"
        )
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #51
0
ファイル: whist.py プロジェクト: sunfall/giles
class Whist(SeatedGame):
    """A Whist game table implementation.  Whist came about sometime in the
    18th century.  This implementation does not (currently) score honours,
    because honours are boring.
    """

    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()

    def show_help(self, player):

        super(Whist, self).show_help(player)
        player.tell_cc("\nWHIST SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nWHIST PLAY:\n\n")
        player.tell_cc("              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n")
        player.tell_cc("                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n")

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_score_str(self):
        return "          ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (self.ns.score, self.ew.score)

    def get_color_code(self, seat):

        if seat == self.seats[0] or seat == self.seats[2]:
            return "^R"
        else:
            return "^M"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name, self.get_color_code(seat), seat)

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            to_return += "It is ^Y%s^~'s turn (%s%s^~).  Trumps are ^C%s^~.\n" % (self.turn.player_name, self.get_color_code(self.turn), self.turn, self.trump_suit)
            to_return += "Tricks:   ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (self.ns.tricks, self.ew.tricks)
        to_return += "The goal score for this game is ^C%s^~.\n" % get_plural_str(self.goal, "point")
        to_return += self.get_score_str()

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" % (player, get_plural_str(new_goal, "point")))

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deal(self):

        dealer_name = self.dealer.player_name

        self.bc_pre("^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" % (dealer_name, self.get_color_code(self.dealer), self.dealer))
        deck = new_deck()
        deck.shuffle()

        # Deal out all of the cards.  We'll flip the last one; that determines
        # the trump suit for the hand.
        self.bc_pre("^R%s^~ deals the cards out to all the players.\n" % dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for i in range(13):
            for seat in self.seats:
                seat.data.hand.add(deck.discard())

        # Flip the dealer's last card; it determines the trump suit.
        last_card = self.dealer.data.hand[-1]
        self.bc_pre("^R%s^~ flips their last card; it is ^C%s^~.\n" % (dealer_name,
           card_to_str(last_card, LONG)))
        self.trump_suit = last_card.suit

        # Sort everyone's hands.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)

        # Show everyone their hands.
        self.show_hands()

        # Set the trick counts to zero.
        self.ns.tricks = 0
        self.ew.tricks = 0

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if (this_suit != self.led_suit and
               hand_has_suit(seat.data.hand, self.led_suit)):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player, "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" % (self.get_sp_str(seat), action_str, card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player and
           self.seats[1].player and self.seats[2].player and
           self.seats[3].player and self.active):
            self.state.set("playing")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Make a new deal.
            self.dealer = self.seats[0]
            self.new_deal()

            # Eldest leads to the first trick.
            self.turn = self.next_seat(self.dealer)
            self.layout.change_turn(self.turn.data.who)

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in ("goal", "score", "sc", "g",):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in ("done", "ready", "d", "r",):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):
                    self.state.set("setup")
                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                card_played = False
                if primary in ("hand", "inventory", "inv", "i",):
                    self.show_hand(player)
                    handled = True

                elif primary in ("play", "move", "pl", "mv",):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == 4:

                        # Finish the trick up.
                        self.finish_trick()

                        # Is that the last trick of this hand?
                        if self.ns.tricks + self.ew.tricks == 13:

                            # Yup.  Finish the hand up.
                            self.finish_hand()

                            # Did someone win the overall game?
                            winner = self.find_winner()
                            if winner:

                                # Yup.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # Nope.  Pass the deal to the next dealer...
                                self.dealer = self.next_seat(self.dealer)

                                # Deal and set up the first player.
                                self.new_deal()
                                self.turn = self.next_seat(self.dealer)
                                self.layout.change_turn(self.turn.data.who)

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(self.log_prefix + "Something went horribly awry; trick ended without a finish.")
            self.bc_pre("Something went horribly wrong; no one won the trick!  Tell the admin.\n")
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" % (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # Give the trick to the correct partnership.
        if winning_seat == self.seats[0] or winning_seat == self.seats[2]:
            self.ns.tricks += 1
        else:
            self.ew.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def finish_hand(self):

        # Which side won more than 6 tricks?
        if self.ns.tricks > 6:
            winning_side = "^RNorth/South^~"
            addend = self.ns.tricks - 6
            self.ns.score += addend
        else:
            winning_side = "^MEast/West^~"
            addend = self.ew.tricks - 6
            self.ew.score += addend

        # Let everyone know.
        self.bc_pre("%s wins the hand and gains ^C%s^~.\n" % (winning_side, get_plural_str(addend, "point")))
        self.bc_pre(self.get_score_str())

    def find_winner(self):

        # Easy: has one of the sides reached a winning score?
        if self.ns.score >= self.goal:
            return self.ns
        elif self.ew.score >= self.goal:
            return self.ew

        return None

    def resolve(self, winning_partnership):

        if self.ns == winning_partnership:
            name_one = self.seats[0].player_name
            name_two = self.seats[2].player_name
        else:
            name_one = self.seats[1].player_name
            name_two = self.seats[3].player_name
        self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))
コード例 #52
0
ファイル: set.py プロジェクト: sunfall/giles
class Set(SeatedGame):
    """A Set game table implementation.  Invented in 1974 by Marsha Jean Falco.
    """

    def __init__(self, server, table_name):

        super(Set, self).__init__(server, table_name)

        self.game_display_name = "Set"
        self.game_name = "set"
        self.seats = []
        self.min_players = 1
        self.max_players = 32767 # We don't even use this.
        self.state = State("need_players")
        self.prefix = "(^RSet^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Set-specific stuff.
        self.max_cards_on_table = DEFAULT_MAX_CARDS
        self.deal_delay = DEFAULT_DEAL_DELAY
        self.layout = None
        self.printable_layout = None
        self.deck = None
        self.last_play_time = None
        self.max_card_count = 81
        self.has_borders = True


    def build_deck(self):

        # Generate the deck...
        self.deck = []
        for count in (ONE, TWO, THREE):
            fill_list = (SMOOTH,)
            if self.has_borders:
                fill_list = (SMOOTH, WAVY, CHUNKY)
            for fill in fill_list:
                for color in (MAGENTA, RED, GREEN):
                    for shape in (BLOB, LOZENGE, SQUIGGLE):
                        self.deck.append((count, fill, color, shape))

        # ...and shuffle it.
        random.shuffle(self.deck)

        # Trim it to at most the max count.
        self.deck = self.deck[:self.max_card_count]

    def build_layout(self):

        # Put the first twelve cards on the table.
        self.layout = self.deck[:12]
        self.deck = self.deck[12:]

    def update_layout(self):

        # If the size of the layout is 12 or smaller, this is easy;
        # we just draw cards left (if any) to fill gaps in the board.
        # If it's larger than 12, it's easy too; we rebuild the
        # layout without any gaps (and then, juuust in case, add
        # blank cards if it somehow got smaller than 12.)

        layout_len = len(self.layout)
        if len(self.layout) <= 12:
            for i in range(layout_len):
                if not self.layout[i] and self.deck:
                    self.layout[i] = self.deck[0]
                    self.deck = self.deck[1:]

        else:
            new_layout = [x for x in self.layout if x]
            while len(new_layout) < 12:
                new_layout.append(None)
            self.layout = new_layout

    def get_card_art_bits(self, card, line_number):
        # .----. 1 /~~~~\ |=||=|
        # |2or3| 2 {    } =    =
        # |1or3| 3 {    } |    |
        # |2or3| 4 {    } =    =
        # `----' 5 \~~~~/ |=||=|

        # Just in case...
        if line_number < 1 or line_number > 5:
            return

        # At the end of the game, we will sometimes print blank
        # spaces where cards should go.  Handle that.
        if not card:
            return "      "
        count, fill, color, shape = card

        # If a line has a piece of art, it looks like this...
        art_bit = "%s%s^~" % (color.code, shape.art)
        # ...otherwise this.
        blank_bit = "    "

        if line_number == 1:
            return fill.edge_art[0]
        elif line_number == 2 or line_number == 4:
            center = blank_bit
            if count == TWO or count == THREE:
                center = art_bit
            return fill.edge_art[1] % center
        elif line_number == 3:
            center = blank_bit
            if count == ONE or count == THREE:
                center = art_bit
            return fill.edge_art[2] % center
        elif line_number == 5:
            return fill.edge_art[3]

        # Dunno how we got here...
        return "ERROR"

    def update_printable_layout(self):

        self.printable_layout = []
        if not self.layout:
            self.printable_layout.append("The layout is currently ^cempty^~.\n")
            return

        # If the layout doesn't have a number of card spaces divisible
        # by 3, something is horribly wrong, and we should bail.
        if len(self.layout) % 3 != 0:
            self.printable_layout.append("Something is ^Rhorribly wrong^~ with the layout.  Alert an admin.\n")
            return

        # Okay, we have a usable layout.  Generate it!
        cards_per_row = len(self.layout) / 3
        self.printable_layout.append("=======" * cards_per_row + "=\n")
        for row in range(3):
            if row == 0:
                row_char = "A"
            elif row == 1:
                row_char = "B"
            else:
                row_char = "C"
            for card_line in range(1, 6):
                this_line = ""
                for col in range(cards_per_row):
                    this_line += (" %s" % self.get_card_art_bits(self.layout[col * 3 + row], card_line))
                self.printable_layout.append(this_line + "\n")

            # Now we print the codes for each card under the cards.
            this_line = ""
            for col in range(1, cards_per_row + 1):
                this_line += ("   %s%s  " % (row_char, col))
            self.printable_layout.append(this_line + "\n\n")

    def show(self, player):

        if not self.printable_layout:
            self.update_printable_layout()
        for line in self.printable_layout:
            player.tell_cc(line)

    def send_layout(self):
        for listener in self.channel.listeners:
            self.show(listener)

    def join(self, player, join_bits):

        # We have to override join for two reasons: one, we allow players
        # to join during the game (!), and two, we have to create seats
        # for players on the fly, as we have no idea how many there might
        # end up being.

        state = self.state.get()
        if state == "need_players" or state == "playing":
            if len(join_bits) != 0:
                player.tell_cc(self.prefix + "Cannot request a specific seat in Set.\n")
            elif self.get_seat_of_player(player):
                player.tell_cc(self.prefix + "You're already playing!\n")
            else:
                seat = Seat("%s" % str(len(self.seats) + 1))
                seat.data.score = 0
                self.seats.append(seat)
                seat.sit(player)
                player.tell_cc(self.prefix + "You are now sitting in seat %s.\n" % seat)
                if not self.channel.is_connected(player):
                    self.channel.connect(player)
                self.channel.broadcast_cc(self.prefix + "^Y%s^~ is now playing in seat ^C%s^~.\n" % (player, seat))
                self.num_players += 1
        else:
            player.tell_cc(self.prefix + "Not looking for players.\n")

        return True

    def set_max_columns(self, player, column_str):

        if not column_str.isdigit():
            player.tell_cc(self.prefix + "Must provide numbers.\n")
            return

        column_val = int(column_str)
        if column_val < 7 or column_val > 9:
            player.tell_cc(self.prefix + "Must provide a value between 7 and 9 inclusive.\n")
            return

        self.max_cards_on_table = column_val * 3
        self.channel.broadcast_cc(self.prefix + "^Y%s^~ set the maximum columns to ^C%s^~.\n" % (player, str(column_val)))

    def set_delay(self, player, delay_str):

        if not delay_str.isdigit():
            player.tell_cc(self.prefix + "Must provide numbers.\n")
            return

        delay_val = int(delay_str)
        if delay_val < 5 or delay_val > 300:
            player.tell_cc(self.prefix + "Must provide a value between 5 and 300 inclusive.\n")
            return

        self.deal_delay = delay_val
        self.channel.broadcast_cc(self.prefix + "^Y%s^~ set the deal delay to ^C%s^~ seconds.\n" % (player, str(delay_val)))

    def set_max_count(self, player, count_str):

        if not count_str.isdigit():
            player.tell_cc(self.prefix + "Must provide numbers.\n")
            return

        count_val = int(count_str)
        if count_val < 21 or count_val > 81 or count_val %3 != 0:
            player.tell_cc(self.prefix + "Must provide a value between 21 and 81 inclusive, divisible by 3.\n")
            return

        self.max_card_count = count_val
        self.channel.broadcast_cc(self.prefix + "^Y%s^~ set the maximum card count to ^C%s^~ cards.\n" % (player, str(count_val)))

    def set_border(self, player, border_str):

        border_bool = booleanize(border_str)
        if border_bool:
            if border_bool > 0:
                self.has_borders = True
                display_str = "^Con^~"
            else:
                self.has_borders = False
                display_str = "^coff^~"
            self.channel.broadcast_cc(self.prefix + "^R%s^~ has turned borders %s.\n" % (player, display_str))
        else:
            player.tell_cc(self.prefix + "Not a valid boolean!\n")

    def handle(self, player, command_str):

        # Handle common commands first.
        handled = self.handle_common_commands(player, command_str)

        if not handled:
            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if primary in ("score", "scores"):
                self.show_scores(player)
                handled = True

            elif state == "need_players":
                if primary in ("column", "columns"):
                    if len(command_bits) == 2:
                        self.set_max_columns(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid columns command.\n")
                    handled = True

                elif primary in ("delay",):
                    if len(command_bits) == 2:
                        self.set_delay(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid delay command.\n")
                    handled = True

                elif primary in ("cards", "count"):
                    if len(command_bits) == 2:
                        self.set_max_count(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid cards command.\n")
                    handled = True

                elif primary in ("borders", "border"):
                    if len(command_bits) == 2:
                        self.set_border(player, command_bits[1])
                    else:
                        player.tell_cc(self.prefix + "Invalid border command.\n")
                    handled = True

                elif primary in ("start",):
                    if not len(self.seats):
                        player.tell_cc(self.prefix + "Need at least one player!\n")
                    else:
                        self.state.set("playing")
                        self.channel.broadcast_cc(self.prefix + "Game on!\n")
                        self.build_deck()
                        self.build_layout()
                        self.update_printable_layout()
                        self.send_layout()
                        self.last_play_time = time.time()
                    handled = True

            elif state == "playing":

                # Everything at this point should be a move, which consists
                # of a list of 3 card choices.  As always, do the polite thing
                # for players who do the play/pl/move/mv/thing.
                if primary in ('play', 'move', 'pl', 'mv'):
                    play_bits = demangle_move(command_bits[1:])
                else:
                    play_bits = demangle_move(command_bits)

                if play_bits and len(play_bits) == 3:
                    self.declare(player, play_bits)
                    handled = True

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def tick(self):

        # If the game hasn't started or is finished, don't bother.
        if not self.last_play_time or self.state.get() == "finished":
            return

        # Also don't bother if the maximum number of cards are already
        # on the table.
        if len(self.layout) >= self.max_cards_on_table:
            return

        # Also don't bother if the deck is empty.
        if not self.deck:
            return

        # Okay, so, we're playing.  See if too much time has passed.
        curr_time = time.time()
        if curr_time - self.last_play_time < self.deal_delay:
            return

        # Yup.  Deal out three new cards.
        for i in range(3):
            if self.deck:
                self.layout.append(self.deck[0])
                self.deck = self.deck[1:]

        self.update_printable_layout()
        self.send_layout()
        self.channel.broadcast_cc(self.prefix + "New cards have automatically been dealt.\n")

        # Update the last play time.
        self.last_play_time = time.time()

    def declare(self, player, declare_bits):

        if not self.get_seat_of_player(player):
            player.tell_cc(self.prefix + "You're not playing in this game!  (But you should join.)\n")
            return

        # Check the three cards for validity and convert them to locations
        # in our linear array.
        valid = True
        card_locations = []
        cards_per_row = len(self.layout) / 3
        for bit in declare_bits:
            # Letter first.
            if bit[0] < 0 or bit[0] > 2:
                valid = False
            else:
                addend = bit[0]

            # And the number.
            multer = bit[1]
            if multer < 0 or multer >= cards_per_row:
                valid = False
            else:

                # Phew.  Passed all the tests.
                card_locations.append(multer * 3 + addend)

        if not valid:
            player.tell_cc(self.prefix + "You declared an invalid card.\n")
            return

        elif ((card_locations[0] == card_locations[1]) or
           (card_locations[1] == card_locations[2]) or
           (card_locations[0] == card_locations[2])):
            player.tell_cc(self.prefix + "You can't declare duplicate cards.\n")
            return

        # Okay, so, we potentially have valid cards...
        cards = [self.layout[x] for x in card_locations]

        # Bail if any of these are empty locations.
        if not cards[0] or not cards[1] or not cards[2]:
            player.tell_cc(self.prefix + "You can't pick empty spaces.\n")
            return

        # All right.  Three valid, actual cards.  Now let's see if they're
        # actually a set!
        if self.is_a_set(cards):
            seat = self.get_seat_of_player(player)
            seat.data.score += 1
            # zomg.  Is an actual set!  Notify the press.  Update the layout
            # and send it out.
            for i in card_locations:
                self.layout[i] = None
            self.update_layout()
            self.update_printable_layout()
            self.send_layout()
            self.channel.broadcast_cc(self.prefix + "^Y%s^~ found a set! (%s)\n" %
               (player, self.make_set_str(cards)))

            # Determine if the game is over.  If so, we're done!
            if self.no_more_sets():
                self.resolve()
                self.finish()

            # Lastly, mark this as the time of the last valid play.
            self.last_play_time = time.time()

        else:
            player.tell_cc(self.prefix + self.make_set_str(cards) + " is not a set!\n")

    def third_card(self, one, two):

        # For any two cards, the third card to make it a set can be
        # determined easily.  For each element, if the two cards are
        # the same, the third must have the same one as well; if
        # different, use BITFIELDS to find the missing one.  Since
        # there are 3 possible values, subtract 7 from the summation
        # of the two's bit values to get the third one.
        three = []
        for k in range(4):
            if one[k] == two[k]:
                three.append(one[k])
            else:
                three.append(BITFIELDS[k][7 - (BITFIELDS[k][one[k]] +
                   BITFIELDS[k][two[k]])])
        return tuple(three)

    def is_a_set(self, cards):

        return self.third_card(cards[0], cards[1]) == cards[2]

    def make_set_str(self, cards):

        card_str_list = []
        for card in cards:
            card_str = card[2].code + " ".join([x.display for x in card])
            if card[0] != ONE:
                card_str += "s"
            card_str_list.append(card_str + "^~")

        return ", ".join(card_str_list)

    def no_more_sets(self):

        # First, bail if the deck still has any cards whatsoever, as we can't
        # possibly know that there aren't any sets left until the deck is
        # depleted.
        if self.deck:
            return False

        # Okay, now, get a list of cards on the layout.
        cards_left = [x for x in self.layout if x]

        count_left = len(cards_left)
        # If there are more than 20 cards on the table, we know for a fact
        # that there has to be a set left.
        if count_left > 20:
            return False

        # Take every unique pair of cards on the layout and determine what
        # the third card would be that makes them a set.  If that card is
        # still on the table, we have a valid set left.
        for i in range(count_left):
            for j in range(i + 1, count_left):
                if self.third_card(cards_left[i], cards_left[j]) in cards_left:
                    return False

        # No sets found.
        return True

    def resolve(self):

        winner_dict = {}
        for seat in self.seats:
            score = seat.data.score
            if score in winner_dict:
                winner_dict[score].append(seat.player_name)
            else:
                winner_dict[score] = [seat.player_name]

        winner_score_list = sorted(winner_dict.keys(), reverse=True)

        winner_score = winner_score_list[0]
        self.send_scores()
        if len(winner_dict[winner_score]) == 1:
            self.channel.broadcast_cc(self.prefix + "^Y%s^~ is the winner!\n" % (winner_dict[winner_score][0]))
        else:
            self.channel.broadcast_cc(self.prefix + "These players tied for first: ^Y%s^~\n" % (", ".join(winner_dict[winner_score])))

    def show_scores(self, player):

        player.tell_cc("\nSCORES:\n\n")
        state = "yellow"
        for seat in self.seats:
            player_str = seat.player_name
            if state == "yellow":
                name_color_code = "^Y"
                score_color_code = "^C"
                state = "magenta"
            elif state == "magenta":
                name_color_code = "^M"
                score_color_code = "^G"
                state = "yellow"
            tell_string = "   ^R%s^~: %s%s^~, %s%s^~" % (seat, name_color_code, player_str, score_color_code, get_plural_str(seat.data.score, "point"))
            player.tell_cc(tell_string + "\n")
        player.tell_cc("\n")

    def send_scores(self):
        for player in self.channel.listeners:
            self.show_scores(player)

    def show_help(self, player):

        super(Set, self).show_help(player)
        player.tell_cc("\nSET SETUP PHASE:\n\n")
        player.tell_cc("                ^!columns^. <num>     Set the maximum columns to <num> (7-9).\n")
        player.tell_cc("                  ^!delay^. <sec>     Set the autodeal delay to <sec> secs.\n")
        player.tell_cc("                  ^!cards^. <num>     Set the maximum card count to <num>.\n")
        player.tell_cc("               ^!borders^. on|off     Set the borders on or off.\n")
        player.tell_cc("                        ^!start^.     Start the game.\n")
        player.tell_cc("\nSET PLAY:\n\n")
        player.tell_cc("                   ^!l1^., ^!l2^., ^!l3^.     Declare <l1>, <l2>, <l3> a set.\n")
        player.tell_cc("                       ^!scores^.     See the current scores.\n")
コード例 #53
0
ファイル: game.py プロジェクト: Cloudxtreme/giles
class Game(object):
    """The base Game class.  Does a lot of the boring footwork that all
    games need to handle: adding players, generating the chat channel for
    the game, handling kibitzing and player replacement, and so on.  In
    general, though, you want one of the subclasses of this class, either
    SeatedGame() or SeatlessGame().
    """

    def __init__(self, server, table_name):

        self.server = server
        self.channel = server.channel_manager.has_channel(table_name)
        if not self.channel:
            self.channel = self.server.channel_manager.add_channel(table_name,
                                                gameable=True, persistent=True)
        else:
            self.channel.persistent = True
        self.game_display_name = "Generic Game"
        self.game_name = "game"
        self.table_display_name = table_name
        self.table_name = table_name.lower()

        self.active = False
        self.private = False

        self.state = State("config")
        self.prefix = "(^RGame^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Override this next variable in your subclasses if you're not
        # done debugging them.
        self.debug = False

    def __repr__(self):
        return ("%s (%s)" % (self.table_display_name, self.game_display_name))

    def log_pre(self, log_str):

        # This utility function logs with the proper prefix.
        self.server.log.log(self.log_prefix + log_str)

    def tell_pre(self, player, tell_str):

        # This utility function tells a player a line with the proper prefix.
        player.tell_cc(self.prefix + tell_str)

    def bc_pre(self, send_str):

        # This utility function sends a message to the game channel with the
        # proper prefix.
        self.channel.broadcast_cc(self.prefix + send_str)

    def handle(self, player, command_str):

        # The generic handle does very little work; it passes it all off
        # to the common command handler.  You are not expected to
        # actually /call/ this handle(), but if you do it has the same
        # effect as calling handle_common_commands().

        self.handle_common_commands(player, command_str)

    def show_help(self, player):
        self.log_pre("%s asked for help with the game." % player)
        player.tell_cc("\nVIEWING:\n\n")
        player.tell_cc("                ^!kibitz^., ^!watch^.     Watch the game as it happens.\n")
        player.tell_cc("                ^!show^., ^!look^., ^!l^.     Look at the game itself.\n")
        player.tell_cc("\nPARTICIPATING:\n\n")
        player.tell_cc("            ^!terminate^., ^!finish^.     Terminate game.\n")
        if self.debug:
            player.tell_cc("\nDEBUG:\n\n")
            player.tell_cc("         ^!change_state^. <state>     Change game state to <state>.\n")

    def show(self, player):

        # This function should /absolutely/ be overridden by any games.
        self.tell_pre(player, "This is the default game class; nothing to show.\n")

    def finish(self):

        # If you have fancy cleanup that should be done when a game is
        # done, override this function.
        self.channel.persistent = False
        self.state.set("finished")

    def terminate(self, player):

        self.bc_pre("^Y%s^~ has terminated the game.\n" % player)
        self.log_pre("%s has terminated the game." % player)
        self.finish()

    def tick(self):

        # If your game has events that occur potentially without player
        # intervention, override this class.  An obvious example is a
        # game with a timer; a less-obvious one is a game that you want
        # to auto-transition whenever certain conditions are met, such
        # as a game auto-starting when all the players are ready and
        # available.
        pass

    def remove_player(self, player):
        """Signature for removing a player from the game.

        When a player removes themselves from a game or disconnects from
        the server, this method is called on every game currently
        running; implementations are expected to only remove the player
        from a game if they are participating.
        """

        # You will almost certainly want to override this if you're
        # writing a new subclass of Game().  Existing subclasses
        # may or may not have useful implementations extant.
        pass

    def handle_common_commands(self, player, command_str):

        # This handles certain command bits common to all games.
        # - If the game is finished, reject commands.
        # - At any point, take these generic commands:
        #   * help (print help in regards to the game)
        #   * kibitz (watch the game)
        #   * show (show the game itself)
        #   * terminate (end the game immediately)
        #   * private (make private)
        #   * public (make public)
        # - In addition, if we're in debug mode, allow people to
        #   forcibly switch states via change_state.
        #
        # We also return whether or not we handled the command, which may
        # be useful to games that call us.

        handled = False
        # Pull out the command bits.
        command_bits = command_str.split()
        primary = command_bits[0].lower()

        # You can always ask for help...
        if primary in ('help', 'h', '?'):
            self.show_help(player)
            handled = True

        # You can always add yourself as a kibitzer...
        elif primary in ('kibitz', 'watch'):
            if not self.channel.is_connected(player):
                self.channel.connect(player)
                self.show(player)
            else:
                self.tell_pre(player, "You're already watching this game!\n")
            handled = True

        elif primary in ('show', 'look', 'l'):
            self.show(player)
            handled = True

        elif primary in ('terminate', 'finish', 'flip'):
            self.terminate(player)
            handled = True

        elif primary in ('private',):
            self.bc_pre("^R%s^~ has turned the game ^cprivate^~.\n" % (player))
            self.private = True
            handled = True

        elif primary in ('public',):
            self.bc_pre("^R%s^~ has turned the game ^Cpublic^~.\n" % (player))
            self.private = False
            handled = True

        elif primary in ('change_state',):
            if not self.debug:
                self.tell_pre(player, "No switching states in production!\n")
            elif len(command_bits) != 2:
                self.tell_pre(player, "Invalid state to switch to.\n")
            else:
                self.state.set(command_bits[1].lower())
                self.bc_pre("^R%s^~ forced a state change to ^C%s^~.\n" % (player, self.state.get()))
            handled = True

        return handled
コード例 #54
0
ファイル: breakthrough.py プロジェクト: Cloudxtreme/giles
class Breakthrough(SeatedGame):
    """A Breakthrough game table implementation.  Invented in 2000 by Dan Troyka.
    """

    def __init__(self, server, table_name):

        super(Breakthrough, self).__init__(server, table_name)

        self.game_display_name = "Breakthrough"
        self.game_name = "breakthrough"
        self.seats = [
            Seat("Black"),
            Seat("White")
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RBreakthrough^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Breakthrough-specific stuff.
        self.width = 8
        self.height = 8
        self.turn = None
        self.black = self.seats[0]
        self.white = self.seats[1]
        self.resigner = None
        self.layout = None

        # We cheat and create a black and white piece here.  Breakthrough
        # doesn't differentiate between pieces, so this allows us to do
        # comparisons later.
        self.bp = Piece("^K", "b", "B")
        self.bp.data.owner = self.black
        self.wp = Piece("^W", "w", "W")
        self.wp.data.owner = self.white

        self.init_board()

    def init_board(self):

        # Create the layout and the pieces it uses as well.  Note that we
        # can be ultra-lazy with the pieces, as Breakthrough doesn't distinguish
        # between them, so there's no reason to create a bunch of identical
        # bits.
        self.layout = SquareGridLayout()
        self.layout.resize(self.width, self.height)
        last_row = self.height - 1
        next_last_row = last_row - 1

        for i in range(self.width):
            self.layout.place(self.bp, 0, i, update=False)
            self.layout.place(self.bp, 1, i, update=False)
            self.layout.place(self.wp, next_last_row, i, update=False)
            self.layout.place(self.wp, last_row, i, update=False)

        # Set the piece counts.
        self.white.data.piece_count = self.width * 2
        self.black.data.piece_count = self.width * 2
        self.layout.update()

    def show(self, player):

        player.tell_cc(self.layout)
        player.tell_cc(self.get_turn_str() + "\n")

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def get_turn_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        if self.turn == self.black:
            player = self.seats[0].player
            color_msg = "^KBlack^~"
        else:
            player = self.seats[1].player
            color_msg = "^WWhite^~"

        return ("It is ^Y%s^~'s turn (%s)." % (player, color_msg))

    def move(self, player, src, dst):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if self.turn != seat:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        src_c, src_r = src
        dst_c, dst_r = dst
        src_str = "%s%s" % (COLS[src_c], src_r + 1)
        dst_str = "%s%s" % (COLS[dst_c], dst_r + 1)

        # Make sure they're all in range.
        if (not self.layout.is_valid(src_r, src_c) or
         not self.layout.is_valid(dst_r, dst_c)):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Does the player even have a piece there?
        src_loc = self.layout.grid[src_r][src_c]
        if not src_loc or src_loc.data.owner != self.turn:
            self.tell_pre(player, "You don't have a piece at ^C%s^~.\n" % src_str)
            return False

        # Is the destination within range?
        if self.turn == self.black:
            row_delta = 1
        else:
            row_delta = -1

        if src_r + row_delta != dst_r:
            self.tell_pre(player, "You can't move from ^C%s^~ to row ^R%d^~.\n" % (src_str, dst_r + 1))
            return False
        if abs(src_c - dst_c) > 1:
            self.tell_pre(player, "You can't move from ^C%s^~ to column ^R%s^~.\n" % (src_str, COLS[dst_c]))
            return False

        # Okay, this is actually (gasp) a potentially legitimate move.  If
        # it's a move forward, it only works if the forward space is empty.
        if src_c == dst_c and self.layout.grid[dst_r][dst_c]:
            self.tell_pre(player, "A straight-forward move can only be into an empty space.\n")
            return False

        # Otherwise, it must not have one of the player's own pieces in it.
        dst_loc = self.layout.grid[dst_r][dst_c]
        if src_loc == dst_loc:
            self.tell_pre(player, "A diagonal-forward move cannot be onto your own piece.\n")
            return False

        additional_str = ""
        opponent = self.seats[0]
        if seat == self.seats[0]:
            opponent = self.seats[1]
        if dst_loc:
            # It's a capture.
            additional_str = ", capturing one of ^R%s^~'s pieces" % (opponent.player)
            opponent.data.piece_count -= 1
        self.bc_pre("^Y%s^~ moves a piece from ^C%s^~ to ^G%s^~%s.\n" % (seat.player, src_str, dst_str, additional_str))

        # Make the move on the layout.
        self.layout.move(src_r, src_c, dst_r, dst_c, True)

        return ((src_r, src_c), (dst_r, dst_c))

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
           and self.seats[1].player and self.active):
            self.state.set("playing")
            self.bc_pre("^KBlack^~: ^R%s^~; ^WWhite^~: ^Y%s^~\n" %
               (self.seats[0].player, self.seats[1].player))
            self.turn = self.black
            self.send_board()

    def set_size(self, player, size_bits):

        # Is there an 'x' in the middle of a single argument?
        if len(size_bits) == 1:
            size_bits[0] = size_bits[0].lower()
            if "x" in size_bits[0]:
                size_bits = size_bits[0].split("x")

        width = size_bits[0]
        # If there's a single element, height == width.
        if len(size_bits) == 1:
            height = width
        elif len(size_bits) == 2:
            width = size_bits[0]
            height = size_bits[1]
        else:
            self.tell_pre(player, "Invalid size command.\n")
            return

        if not width.isdigit() or not height.isdigit():
            self.tell_pre(player, "Invalid size command.\n")
            return

        w = int(width)
        h = int(height)

        if w < MIN_WIDTH or w > MAX_WIDTH:
            self.tell_pre(player, "Width must be between %d and %d inclusive.\n" % (MIN_WIDTH, MAX_WIDTH))
            return

        if h < MIN_HEIGHT or h > MAX_HEIGHT:
            self.tell_pre(player, "Height must be between %d and %d inclusive.\n" % (MIN_HEIGHT, MAX_HEIGHT))
            return

        # Valid!
        self.width = w
        self.height = h
        self.bc_pre("^R%s^~ has set the board size to ^C%d^Gx^C%d^~.\n" % (player, w, h))
        self.init_board()

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("^R%s^~ is resigning from the game.\n" % player)
        return True

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz",):

                    self.set_size(player, command_bits[1:])
                    handled = True

                if primary in ("done", "ready", "d", "r",):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):

                    self.state.set("setup")
                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in ("move", "play", "mv", "pl",):

                    invalid = False
                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 2:
                        made_move = self.move(player, move_bits[0], move_bits[1])
                    else:
                        invalid = True

                    if invalid:
                        self.tell_pre(player, "Invalid move command.\n")
                    handled = True

                elif primary in ("resign",):

                    if self.resign(player):
                        made_move = True

                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()
                    else:

                        # Nope.  Switch turns...
                        self.turn = self.next_seat(self.turn)

                        # ...show everyone the board, and keep on.
                        self.send_board()

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.  Same if
        # they lost all their pieces.
        if self.resigner == self.white or self.seats[1].data.piece_count == 0:
            return self.seats[0].player_name
        elif self.resigner == self.black or self.seats[1].data.piece_count == 0:
            return self.seats[1].player_name

        # Aw, we have to do work.  If black has a piece on the last row,
        # they win; if white has a piece on the first row, they win.
        if self.bp in self.layout.grid[-1]:
            return self.seats[0].player_name
        if self.wp in self.layout.grid[0]:
            return self.seats[1].player_name

        # ...that wasn't really much work, but there's no winner yet.
        return None

    def resolve(self, winner):
        self.send_board()
        self.bc_pre("^C%s^~ wins!\n" % winner)

    def show_help(self, player):

        super(Breakthrough, self).show_help(player)
        player.tell_cc("\nBREAKTHROUGH SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("    ^!size^. <size> | <w> <h>, ^!sz^.     Set board to <size>x<size>/<w>x<h>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nBREAKTHROUGH PLAY:\n\n")
        player.tell_cc("          ^!move^. <ln> <ln2>, ^!mv^.     Move from <ln> to <ln2> (letter number).\n")
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #55
0
ファイル: redstone.py プロジェクト: Cloudxtreme/giles
class Redstone(SeatedGame):
    """A Redstone game table implementation.  Invented in 2012 by Mark Steere.
    """
    def __init__(self, server, table_name):

        super(Redstone, self).__init__(server, table_name)

        self.game_display_name = "Redstone"
        self.game_name = "redstone"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RRedstone^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Redstone-specific stuff.
        self.height = 19
        self.width = 19
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.made_move = False
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.made_move = False
        self.resigner = None
        self.layout = None

        # Like most abstracts, Redstone doesn't need to differentiate between
        # the pieces on the board.
        self.bp = Piece("^K", "x", "X")
        self.bp.data.owner = self.black
        self.black.data.piece = self.bp
        self.wp = Piece("^W", "o", "O")
        self.wp.data.owner = self.white
        self.white.data.piece = self.wp
        self.rp = Piece("^R", "r", "R")
        self.rp.data.owner = None

        # Initialize the starting layout.
        self.init_layout()

    def init_layout(self):

        # Create the layout.  Empty, so easy.
        self.layout = SquareGridLayout(highlight_color="^I")
        self.layout.resize(self.width, self.height)

    def get_sp_str(self, seat):

        return "^C%s^~ (%s)" % (seat.player_name, seat.data.seat_str)

    def get_turn_str(self):

        if not self.turn:
            return "The game has not yet started.\n"

        return "It is ^C%s^~'s turn (%s).\n" % (self.turn.player_name,
                                                self.turn.data.seat_str)

    def show(self, player):

        player.tell_cc(self.layout)
        player.tell_cc(self.get_turn_str())

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def set_size(self, player, size_bits):

        # Is there an 'x' in the middle of a single argument?
        if len(size_bits) == 1:
            size_bits[0] = size_bits[0].lower()
            if "x" in size_bits[0]:
                size_bits = size_bits[0].split("x")

        width = size_bits[0]
        # If there's a single element, height == width.
        if len(size_bits) == 1:
            height = width
        elif len(size_bits) == 2:
            width = size_bits[0]
            height = size_bits[1]
        else:
            self.tell_pre(player, "Invalid size command.\n")
            return

        if not width.isdigit() or not height.isdigit():
            self.tell_pre(player, "You didn't even send numbers!\n")
            return

        w = int(width)
        h = int(height)

        if w < MIN_SIZE or w > MAX_SIZE or h < MIN_SIZE or h > MAX_SIZE:
            self.tell_pre(
                player,
                "Width and height must be between %d and %d inclusive.\n" %
                (MIN_SIZE, MAX_SIZE))
            return

        # Valid!
        self.width = w
        self.height = h
        self.bc_pre("^R%s^~ has set the board size to ^C%d^Gx^C%d^~.\n" %
                    (player, w, h))
        self.init_layout()

    def recurse_capture(self, seat, row, col, visited):

        # If it's a dud coordinate, bail.
        if not self.layout.is_valid(row, col):
            return None

        # If we've been here, bail.
        if visited[row][col]:
            return None

        # If it's an empty space, then it's a liberty.
        pos = self.layout.grid[row][col]
        if not pos:
            return []

        # If it's a piece of the other player, bail.
        if pos.data.owner != seat:
            return None

        # Okay.  New piece, right color.  Mark it visited.
        visited[row][col] = True

        # Recurse on adjacencies.  If any return an empty list, this group has
        # a liberty and we return an empty list as well; otherwise we return a
        # concatenation of pieces found further on, for easy removal.
        return_list = [(row, col)]
        for r_delta, c_delta in CONNECTION_DELTAS:
            result = self.recurse_capture(seat, row + r_delta, col + c_delta,
                                          visited)
            if result == []:

                # Liberty!  Bail.
                return []
            elif result:

                # Found a subgroup with no liberties.  Extend.
                return_list.extend(result)

        # We never found a liberty.  Return the list of pieces.
        return return_list

    def move_is_capture(self, piece, row, col):

        # Tentatively place the piece here.
        self.layout.place(piece, row, col, update=False)

        # Build the visitation list.
        visited = []
        for r in range(self.height):
            visited.append([None] * self.width)

        # If this piece is not a redstone, we check its own liberties.  We
        # can quickly bail if this succeeds.
        if piece != self.rp:
            if self.recurse_capture(piece.data.owner, row, col, visited):
                self.layout.remove(row, col, update=False)
                return True

        # Now we check the liberties of all four adjacent locations, assuming
        # there's a piece there and it's not a redstone.
        for r_delta, c_delta in CONNECTION_DELTAS:
            new_r = row + r_delta
            new_c = col + c_delta
            if self.layout.is_valid(new_r, new_c):
                pos = self.layout.grid[new_r][new_c]
                if pos and pos != self.rp:

                    # We have to rebuild the visited list for each piece we
                    # check, because of the recursive "bail fast" method we use
                    # for detecting liberties.  This should be improved.
                    visited = []
                    for r in range(self.height):
                        visited.append([None] * self.width)

                    if self.recurse_capture(pos.data.owner, new_r, new_c,
                                            visited):

                        # Bail.
                        self.layout.remove(row, col, update=False)
                        return True

        # We never found a capture.  Remove and return.
        self.layout.remove(row, col, update=False)
        return False

    def move(self, player, move_bits):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        col, row = move_bits

        # Is the move on the board?
        if not self.layout.is_valid(row, col):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Is there a piece already there?
        if self.layout.grid[row][col]:
            self.tell_pre(player, "There is already a piece there.\n")
            return False

        # Is it a capturing move?
        piece = seat.data.piece
        if self.move_is_capture(piece, row, col):
            self.tell_pre(player, "That would cause a capture.\n")
            return False

        # Valid.  Put a piece there.
        move_str = "%s%s" % (COLS[col], row + 1)
        self.layout.place(piece, row, col, True)

        # Update the board.
        self.bc_pre("%s places a piece at ^C%s^~.\n" %
                    (self.get_sp_str(seat), move_str))

        seat.data.made_move = True
        return True

    def capture(self, row, col):

        # If for some reason this is called and there's not a redstone at this
        # location, bail.
        if self.layout.grid[row][col] != self.rp:
            return -1

        # All right.  Check all four adjacencies; if they no longer have a
        # liberty, capture them.  Note that we come up with the list first,
        # and /then/ execute the captures, as doing them as we find them may
        # give groups liberties during the removal process.

        visited = []
        for r in range(self.height):
            visited.append([None] * self.width)

        capture_list = []
        for r_delta, c_delta in CONNECTION_DELTAS:
            new_r = row + r_delta
            new_c = col + c_delta
            if self.layout.is_valid(new_r, new_c):
                loc = self.layout.grid[new_r][new_c]
                if loc and loc != self.rp:
                    captures = self.recurse_capture(loc.data.owner, new_r,
                                                    new_c, visited)
                    if captures:
                        capture_list.extend(
                            [x for x in captures if x not in capture_list])

        # Remove all pieces in the capture list.
        for capture_r, capture_c in capture_list:
            self.layout.remove(capture_r, capture_c, update=False)

        self.layout.update()

        # Return the number of pieces captured.
        return len(capture_list)

    def red(self, player, move_bits):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        col, row = move_bits

        # Is the move on the board?
        if not self.layout.is_valid(row, col):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Is there a piece already there?
        if self.layout.grid[row][col]:
            self.tell_pre(player, "There is already a piece there.\n")
            return False

        # Is it not a capturing move?
        piece = self.rp
        if not self.move_is_capture(piece, row, col):
            self.tell_pre(player, "That would not cause a capture.\n")
            return False

        # Valid.  Put the piece there.
        move_str = "%s%s" % (COLS[col], row + 1)
        self.layout.place(piece, row, col, True)

        # Redstones by definition make captures.
        capture_count = self.capture(row, col)

        self.bc_pre("%s places a ^Rredstone^~ at ^C%s^~, ^Ycapturing %s^~.\n" %
                    (self.get_sp_str(seat), move_str,
                     get_plural_str(capture_count, "stone")))

        seat.data.made_move = True
        return True

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("%s is resigning from the game.\n" % self.get_sp_str(seat))
        return True

    def tick(self):

        # If both seats are occupied and the game is active, start.
        if (self.state.get() == "need_players" and self.black.player
                and self.white.player and self.active):
            self.bc_pre("%s: ^C%s^~; %s: ^C%s^~\n" %
                        (self.black.data.seat_str, self.black.player_name,
                         self.white.data.seat_str, self.white.player_name))
            self.state.set("playing")
            self.turn = self.black
            self.send_board()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.lower().split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz"):

                    self.set_size(player, command_bits[1:])
                    handled = True

                elif primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):

                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    self.state.set("setup")
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in (
                        "move",
                        "play",
                        "mv",
                        "pl",
                ):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        self.tell_pre(player, "Invalid move command.\n")
                    handled = True

                if primary in (
                        "redstone",
                        "red",
                        "r",
                ):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.red(player, move_bits[0])
                    else:
                        self.tell_pre(player, "Invalid red command.\n")
                    handled = True

                elif primary in ("resign", ):

                    made_move = self.resign(player)
                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()

                    if winner:

                        # Yup!
                        self.resolve(winner)
                        self.finish()
                    else:

                        # No.  Switch turns.
                        self.turn = self.next_seat(self.turn)
                        self.send_board()

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def find_winner(self):

        # Did someone resign?
        if self.resigner == self.white:
            return self.black
        elif self.resigner == self.black:
            return self.white

        # If one player has no pieces left, the other player won.  If neither
        # player has a piece, mover wins.
        found_white = False
        found_black = False
        for r in range(self.height):
            for c in range(self.width):
                loc = self.layout.grid[r][c]
                if loc:
                    if loc.data.owner == self.black:
                        found_black = True
                    elif loc.data.owner == self.white:
                        found_white = True

        if not found_black and self.black.data.made_move:
            if not found_white and self.white.data.made_move:

                # Mover wins.
                return self.turn
            else:
                return self.white
        elif not found_white and self.white.data.made_move:
            return self.black

        # No winner yet.
        return None

    def resolve(self, winner):

        self.send_board()
        self.bc_pre("%s wins!\n" % self.get_sp_str(winner))

    def show_help(self, player):

        super(Redstone, self).show_help(player)
        player.tell_cc("\nREDSTONE SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "             ^!size^. <size>,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nREDSTONE PLAY:\n\n")
        player.tell_cc(
            "      ^!move^. <ln>, ^!play^., ^!mv^., ^!pl^.     Make move <ln> (letter number).\n"
        )
        player.tell_cc(
            "                  ^!red^. <ln>, ^!r^.     Place redstone at <ln> (letter number).\n"
        )
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #56
0
ファイル: breakthrough.py プロジェクト: Cloudxtreme/giles
class Breakthrough(SeatedGame):
    """A Breakthrough game table implementation.  Invented in 2000 by Dan Troyka.
    """
    def __init__(self, server, table_name):

        super(Breakthrough, self).__init__(server, table_name)

        self.game_display_name = "Breakthrough"
        self.game_name = "breakthrough"
        self.seats = [Seat("Black"), Seat("White")]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RBreakthrough^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Breakthrough-specific stuff.
        self.width = 8
        self.height = 8
        self.turn = None
        self.black = self.seats[0]
        self.white = self.seats[1]
        self.resigner = None
        self.layout = None

        # We cheat and create a black and white piece here.  Breakthrough
        # doesn't differentiate between pieces, so this allows us to do
        # comparisons later.
        self.bp = Piece("^K", "b", "B")
        self.bp.data.owner = self.black
        self.wp = Piece("^W", "w", "W")
        self.wp.data.owner = self.white

        self.init_board()

    def init_board(self):

        # Create the layout and the pieces it uses as well.  Note that we
        # can be ultra-lazy with the pieces, as Breakthrough doesn't distinguish
        # between them, so there's no reason to create a bunch of identical
        # bits.
        self.layout = SquareGridLayout()
        self.layout.resize(self.width, self.height)
        last_row = self.height - 1
        next_last_row = last_row - 1

        for i in range(self.width):
            self.layout.place(self.bp, 0, i, update=False)
            self.layout.place(self.bp, 1, i, update=False)
            self.layout.place(self.wp, next_last_row, i, update=False)
            self.layout.place(self.wp, last_row, i, update=False)

        # Set the piece counts.
        self.white.data.piece_count = self.width * 2
        self.black.data.piece_count = self.width * 2
        self.layout.update()

    def show(self, player):

        player.tell_cc(self.layout)
        player.tell_cc(self.get_turn_str() + "\n")

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def get_turn_str(self):

        if not self.turn:
            return ("The game has not yet started.\n")

        if self.turn == self.black:
            player = self.seats[0].player
            color_msg = "^KBlack^~"
        else:
            player = self.seats[1].player
            color_msg = "^WWhite^~"

        return ("It is ^Y%s^~'s turn (%s)." % (player, color_msg))

    def move(self, player, src, dst):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if self.turn != seat:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        src_c, src_r = src
        dst_c, dst_r = dst
        src_str = "%s%s" % (COLS[src_c], src_r + 1)
        dst_str = "%s%s" % (COLS[dst_c], dst_r + 1)

        # Make sure they're all in range.
        if (not self.layout.is_valid(src_r, src_c)
                or not self.layout.is_valid(dst_r, dst_c)):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Does the player even have a piece there?
        src_loc = self.layout.grid[src_r][src_c]
        if not src_loc or src_loc.data.owner != self.turn:
            self.tell_pre(player,
                          "You don't have a piece at ^C%s^~.\n" % src_str)
            return False

        # Is the destination within range?
        if self.turn == self.black:
            row_delta = 1
        else:
            row_delta = -1

        if src_r + row_delta != dst_r:
            self.tell_pre(
                player, "You can't move from ^C%s^~ to row ^R%d^~.\n" %
                (src_str, dst_r + 1))
            return False
        if abs(src_c - dst_c) > 1:
            self.tell_pre(
                player, "You can't move from ^C%s^~ to column ^R%s^~.\n" %
                (src_str, COLS[dst_c]))
            return False

        # Okay, this is actually (gasp) a potentially legitimate move.  If
        # it's a move forward, it only works if the forward space is empty.
        if src_c == dst_c and self.layout.grid[dst_r][dst_c]:
            self.tell_pre(
                player,
                "A straight-forward move can only be into an empty space.\n")
            return False

        # Otherwise, it must not have one of the player's own pieces in it.
        dst_loc = self.layout.grid[dst_r][dst_c]
        if src_loc == dst_loc:
            self.tell_pre(
                player,
                "A diagonal-forward move cannot be onto your own piece.\n")
            return False

        additional_str = ""
        opponent = self.seats[0]
        if seat == self.seats[0]:
            opponent = self.seats[1]
        if dst_loc:
            # It's a capture.
            additional_str = ", capturing one of ^R%s^~'s pieces" % (
                opponent.player)
            opponent.data.piece_count -= 1
        self.bc_pre("^Y%s^~ moves a piece from ^C%s^~ to ^G%s^~%s.\n" %
                    (seat.player, src_str, dst_str, additional_str))

        # Make the move on the layout.
        self.layout.move(src_r, src_c, dst_r, dst_c, True)

        return ((src_r, src_c), (dst_r, dst_c))

    def tick(self):

        # If both seats are full and the game is active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
                and self.seats[1].player and self.active):
            self.state.set("playing")
            self.bc_pre("^KBlack^~: ^R%s^~; ^WWhite^~: ^Y%s^~\n" %
                        (self.seats[0].player, self.seats[1].player))
            self.turn = self.black
            self.send_board()

    def set_size(self, player, size_bits):

        # Is there an 'x' in the middle of a single argument?
        if len(size_bits) == 1:
            size_bits[0] = size_bits[0].lower()
            if "x" in size_bits[0]:
                size_bits = size_bits[0].split("x")

        width = size_bits[0]
        # If there's a single element, height == width.
        if len(size_bits) == 1:
            height = width
        elif len(size_bits) == 2:
            width = size_bits[0]
            height = size_bits[1]
        else:
            self.tell_pre(player, "Invalid size command.\n")
            return

        if not width.isdigit() or not height.isdigit():
            self.tell_pre(player, "Invalid size command.\n")
            return

        w = int(width)
        h = int(height)

        if w < MIN_WIDTH or w > MAX_WIDTH:
            self.tell_pre(
                player, "Width must be between %d and %d inclusive.\n" %
                (MIN_WIDTH, MAX_WIDTH))
            return

        if h < MIN_HEIGHT or h > MAX_HEIGHT:
            self.tell_pre(
                player, "Height must be between %d and %d inclusive.\n" %
                (MIN_HEIGHT, MAX_HEIGHT))
            return

        # Valid!
        self.width = w
        self.height = h
        self.bc_pre("^R%s^~ has set the board size to ^C%d^Gx^C%d^~.\n" %
                    (player, w, h))
        self.init_board()

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if self.turn != seat:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("^R%s^~ is resigning from the game.\n" % player)
        return True

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.split()
            primary = command_bits[0]

            if state == "setup":

                if primary in (
                        "size",
                        "sz",
                ):

                    self.set_size(player, command_bits[1:])
                    handled = True

                if primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):

                    self.state.set("setup")
                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in (
                        "move",
                        "play",
                        "mv",
                        "pl",
                ):

                    invalid = False
                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 2:
                        made_move = self.move(player, move_bits[0],
                                              move_bits[1])
                    else:
                        invalid = True

                    if invalid:
                        self.tell_pre(player, "Invalid move command.\n")
                    handled = True

                elif primary in ("resign", ):

                    if self.resign(player):
                        made_move = True

                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()
                    if winner:
                        self.resolve(winner)
                        self.finish()
                    else:

                        # Nope.  Switch turns...
                        self.turn = self.next_seat(self.turn)

                        # ...show everyone the board, and keep on.
                        self.send_board()

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def find_winner(self):

        # If someone resigned, this is the easiest thing ever.  Same if
        # they lost all their pieces.
        if self.resigner == self.white or self.seats[1].data.piece_count == 0:
            return self.seats[0].player_name
        elif self.resigner == self.black or self.seats[1].data.piece_count == 0:
            return self.seats[1].player_name

        # Aw, we have to do work.  If black has a piece on the last row,
        # they win; if white has a piece on the first row, they win.
        if self.bp in self.layout.grid[-1]:
            return self.seats[0].player_name
        if self.wp in self.layout.grid[0]:
            return self.seats[1].player_name

        # ...that wasn't really much work, but there's no winner yet.
        return None

    def resolve(self, winner):
        self.send_board()
        self.bc_pre("^C%s^~ wins!\n" % winner)

    def show_help(self, player):

        super(Breakthrough, self).show_help(player)
        player.tell_cc("\nBREAKTHROUGH SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "    ^!size^. <size> | <w> <h>, ^!sz^.     Set board to <size>x<size>/<w>x<h>.\n"
        )
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nBREAKTHROUGH PLAY:\n\n")
        player.tell_cc(
            "          ^!move^. <ln> <ln2>, ^!mv^.     Move from <ln> to <ln2> (letter number).\n"
        )
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #57
0
ファイル: tanbo.py プロジェクト: Cloudxtreme/giles
class Tanbo(SeatedGame):
    """A Tanbo game table implementation.  Invented in 1993 by Mark Steere.
    This only implements the 2p version, although it does have the 9x9, 13x13,
    and 19x19 sizes.  There's also a 21x21 size that came from discussion with
    Mark, and 5x5 and 7x7 sizes that came naturally from the piece layouts.
    """
    def __init__(self, server, table_name):

        super(Tanbo, self).__init__(server, table_name)

        self.game_display_name = "Tanbo"
        self.game_name = "tanbo"
        self.seats = [
            Seat("Black"),
            Seat("White"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.prefix = "(^RTanbo^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Tanbo-specific stuff.
        self.size = 19
        self.turn = None
        self.black = self.seats[0]
        self.black.data.seat_str = "^KBlack^~"
        self.black.data.root_list = []
        self.white = self.seats[1]
        self.white.data.seat_str = "^WWhite^~"
        self.white.data.root_list = []
        self.resigner = None
        self.layout = None

        # Initialize the starting layout.
        self.init_layout()

    def get_root_piece(self, seat, num):
        if seat == self.black:
            p = Piece("^K", "x", "X")
        else:
            p = Piece("^W", "o", "O")

        p.data.owner = seat
        p.data.num = num
        return p

    def init_layout(self):

        # Create the layout and fill it with pieces.
        self.layout = SquareGridLayout(highlight_color="^I")
        self.layout.resize(self.size)

        black_count = 0
        white_count = 0
        self.black.data.root_list = []
        self.white.data.root_list = []

        # There are three different layouts depending on the size.  All of them
        # alternate between black and white pieces; the two larger ones have 16
        # roots, whereas the smallest size has 4.  (There's a 5x5 grid for
        # testing as well.)
        if self.size == 5:
            jump_delta = 4
            extent = 2
            offset = 0
        elif self.size == 7:
            jump_delta = 6
            extent = 2
            offset = 0
        elif self.size == 9:
            jump_delta = 6
            extent = 2
            offset = 1
        elif self.size == 13:
            jump_delta = 4
            extent = 4
            offset = 0
        elif self.size == 19:
            jump_delta = 6
            extent = 4
            offset = 0
        else:  # size == 21
            jump_delta = 4
            extent = 6
            offset = 0
        for i in range(extent):
            for j in range(extent):
                if (i + j) % 2:
                    p = self.get_root_piece(self.black, black_count)
                    self.black.data.root_list.append(p)
                    black_count += 1
                else:
                    p = self.get_root_piece(self.white, white_count)
                    self.white.data.root_list.append(p)
                    white_count += 1
                row = offset + i * jump_delta
                col = offset + j * jump_delta
                p.data.start = (row, col)
                self.layout.place(p, row, col, update=False)
        self.layout.update()

    def get_sp_str(self, seat):

        return "^C%s^~ (%s)" % (seat.player_name, seat.data.seat_str)

    def get_turn_str(self):

        if not self.turn:
            return "The game has not yet started.\n"

        return "It is ^C%s^~'s turn (%s).\n" % (self.turn.player_name,
                                                self.turn.data.seat_str)

    def show(self, player):

        player.tell_cc(self.layout)
        player.tell_cc(self.get_turn_str())

    def send_board(self):

        for player in self.channel.listeners:
            self.show(player)

    def set_size(self, player, size_str):

        if not size_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        size = int(size_str)

        if size not in (
                5,
                7,
                9,
                13,
                19,
                21,
        ):
            self.tell_pre(player, "Size must be 5, 7, 9, 13, 19, or 21.\n")
            return False

        # Valid!
        self.size = size
        self.bc_pre("^R%s^~ has set the board size to ^C%d^~.\n" %
                    (player, size))
        self.init_layout()

    def can_place_at(self, seat, row, col):

        # You can place a piece in Tanbo iff it is adjacent to exactly one of
        # your own pieces.  If a location is valid, we'll return the piece that
        # is adjacent; otherwise we return nothing.

        if self.layout.grid[row][col]:

            # Occupied; clearly can't place here.
            return None

        adj_count = 0
        for r_delta, c_delta in CONNECTION_DELTAS:
            new_r = row + r_delta
            new_c = col + c_delta
            if self.layout.is_valid(new_r, new_c):
                loc = self.layout.grid[new_r][new_c]
                if loc and loc.data.owner == seat:
                    piece = loc
                    adj_count += 1

        if adj_count == 1:
            return piece
        else:
            return None

    def recurse_is_bound(self, piece, row, col, prev_row, prev_col):

        # Thanks to the way Tanbo placement works, we don't have to worry about
        # loops, so we can get away with not using an adjacenty map and instead
        # just track the direction we came from.
        #
        # Bail if this isn't a valid location.
        if not self.layout.is_valid(row, col):
            return True

        loc = self.layout.grid[row][col]

        # If there's no piece here, see if we can place one.
        if not loc:
            if self.can_place_at(piece.data.owner, row, col):

                # Yup.  This root isn't bound.
                return False

            else:

                # No, this location is binding.
                return True

        elif loc != piece:

            # Some other root.  Definitely binding.
            return True

        else:

            # Okay, it's another part of this root.  Recurse, but don't double
            # back.
            for r_delta, c_delta in CONNECTION_DELTAS:
                new_r = row + r_delta
                new_c = col + c_delta
                if new_r != prev_row or new_c != prev_col:
                    if not self.recurse_is_bound(piece, new_r, new_c, row,
                                                 col):

                        # A recursive call found a liberty.  Awesome!
                        return False

            # All of the recursive calls returned that they were bound.  This
            # (sub)root is bound.
            return True

    def root_is_bound(self, piece):

        # We'll just start recursing at the root's starting location and find
        # whether it's bound or not.
        row, col = piece.data.start
        return self.recurse_is_bound(piece, row, col, None, None)

    def kill_root(self, piece):

        # We could do this recursively, but we're lazy.
        for r in range(self.size):
            for c in range(self.size):
                loc = self.layout.grid[r][c]
                if loc == piece:
                    self.layout.remove(r, c, update=False)
        self.layout.update()

        # Remove this root from the owner's root list.
        piece.data.owner.data.root_list.remove(piece)

    def update_roots(self, row, col):

        # If the piece at row, col is part of a bounded root, that root is killed.
        piece = self.layout.grid[row][col]
        if self.root_is_bound(piece):
            self.kill_root(piece)

            # -1 indicates a suicide.
            return -1

        # Not a suicide; loop through all roots, finding bound ones.
        bound_root_list = []
        all_roots = self.black.data.root_list[:]
        all_roots.extend(self.white.data.root_list)
        for root in all_roots:
            if self.root_is_bound(root):
                bound_root_list.append(root)

        bound_count = 0
        for bound_root in bound_root_list:
            self.kill_root(bound_root)
            bound_count += 1

        # Return the number of roots we killed.
        return bound_count

    def move(self, player, move_bits):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't move; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to move.\n")
            return False

        col, row = move_bits

        # Is the move on the board?
        if not self.layout.is_valid(row, col):
            self.tell_pre(player, "Your move is out of bounds.\n")
            return False

        # Is it a valid Tanbo play?
        piece = self.can_place_at(seat, row, col)
        if not piece:
            self.tell_pre(
                player,
                "That location is not adjacent to exactly one of your pieces.\n"
            )
            return False

        # Valid.  Put the piece there.
        move_str = "%s%s" % (COLS[col], row + 1)
        self.layout.place(piece, row, col, True)

        # Update the root statuses.
        root_kill_str = ""
        root_kill = self.update_roots(row, col)
        if root_kill < 0:
            root_kill_str = ", ^ysuiciding the root^~"
        elif root_kill > 0:
            root_kill_str = ", ^Ykilling %s^~" % (get_plural_str(
                root_kill, "root"))
        self.bc_pre("%s grows a root to ^C%s^~%s.\n" %
                    (self.get_sp_str(seat), move_str, root_kill_str))

        return True

    def resign(self, player):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You can't resign; you're not playing!\n")
            return False

        if seat != self.turn:
            self.tell_pre(player, "You must wait for your turn to resign.\n")
            return False

        self.resigner = seat
        self.bc_pre("%s is resigning from the game.\n" % self.get_sp_str(seat))
        return True

    def tick(self):

        # If both seats are occupied and the game is active, start.
        if (self.state.get() == "need_players" and self.black.player
                and self.white.player and self.active):
            self.bc_pre("%s: ^C%s^~; %s: ^C%s^~\n" %
                        (self.black.data.seat_str, self.black.player_name,
                         self.white.data.seat_str, self.white.player_name))
            self.state.set("playing")
            self.turn = self.black
            self.send_board()

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()
            command_bits = command_str.lower().split()
            primary = command_bits[0]

            if state == "setup":

                if primary in ("size", "sz"):

                    if len(command_bits) == 2:
                        self.set_size(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid size command.\n")
                    handled = True

                elif primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):

                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):

                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    self.state.set("setup")
                    handled = True

            elif state == "playing":

                made_move = False

                if primary in (
                        "move",
                        "play",
                        "mv",
                        "pl",
                ):

                    move_bits = demangle_move(command_bits[1:])
                    if move_bits and len(move_bits) == 1:
                        made_move = self.move(player, move_bits[0])
                    else:
                        self.tell_pre(player, "Invalid move command.\n")
                    handled = True

                elif primary in ("resign", ):

                    made_move = self.resign(player)
                    handled = True

                if made_move:

                    # Did someone win?
                    winner = self.find_winner()

                    if winner:

                        # Yup!
                        self.resolve(winner)
                        self.finish()
                    else:

                        # No.  Change turns and send the board to listeners.
                        self.turn = self.next_seat(self.turn)
                        self.send_board()

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def find_winner(self):

        # Did someone resign?
        if self.resigner == self.white:
            return self.black
        elif self.resigner == self.black:
            return self.white

        # If one player has no pieces left, the other player won.
        if not len(self.white.data.root_list):
            return self.black
        elif not len(self.black.data.root_list):
            return self.white

        # No winner.
        return None

    def resolve(self, winner):

        self.send_board()
        self.bc_pre("%s wins!\n" % self.get_sp_str(winner))

    def show_help(self, player):

        super(Tanbo, self).show_help(player)
        player.tell_cc("\nTANBO SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "     ^!size^. 5|7|9|13|19|21,  ^!sz^.     Set board to <size>.\n")
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nTANBO PLAY:\n\n")
        player.tell_cc(
            "      ^!move^. <ln>, ^!play^., ^!mv^., ^!pl^.     Make move <ln> (letter number).\n"
        )
        player.tell_cc("                       ^!resign^.     Resign.\n")
コード例 #58
0
class RockPaperScissors(SeatedGame):
    """A Rock-Paper-Scissors game table implementation.
    """
    def __init__(self, server, table_name):

        super(RockPaperScissors, self).__init__(server, table_name)

        self.game_display_name = "Rock-Paper-Scissors"
        self.game_name = "rps"
        self.seats = [
            Seat("Left"),
            Seat("Right"),
        ]
        self.min_players = 2
        self.max_players = 2
        self.state = State("need_players")
        self.plays = [None, None]
        self.prefix = "(^RRPS^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # RPS requires both seats, so may as well mark them active.
        self.seats[0].active = True
        self.seats[1].active = True

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        state = self.state.get()

        if state == "need_moves":

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            # If this player is used to prefacing plays with 'move'/'play',
            # let's be polite and just chomp that away.  Also allow 'throw'
            # and 'th', even though they're undocumented, because they seem
            # like an obvious sort of command to try.  (Read that as: I kept
            # typing it.)
            if primary in ('move', 'play', 'throw', 'mv', 'pl',
                           'th') and len(command_bits) > 1:
                primary = command_bits[1].lower()
            if primary in ('r', 'p', 's', 'rock', 'paper', 'scissors'):
                self.move(player, primary)
                handled = True

            if self.plays[0] and self.plays[1] and self.active:

                # Got the moves!
                self.resolve()
                self.finish()

        if not handled:
            player.tell_cc(self.prefix + "Invalid command.\n")

    def tick(self):

        # If we were looking for players, check to see if both
        # seats are full and the game is active.  If so, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
                and self.seats[1].player and self.active):
            self.state.set("need_moves")
            self.channel.broadcast_cc(
                self.prefix + "Left: ^Y%s^~; Right: ^Y%s^~\n" %
                (self.seats[0].player, self.seats[1].player))
            self.channel.broadcast_cc(self.prefix +
                                      "Players, make your moves!\n")

    def show(self, player):

        state = self.state.get()
        if state == "need_players":
            player.tell_cc(
                self.prefix +
                "Everyone is hovering around the table, waiting for players.\n"
            )
        elif state == "need_moves":
            for loc, color in ((0, "^Y"), (1, "^M")):
                if self.seats[loc].player:
                    name = repr(self.seats[loc].player)
                    if self.plays[loc]:
                        player.tell_cc(
                            self.prefix + color + name +
                            "^~'s hand is trembling with anticipation.\n")
                    else:
                        player.tell_cc(self.prefix + color + name +
                                       "^~ seems to be deep in thought.\n")
                else:
                    player.tell_cc(self.prefix +
                                   "^C%s^~ is strangely empty.\n" %
                                   self.seats[loc])
        else:
            player.tell_cc(self.prefix + "Nothing to see here.  Move along.\n")

    def show_help(self, player):

        super(RockPaperScissors, self).show_help(player)
        player.tell_cc("\nROCK-PAPER-SCISSORS:\n\n")
        player.tell_cc(
            "                      ^!rock^., ^!r^.     Throw rock.\n")
        player.tell_cc(
            "                     ^!paper^., ^!p^.     Throw paper.\n")
        player.tell_cc(
            "                  ^!scissors^., ^!s^.     Throw scissors.\n")

    def move(self, player, play):

        seat = self.get_seat_of_player(player)
        if not seat:
            player.tell_cc(self.prefix + "You're not playing in this game!\n")
            return

        if play in ('r', 'rock'):
            this_move = "rock"
        elif play in ('p', 'paper'):
            this_move = "paper"
        elif play in ('s', 'scissors'):
            this_move = "scissors"
        else:
            player.tell_cc(self.prefix + "Invalid play.\n")
            return

        self.channel.broadcast_cc(self.prefix +
                                  "%s's hand twitches.\n" % player)

        if seat == self.seats[0]:
            self.plays[0] = this_move
        else:
            self.plays[1] = this_move

    def resolve(self):

        one = self.plays[0]
        two = self.plays[1]
        one_name = "^Y" + repr(self.seats[0].player) + "^~"
        two_name = "^M" + repr(self.seats[1].player) + "^~"
        self.channel.broadcast_cc(self.prefix +
                                  "Jan... ken... pon... Throwdown time!\n")
        self.channel.broadcast_cc(self.prefix +
                                  "%s throws ^!%s^.; %s throws ^!%s^.!\n" %
                                  (one_name, one, two_name, two))
        if one == two:
            msg = "It's a tie!\n"
        elif ((one == "rock" and two == "paper")
              or (one == "paper" and two == "scissors")
              or (one == "scissors" and two == "rock")):
            msg = two_name + " wins!\n"
        else:
            msg = one_name + " wins!\n"
        self.channel.broadcast_cc(msg)

    def remove_player(self, player):

        # Not only do we want to do the standard things, but if this person
        # really is a player, we want to invalidate their throw.  That way
        # you're not stuck with another player's throw mid-game.
        if self.seats[0].player == player:
            self.plays[0] = None
        elif self.seats[1].player == player:
            self.plays[1] = None
        super(RockPaperScissors, self).remove_player(player)
コード例 #59
0
ファイル: game.py プロジェクト: sunfall/giles
class Game(object):
    """The base Game class.  Does a lot of the boring footwork that all
    games need to handle: adding players, generating the chat channel for
    the game, handling kibitzing and player replacement, and so on.  In
    general, though, you want one of the subclasses of this class, either
    SeatedGame() or SeatlessGame().
    """

    def __init__(self, server, table_name):

        self.server = server
        self.channel = server.channel_manager.has_channel(table_name)
        if not self.channel:
            self.channel = self.server.channel_manager.add_channel(table_name,
                                                gameable=True, persistent=True)
        else:
            self.channel.persistent = True
        self.game_display_name = "Generic Game"
        self.game_name = "game"
        self.table_display_name = table_name
        self.table_name = table_name.lower()

        self.active = False
        self.private = False

        self.state = State("config")
        self.prefix = "(^RGame^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Override this next variable in your subclasses if you're not
        # done debugging them.
        self.debug = False

    def __repr__(self):
        return ("%s (%s)" % (self.table_display_name, self.game_display_name))

    def log_pre(self, log_str):

        # This utility function logs with the proper prefix.
        self.server.log.log(self.log_prefix + log_str)

    def tell_pre(self, player, tell_str):

        # This utility function tells a player a line with the proper prefix.
        player.tell_cc(self.prefix + tell_str)

    def bc_pre(self, send_str):

        # This utility function sends a message to the game channel with the
        # proper prefix.
        self.channel.broadcast_cc(self.prefix + send_str)

    def handle(self, player, command_str):

        # The generic handle does very little work; it passes it all off
        # to the common command handler.  You are not expected to
        # actually /call/ this handle(), but if you do it has the same
        # effect as calling handle_common_commands().

        self.handle_common_commands(player, command_str)

    def show_help(self, player):
        self.log_pre("%s asked for help with the game." % player)
        player.tell_cc("\nVIEWING:\n\n")
        player.tell_cc("                ^!kibitz^., ^!watch^.     Watch the game as it happens.\n")
        player.tell_cc("                ^!show^., ^!look^., ^!l^.     Look at the game itself.\n")
        player.tell_cc("\nPARTICIPATING:\n\n")
        player.tell_cc("            ^!terminate^., ^!finish^.     Terminate game.\n")
        if self.debug:
            player.tell_cc("\nDEBUG:\n\n")
            player.tell_cc("         ^!change_state^. <state>     Change game state to <state>.\n")

    def show(self, player):

        # This function should /absolutely/ be overridden by any games.
        self.tell_pre(player, "This is the default game class; nothing to show.\n")

    def finish(self):

        # If you have fancy cleanup that should be done when a game is
        # done, override this function.
        self.log_pre("This game has been marked as finished.")
        self.channel.persistent = False
        self.state.set("finished")

    def terminate(self, player):

        self.bc_pre("^Y%s^~ has terminated the game.\n" % player)
        self.log_pre("%s has terminated the game." % player)
        self.finish()

    def tick(self):

        # If your game has events that occur potentially without player
        # intervention, override this class.  An obvious example is a
        # game with a timer; a less-obvious one is a game that you want
        # to auto-transition whenever certain conditions are met, such
        # as a game auto-starting when all the players are ready and
        # available.
        pass

    def remove_player(self, player):
        """Signature for removing a player from the game.

        When a player removes themselves from a game or disconnects from
        the server, this method is called on every game currently
        running; implementations are expected to only remove the player
        from a game if they are participating.
        """

        # You will almost certainly want to override this if you're
        # writing a new subclass of Game().  Existing subclasses
        # may or may not have useful implementations extant.
        pass

    def handle_common_commands(self, player, command_str):

        # This handles certain command bits common to all games.
        # - If the game is finished, reject commands.
        # - At any point, take these generic commands:
        #   * help (print help in regards to the game)
        #   * kibitz (watch the game)
        #   * show (show the game itself)
        #   * terminate (end the game immediately)
        #   * private (make private)
        #   * public (make public)
        # - In addition, if we're in debug mode, allow people to
        #   forcibly switch states via change_state.
        #
        # We also return whether or not we handled the command, which may
        # be useful to games that call us.

        handled = False
        # Pull out the command bits.
        command_bits = command_str.split()
        primary = command_bits[0].lower()

        # You can always ask for help...
        if primary in ('help', 'h', '?'):
            self.show_help(player)
            handled = True

        # You can always add yourself as a kibitzer...
        elif primary in ('kibitz', 'watch'):
            if not self.channel.is_connected(player):
                self.channel.connect(player)
                self.show(player)
            else:
                self.tell_pre(player, "You're already watching this game!\n")
            handled = True

        elif primary in ('show', 'look', 'l'):
            self.show(player)
            handled = True

        elif primary in ('terminate', 'finish', 'flip'):
            self.terminate(player)
            handled = True

        elif primary in ('private',):
            self.bc_pre("^R%s^~ has turned the game ^cprivate^~.\n" % (player))
            self.private = True
            handled = True

        elif primary in ('public',):
            self.bc_pre("^R%s^~ has turned the game ^Cpublic^~.\n" % (player))
            self.private = False
            handled = True

        elif primary in ('change_state',):
            if not self.debug:
                self.tell_pre(player, "No switching states in production!\n")
            elif len(command_bits) != 2:
                self.tell_pre(player, "Invalid state to switch to.\n")
            else:
                self.state.set(command_bits[1].lower())
                self.bc_pre("^R%s^~ forced a state change to ^C%s^~.\n" % (player, self.state.get()))
            handled = True

        return handled