Closed herzbube closed 1 year ago
The following KVO-triggered updating logic in GoBoardPosition
must be replaced:
numberOfBoardPositions
needs to be adjustedcurrentBoardPosition
needs to be changed +1 if before the node was added it was referring to the leaf node in GoNodeModel
numberOfBoardPositions
needs to be adjustedThe following actors must be changed accordingly:
GoNodeModel
that adds a new node: appendNode:
.GoGame
methods play:
and pass
.GoNodeModel
has several methods to discard nodes, but all of them ultimately invoke the same primitive method: discardNodesFromIndex:
ChangeAndDiscardCommand
and DiscardAndPlayCommand
.To avoid similar issues with an inconsistent model state in the future, all KVO observing of GoBoardPosition
properties currentBoardPosition
and numberOfBoardPositions
should be replaced. Instead observers should react to notifications posted to the default notification centre, and the actors outlined in the previous comment should explicitly post those notifications when all Go model objects have received their correct state updates.
New notifications:
currentBoardPositionDidChange
numberOfBoardPositionsDidChange
GoBoardPosition
currently uses KVO to react to changes inGoNodeModel
and to update its propertiesnumberOfBoardPositions
andcurrentBoardPosition
. This "magic" is convenient, because clients that add or discard nodes to/fromGoNodeModel
do not need to also updateGoBoardPosition
. On the negative side, the updating logic triggered by KVO is difficult to understand and requires extensive documentation, because it involves assumptions about the behaviour of other parts of the code (at leastGoGame
,ChangeAndDiscardCommand
andDiscardAndPlayCommand
).After various changes that were required to support game variations and board setup in multiple nodes, one specific error case was now found to exist:
GoNode
methodmodifyBoard
is invoked only after the node is added toGoNodeModel
. Before the commit the order was reversed.GoNode
and calculated by themodifyBoard
method. In order for a Zobrist hash to be calculated its predecessor hash must be known. The predecessor hash is stored in a node's parent node, and that parent node is known only after a new node has been added to the game tree.GoBoardPosition
is already triggered when the new node is added toGoNodeModel
, i.e. before theGoNode
methodmodifyBoard
is invoked.GoBoardPosition
propertycurrentBoardPosition
are triggered, they now see Go model objects in the wrong state because theGoNode
methodmodifyBoard
has not been invoked yet.GoBoardPosition
's own KVO-triggered updating logic: The updating logic tries to set theGoGame
propertynextMoveColor
. For that to work theGoGame
propertysetupFirstMoveColor
must be up-to-date. However,setupFirstMoveColor
is set by theGoNode
methodmodifyBoard
(from withinGoNodeSetup
), and as we have seen that method has not been invoked yet. This means thatGoBoardPosition
's updating logic can easily set theGoGame
propertynextMoveColor
with the wrong value.After this analysis, it seems best to no longer use KVO in
GoBoardPosition
to react to changes inGoNodeModel
. Although this removes some convenience, it also removes a difficult to understand piece of code and therefore improves overall maintainability.