def finalize_turn(plane, matrix, comm, image, bdict, slabel): """This function, finalize_turn, is called at the end of every turn, regardless of ACCEPT or PASS. Note that it is necessary to update the graphics even if the player PASSed: It sometimes happens that a player can capture by simply PASSing, usually due to a miscalculation by the opponent, and the capture must be depicted.""" global GcountdownID if GcountdownID is not None: GLib.source_remove(GcountdownID) GcountdownID = None # guarantee that no attempt is made to manually disconnect countdown_timeout later -- happened previous line countdown_timeout.timer = TURNLIMITCOUNT # reset the Timer preevolvedstate = matrix.create_state( "R" ) # need to send opponent state prior to evolution, for Replay and Capture purposes if initialize_turn.numturns >= 2: # "real" turn numcaptured = matrix.implement_gains(comm.get_newest_data_mutable( )) # note: GerGorMatrix.implement_gains filters by "yes evolved" matrix.evolve(numcaptured) GerGorPaintable( matrix.create_state(), [d for d in comm.get_newest_data() if is_current(d)]).paint( image, plane) # note: comm's data was modified above matrix.refresh_GtkWidgets( ) # show the post-evolution numbers on the matrix matrix.set_replay_state_to_current( ) # our state at end of our current turn, *before* opponent next captures (need for REPLAY during our next turn) comm.send(preevolvedstate + matrix.create_state() ) # opponent needs to know pre- and post-evolution states if len( matrix ) == 1: # important to do comm.send before this: opponent needs to depict Adrenaline matrix.adrenalize(False) slabel.set_label(STATUSOPPTURN) bdict["Quit"].set_sensitive( True) # always allow QUIT while waiting for opponent global GstandbyID # may need to manually disconnect later -- record ID GstandbyID = GLib.timeout_add( COMMTIMEOUTFREQ, standby_timeout, plane, matrix, comm, image, bdict, slabel) # wait for opponent to finish turn and send data
def replay_timeout(plane, matrix, comm, image, animation, bdict, slabel): # can change game's state from "Replaying Opponent" to "Waiting for choice (Autopilot *)" # deliberately disable: nothing # deliberately enable: REPLAY, PASS, AUTO, QUIT, ENTRIES/SHOW # can also change game's state from "Replaying Opponent" to "Loss" # deliberately disable: nothing # deliberately enable: REPLAY, QUIT if animation: # GerGorAnimation.__bool__() indicates whether more frames are available GerGorPaintable( matrix.get_replay_state(), animation.next_frame()).paint( image, plane) # arguments already filtered in on_replay_button_press return True # tell GTK+ to continue running this timeout until there are no more frames GerGorPaintable(matrix.create_state(), [d for d in comm.get_newest_data() if is_current(d)]).paint( image, plane) global Ggameover if Ggameover: # player can REPLAY even after LOSS -- reprint LOSS message (if WIN then REPLAY disabled and control never reaches here) slabel.set_label(STATUSLOSS) bdict["Replay"].set_sensitive(True) else: if not bdict["Autopilot"].get_active(): matrix.enable_GtkWidgets() # this enables ENTRIES/SHOW bdict["Pass"].set_sensitive(True) bdict["Replay"].set_sensitive(True) bdict["Autopilot"].set_sensitive(True) slabel.set_label(STATUSBEGIN) bdict["Quit"].set_sensitive( True ) # disabled QUIT for animation, enable now that animation is finished countdown_timeout.isanimating = False # indicates to countdown_timeout that Timer *is* allowed to decrement (replay animation is finished) global GreplayID GreplayID = None return False # tell GTK+ to discontinue this timeout -- replay animation is finished
def on_show_button_press(self, row, plane, matrix, comm, image): # can only change game's state from "Waiting for choice (Autopilot *)" to "Waiting for choice (Autopilot same *)" # deliberately disable: nothing (but unpress any other SHOW) # deliberately enable: nothing if self is not on_show_button_press.previously_selected: if on_show_button_press.previously_selected is not None: # first use of SHOW this turn on_show_button_press.previously_selected.set_active( False ) # note: this causes on_show_button_press.previously_selected to receive both "toggled" and "clicked" signals on_show_button_press.previously_selected = self s = GerGorPaintable(matrix.create_state(), [d for d in comm.get_newest_data() if is_current(d)]) s.distinguish(matrix.uncaptured_position(row), self.get_active()) s.paint(image, plane)
def animation_timeout(plane, matrix, comm, image, animation, bdict, slabel): # can only change game's state from "Animating self" to "Waiting for Opponent" # deliberately disable: nothing # deliberately enable: QUIT if animation: # GerGorAnimation.__bool__() indicates whether more frames are available GerGorPaintable(animation.next_frame(), [d for d in comm.get_newest_data() if is_current(d) ]).paint(image, plane) return True # tell GTK+ to continue running this timeout until there are no more frames # disconnect animation_timeout early because of finalize_turn starts another timeout? finalize_turn(plane, matrix, comm, image, bdict, slabel) # this will enable QUIT countdown_timeout.isanimating = False global GanimationID GanimationID = None return False # tell GTK+ to discontinue this timeout -- end-of-turn animation is finished
def implement_gains(self, awaystate): """This method, .implement_gains, accepts a LIST of dictionaries containing raw data describing the opponent's discs and checks whether any of theirs were captured by any of ours. Each captured disc is removed from the MUTABLE parameter awaystate. This is deliberate: this parameter is supplied by GerGorComm and is always supposed to represent the most recently known state of the opponent, so discs that are known to be captured should indeed be removed. It is called by finalize_turn. The return is the number of opponent's discs that we captured.""" todelete = [ ] # don't actually delete in the loop -- will wreck the iteration for (i, dd) in enumerate(awaystate): if is_current(dd): for r in filter( bool, self.Rows): # only uncaptured discs can capture! if check_capture(dd["center"], dd["radius"], r.get_center(), r.get_radius(), r.is_adrenalized()): todelete.append(i) break # if dd captured by one r, no need to check if also captured by other r -- terminate inner loop and check next dd todelete.reverse( ) # delete from high to low, otherwise earlier deletions make remaining indices wrong for i in todelete: del awaystate[i] # deliberate: modifies mutable parameter return len(todelete) # number of captures
def initialize_turn(plane, matrix, comm, image, bdict, slabel): """This function, initialize_turn, is called at the beginning of every turn, immediately after standby_timeout detects data sent from opponent.""" initialize_turn.numturns += 1 comm.receive() if initialize_turn.numturns < 2: # still exchanging info, not a "real" turn if not comm.is_first( ): # if we play second, nice to see the initial positions while waiting for opponent to finish first "real" turn GerGorPaintable( matrix.create_state(), [d for d in comm.get_newest_data() if is_current(d)]).paint( image, plane) bdict["Pass"].clicked( ) # simulate that both players PASS their first turn, to be sure data is shared return # this turn is for "setup", don't continue turn global Ggameover if not comm.get_newest_data( ): # opponent's data is empty, which indicates WIN for us # changes game's state from "Waiting for Opponent" to "Win" # deliberately disable: nothing # deliberately enable: nothing Ggameover = True slabel.set_label(STATUSWIN) return # WIN, no need to further initialize or do anything else if initialize_turn.numturns >= ( 3 if comm.is_first() else 2 ): # captures only allowed if opponent played (1st turn if 2nd player but 2nd turn if first player) matrix.implement_losses(comm.get_newest_data( )) # note: GerGorMatrix.implement_losses filters by "not evolved" matrix.set_previous_focus_to_current( ) # after turn begins, UNDO means to restore whatever state matrix is in now if (len(matrix) == 1) and (random.random() < MATRIXADRENALINERATE): matrix.adrenalize() matrix.refresh_GtkWidgets( ) # update the matrix GUI to express the new situation if not matrix: # GerGorMatrix.__bool__() indicates whether any rows are uncaptured... if not, LOSS Ggameover = True bdict["Autopilot"].set_active( False ) # unpress AUTO, merely for appearance (note: this causes bdict["Autopilot"] to receive both "clicked" and "toggled" signals) slabel.set_label(STATUSLOSS) # change to GtkMessageDialog? comm.send( None) # when opponent receives this "stub" data, will trigger WIN # deliberate: no return (want to see opponent's turn even if LOSS) if initialize_turn.numturns >= ( 3 if comm.is_first() else 2 ): # first "real" turn *for which there is something to replay* (1st turn if 2nd player but 2nd turn if first player) # changes game's state from "Waiting for Opponent" to "Replaying Opponent" # deliberately disable: QUIT # deliberately enable: nothing bdict["Replay"].clicked() # this will disable QUIT else: # control only reaches here if we play first *and* this is our first "real" turn (don't do "full" replay) GerGorPaintable(matrix.create_state(), [d for d in comm.get_newest_data() if is_current(d) ]).paint(image, plane) matrix.enable_GtkWidgets() # this enables ENTRIES/SHOW # don't enable Replay on this very special turn bdict["Pass"].set_sensitive(True) bdict["Autopilot"].set_sensitive(True) bdict["Quit"].set_sensitive(True) slabel.set_label(STATUSBEGIN) if not Ggameover: # if LOSS then no reason to start Timer # changes game's state from "Waiting for Opponent" to "Waiting for Choice (Autopilot *)" # deliberately disable: nothing # deliberately enable: REPLAY, PASS, AUTO, ENTRIES/SHOW (done indirectly by previous if/else clause) global GcountdownID # may need to manually disconnect later -- record ID GcountdownID = GLib.timeout_add(TURNLIMITFREQ, countdown_timeout, matrix, comm, bdict, slabel)