Maru Batsu II
This is the follow up to the previous Maru Batsu post.
In the meantime I have put the intent project on rubyforge, and I am working hard to make it generally usable.
A few things have changed in the way I define the intents and I believe that I have made the system more complete, gradual to get into, easier to use and more structured that you can see in the Maru Batsu examples.
I invite you to follow the progress of the Intent project on Liquid Development.
Maru Batsu --- Installment II
In the previous installment I was defining the intents of Maru Batsu, the tic tac toe game.
In an intent I simply make statements about the game.
I also start realizing that lots of setup code gets repeated and I wonder if I should abstract it out.. but I let it there for the time being, until it gets more bulky and I really feel the need to collapse it into an abstraction.
I start writing up intent_of_recognizing_a_winner but I soon realize that to recognise a winner I will need to check a given board configuration. How would I define that board? I could create an empty board and play out a whole game on it, checking for a winner as I go along move after move, finally finding a winner in the end.
This seems to be a bit long winded, especially if i want to represent many winning board configurations. What I need is to define a mid-game state for the tic tac toe game. I opt for the possibility of injecting an externally defined board in the game and be able to start playing from there. This way I will simply have to specify a certain board and check if it there is a winning configuration for a certain player.
I think that this is a nice example of how a 'testing' requirement, an intent - as I like to call it - shapes code along Dependency Injection patterns.
Injecting a board will also require to either state - or infer - which player's turn it is, check if the board is well-formed, etc..
All of this feels a bit too much for such a small game, so I decide to write out the whole game and later decide if I really need to evolve it there.
Do I really need those constraints? Why should I put these constraints if they are already clear and obvious in the mind of the developers that will build on top of Maru Batsu? Do I really need explicit knowledge where convention-based knowledge and reasonable polite behaviour will do? Probably not. I stick with postponing the choice.
I start writing up the intent, specifying every single step
As I get here I realize that I am not able to follow the evolution of the state of the board. Maybe I really need a different way after all. Yet I am going to continue until the end and refactor it later. After making all moves I will simply make a statement on the configuration of the board. I do not need it, but it will serve the purpose of communicating better with the readers of the intent.
I think this is a good case of breaking the symmetry - in theory you don't need it because it's superfluous, but adding it makes things somehow easier on the eye.
It works and it shows a whole game, but there are several things I feel uneasy about. For a start this looks more like a use case than an intent, a simple statement about the game. I also feel uneasy about
I feel there is some redundant information there, but I cannot put my finger on it yet. Since I am not sure on the illness, I also don't feel sure about the cure. I believe that most of the times in development there aren't any lost opportunities. If it's ugly, if it is a problem, if it gave me discomfort now, it will sick out later as well and at that point I might have some more information to deal with it.
I also notice that with those two statements I have described what a draw is, but I haven't played out any winning scenarios yet.
I think a little about copying and pasting this intent and making the modifications needed to define a winning board. I am also thinking about creating a subroutine that takes the game through a number of moves so that I can recreate the game state on demand and then apply the extra moves and my statements of intent.
I am definetly not satisfied. The board injection solution becomes more and more attractive. I go for it. I don't write the code, I just state that I should be able to set a board. I replace the first part of the intent with the following
This is much much easier on the eye, and it doesn't force me to painstakingly specify every move from the beginning. Of course, I could put a wrong board there, and I am not checking if the board is well formed (number of noughts equal to number of crosses minus one, not more than one winning configuration with no common cell, etc..). I decide to not care. I am writing the intents, and I am not going to state impossible configurations.
However it seems a bit verbose and redundant to keep stating that my board is indeed the board that I have injected. I should create a further intent to check that board.set_up is working correctly. After having stated that intent in one place I will remove the extra should bes in the code, confident that this fresh intent will catch any misbehaving code. Nice, but once again I'll do it later when I really need it.
I clone the intent into a number of sibling intents to check different board configurations.
The use of a variable to avoid the repetition of the board definition makes the code clearer, while still stating that I should get what I have set.
There are a few stylistic points that annoy me. For example, I see
and I realize it is more legible as
This way you can actually read it out aloud.
I also don't like this form much:
I have two methods, one for player_A and one for player_B that are doing essentially the same thing. I would prefer something like:
these are more notes for further refactoring. The code looks quite clean, but I already have quite a backlog of candidates for refactoring.
In the meantime I have added several intents. This is a bit long winded. Be patient and skim over them. If you cannot skim over them while getting a sense of what they are stating, then I have done something wrong and I have not been shaping code after language.
wow! done it. Thanks for keeping up until here.
The intents are not too bad, but there is much more that can be done, even if it might not be clear at this stage yet.
Also notice how much knowledge we have produced about the game, without writing a single executable statement yet. Are we coding or are we doing analysis? Are we analysing or are we writing up specifications? Are these specifications or are they explorations?
The common language of development starts breaking down and becoming irrelevant as we continue along our journey.
In the meantime I have put the intent project on rubyforge, and I am working hard to make it generally usable.
A few things have changed in the way I define the intents and I believe that I have made the system more complete, gradual to get into, easier to use and more structured that you can see in the Maru Batsu examples.
I invite you to follow the progress of the Intent project on Liquid Development.
Maru Batsu --- Installment II
In the previous installment I was defining the intents of Maru Batsu, the tic tac toe game.
intent_of_setting_up_the_game_properly
In an intent I simply make statements about the game.
I also start realizing that lots of setup code gets repeated and I wonder if I should abstract it out.. but I let it there for the time being, until it gets more bulky and I really feel the need to collapse it into an abstraction.
I start writing up intent_of_recognizing_a_winner but I soon realize that to recognise a winner I will need to check a given board configuration. How would I define that board? I could create an empty board and play out a whole game on it, checking for a winner as I go along move after move, finally finding a winner in the end.
This seems to be a bit long winded, especially if i want to represent many winning board configurations. What I need is to define a mid-game state for the tic tac toe game. I opt for the possibility of injecting an externally defined board in the game and be able to start playing from there. This way I will simply have to specify a certain board and check if it there is a winning configuration for a certain player.
I think that this is a nice example of how a 'testing' requirement, an intent - as I like to call it - shapes code along Dependency Injection patterns.
Injecting a board will also require to either state - or infer - which player's turn it is, check if the board is well-formed, etc..
All of this feels a bit too much for such a small game, so I decide to write out the whole game and later decide if I really need to evolve it there.
Do I really need those constraints? Why should I put these constraints if they are already clear and obvious in the mind of the developers that will build on top of Maru Batsu? Do I really need explicit knowledge where convention-based knowledge and reasonable polite behaviour will do? Probably not. I stick with postponing the choice.
I start writing up the intent, specifying every single step
def intent_of_recognizing_a_winner
game = Tictactoe()
game.clean!
game.player_A_ticks row_(1), col_(1)
game.player_B_ticks row_(0), col_(2)
game.player_A_ticks row_(1), col_(2)
As I get here I realize that I am not able to follow the evolution of the state of the board. Maybe I really need a different way after all. Yet I am going to continue until the end and refactor it later. After making all moves I will simply make a statement on the configuration of the board. I do not need it, but it will serve the purpose of communicating better with the readers of the intent.
I think this is a good case of breaking the symmetry - in theory you don't need it because it's superfluous, but adding it makes things somehow easier on the eye.
def intent_of_recognizing_a_winner
game = Tictactoe()
game.clean!
game.player_A_ticks row_(1), col_(1)
game.player_B_ticks row_(0), col_(2)
game.player_A_ticks row_(1), col_(2)
game.player_B_ticks row_(0), col_(2)
game.player_A_ticks row_(1), col_(2)
game.player_B_ticks row_(1), col_(0)
game.player_A_ticks row_(0), col_(0)
game.player_B_ticks row_(2), col_(2)
game.player_A_ticks row_(0), col_(1)
game.player_B_ticks row_(2), col_(1)
game.state.should_be TictactoeBoard(
["X","X","O"],
["O","X","X"],
["", "O","O"]
)
game.is_game_complete?.should_be false
game.is_there_a_winner?.should_be false
game.player_A_ticks row_(2), col_(0)
game.state.should_be TictactoeBoard(
["X", "X","O"],
["O", "X","X"],
["X", "O","O"]
)
game.is_game_complete?.should_be true
game.is_there_a_winner?.should_be false
end
It works and it shows a whole game, but there are several things I feel uneasy about. For a start this looks more like a use case than an intent, a simple statement about the game. I also feel uneasy about
game.is_game_complete?.should_be true
game.is_there_a_winner?.should_be false
I feel there is some redundant information there, but I cannot put my finger on it yet. Since I am not sure on the illness, I also don't feel sure about the cure. I believe that most of the times in development there aren't any lost opportunities. If it's ugly, if it is a problem, if it gave me discomfort now, it will sick out later as well and at that point I might have some more information to deal with it.
I also notice that with those two statements I have described what a draw is, but I haven't played out any winning scenarios yet.
I think a little about copying and pasting this intent and making the modifications needed to define a winning board. I am also thinking about creating a subroutine that takes the game through a number of moves so that I can recreate the game state on demand and then apply the extra moves and my statements of intent.
I am definetly not satisfied. The board injection solution becomes more and more attractive. I go for it. I don't write the code, I just state that I should be able to set a board. I replace the first part of the intent with the following
def intent_of_recognizing_a_winner
game = Tictactoe()
game.clean!
game.set_board TictactoeBoard(
["X","X","O"],
["O","X","X"],
["", "O","O"]
)
game.state.should_be TictactoeBoard(
["X","X","O"],
["O","X","X"],
["", "O","O"]
)
...
This is much much easier on the eye, and it doesn't force me to painstakingly specify every move from the beginning. Of course, I could put a wrong board there, and I am not checking if the board is well formed (number of noughts equal to number of crosses minus one, not more than one winning configuration with no common cell, etc..). I decide to not care. I am writing the intents, and I am not going to state impossible configurations.
However it seems a bit verbose and redundant to keep stating that my board is indeed the board that I have injected. I should create a further intent to check that board.set_up is working correctly. After having stated that intent in one place I will remove the extra should bes in the code, confident that this fresh intent will catch any misbehaving code. Nice, but once again I'll do it later when I really need it.
I clone the intent into a number of sibling intents to check different board configurations.
def intent_of_setting_up_the_game_in_the_middle
game = Tictactoe()
mid_game_board = TictactoeBoard(
["X","X","O"],
["O","X","X"],
["", "O","O"]
)
game.set_board mid_game_board
game.state.should_be mid_game_board
expect true, game.player_A_turn?
expect false, game.player_B_turn?
game.is_there_a_winner?.should_be false
game.is_game_complete?.should_be false
end
The use of a variable to avoid the repetition of the board definition makes the code clearer, while still stating that I should get what I have set.
There are a few stylistic points that annoy me. For example, I see
game.set_board mid_game_board
and I realize it is more legible as
game.set_board_to mid_game_board
This way you can actually read it out aloud.
I also don't like this form much:
game.is_player_A_the_winner?.should_be false
I have two methods, one for player_A and one for player_B that are doing essentially the same thing. I would prefer something like:
game.winner?.should_be game.player_A
game.winner?.should_be game.player_B
game.winner?.should_be nil
these are more notes for further refactoring. The code looks quite clean, but I already have quite a backlog of candidates for refactoring.
In the meantime I have added several intents. This is a bit long winded. Be patient and skim over them. If you cannot skim over them while getting a sense of what they are stating, then I have done something wrong and I have not been shaping code after language.
def intent_of_setting_up_the_game_properly
game = Tictactoe()
game.player_A_symbol.should_be "X"
game.player_B_symbol.should_be "O"
game.clean!
game.state.should_be TictactoeBoard(
["","",""],
["","",""],
["","",""]
)
expect true, game.player_A_turn?
expect false, game.player_B_turn?
game.is_there_a_winner?.should_be false
game.is_game_complete?.should_be false
end
def intent_of_setting_up_the_game_in_the_beginning_1
game = Tictactoe()
mid_game_board = TictactoeBoard(
["","","" ],
["","","X"],
["","","" ]
)
game.set_board_to mid_game_board
game.state.should_be mid_game_board
expect false, game.player_A_turn?
expect true, game.player_B_turn?
game.is_game_complete?.should_be false
game.is_there_a_winner?.should_be false
game.is_player_A_the_winner?.should_be false
game.is_player_B_the_winner?.should_be false
end
def intent_of_setting_up_the_game_in_the_middle_1
game = Tictactoe()
mid_game_board = TictactoeBoard(
["X","X","O"],
["O","X","X"],
["", "O","O"]
)
game.set_board_to mid_game_board
game.state.should_be mid_game_board
expect true, game.player_A_turn?
expect false, game.player_B_turn?
game.is_game_complete?.should_be false
game.is_there_a_winner?.should_be false
game.is_player_A_the_winner?.should_be false
game.is_player_B_the_winner?.should_be false
end
def intent_of_setting_up_the_game_in_the_middle_2
game = Tictactoe()
mid_game_board = TictactoeBoard(
["X","X","O"],
["O","X","X"],
["", "", "O"]
)
game.set_board_to mid_game_board
game.state.should_be mid_game_board
expect false, game.player_A_turn?
expect true, game.player_B_turn?
game.is_game_complete?.should_be false
game.is_there_a_winner?.should_be false
game.is_player_A_the_winner?.should_be false
game.is_player_B_the_winner?.should_be false
end
def intent_of_setting_up_the_game_in_the_end_with_a_draw
game = Tictactoe()
end_game_board = TictactoeBoard(
["X","X","O"],
["O","X","X"],
["X","O","O"]
)
game.set_board_to end_game_board
game.state.should_be end_game_board
expect false, game.player_A_turn?
expect false, game.player_B_turn?
game.is_game_complete?.should_be true
game.is_there_a_winner?.should_be false
game.is_player_A_the_winner?.should_be false
game.is_player_B_the_winner?.should_be false
end
def intent_of_setting_up_the_game_in_the_end_with_player_A_winning
game = Tictactoe()
end_game_board = TictactoeBoard(
["X","X","O"],
["O","X","X"],
["O","X","O"]
)
game.set_board_to end_game_board
game.state.should_be end_game_board
expect false, game.player_A_turn?
expect false, game.player_B_turn?
game.is_game_complete?.should_be true
game.is_there_a_winner?.should_be true
game.is_player_A_the_winner?.should_be true
game.is_player_B_the_winner?.should_be false
end
wow! done it. Thanks for keeping up until here.
The intents are not too bad, but there is much more that can be done, even if it might not be clear at this stage yet.
Also notice how much knowledge we have produced about the game, without writing a single executable statement yet. Are we coding or are we doing analysis? Are we analysing or are we writing up specifications? Are these specifications or are they explorations?
The common language of development starts breaking down and becoming irrelevant as we continue along our journey.
0 Comments:
Post a Comment
<< Home