Saturday, November 19, 2005

Maru Batsu


I will try a small exercise to explore the growth and evolution of code, as it tries to follow my intent. I will also pay attention to what the code is trying to tell me, and I will respond to it as I go along.



I will also try to play by the rules of a specific Format:

  • Specify basic high level intent.
  • Write readable statements that specify that intent in terms of lower level intents.
  • Reality Check.
  • Simplify those statements.
  • Reality Check.
  • Look for repetitions and abstract to higher level intent.
  • Reality Check.
  • Repeat this process.
When I say intent, I refer to the intended purpose of the code. Intent should come before code and it answers the question: "what is this code going to do?". As intent gets stated more in detail, in terms of sub-intents, it also answers the questions: "how do I know that it has been done? what am I expecting out of it?"

Reality Checks are a way to ensure that all our intents still hold. When a reality check fails, it means that the meaning of our code, its conceptual consistency, somehow broke down, violating one or more of the original intents. Ideally intents are written in such a way that a failing reality check immediately pinpoints the broken intent.

When I talk about statements I am almost always talking about code written in such a way that it expresses directly its intention, without forcing us to dry run it in our head to simulate it and uncover its purpose.

You should also watch out for repetition in your code. Conceptual repetitions obfuscate meaning, showing a large variety of concepts and relationships, where only a few clearly stated concepts would suffice. Many repetitions can usually be abstracted by a single intent that can express all of them in a simpler statement.

The purpose of the exercise is to develop a small tic tac toe game, the Maru Batsu from the title.

Let's start.


Maru Batsu --- Installment I

I wonder.. how do I express the intents of user interaction? Or should I start expressing the intents of a lower level?

How do I express the user input? Will I make my intents dependent on the mode of user input? Click this button, write this text, etc.. ?

This difficulty pushes me to look for a more concise way of specifying behaviour, independently from the input mode. What I do is to specify the intent of the interaction.. giving a summary description: user_A_moves_to x,y

game = Tictactoe()

game.player_A_symbol.should_be "X" game.player_B_symbol.should_be "O"

game.clean!
game.state.should_be
TictactoeBoard(
["","",""],
["","",""],
["","",""]
)


game.player_A_ticks row_(1), col_(1)

game.state.should_be

TictactoeBoard(
["","", ""],
["","X",""],
["","", ""]
)
and on and on I continue until the end of the game.

I notice that the shape of my intent already implies an architecture:
  • Pure Game layer
  • Interaction layer
The interaction layer lays outside my intent, whereas the intent specifies the 'pure game' layer.

I also wonder if I should split the pure game layer into some concepts that seem to emerge from the intents:
  • an abstract/pure interaction layer
  • a game engine layer
  • a game board layer.
However, I do not feel the need to do this yet, even if the game board starts emerging due to the need to declare what kind of board position I am expecting. I trust that if this intuition was valid it will emerge again, so I don't even make a note of this.

What about the game ending? I will check if there is a winner at different stages using:
game.is_there_a_winner?.should_be true

game.is_there_a_winner?.should_be false
shall I check after every move? or only at the end of the game?

I am now checking after every move, but I start feeling that I am stating too many things at the same time.

I feel I should do more aimed scenarios, where I reason about different aspects of the game.

Yet, I continue, and I realize that I should also state that there are situations where illegal moves should be hadled, since there is nothing in the pure game layer, in its syntax that stops me from doing overlapping moves or having a player moving twice in a row.

I start adding statements like this:

expect_exception WrongPlayerException {
game.player_A_ticks row_(1), col_(1)
}

expect_exception PositionOccupiedException {
game.player_B_ticks row_(1), col_(1)

}

ok, I feel this is a bit too much for a single intent and I start reorganizing everything into more aimed scenarios.

As I am building the scenarios I try to not say things in more than one scenario, I try to imagine one scenario as a precondition for the following statements. I trust the previous intents, so I don't have to keep restating what I had stated there already.

I also get the titles of a few intents and I put them in order, as a small statement of intents, as if I were explaining the game to someone. And new intents come to my mind as I do that.

def all_intents
intent_of_setting_up_the_game_properly
intent_of_ticking_cells
intent_of_recognizing_a_winner
intent_of_spotting_moves_to_occupied_cells intent_of_spotting_moves_outside_the_board intent_of_spotting_when_moving_twice
end

I have also renamed some intents from names like:

intent_of_checking_the_game_is_setup_properly

later changing them to:

intent_of_setting_up_the_game_properly

where I do not mention checks or other actions external to the game, but I simply state whatever I want to do within the uniiverse of discourse of the game.

Friday, November 04, 2005

Toromaki

Toromaki 1
package org.codesushi;

import java.lang.reflect.*;
import java.util.Arrays;

/**
* @author codesushi
*/
public class SushiDebugProxy implements InvocationHandler {

private Object obj;

public static Object newInstance(Object obj) {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new SushiDebugProxy(obj));
}

private SushiDebugProxy(Object obj) {
this.obj = obj;
}

public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Object result;
try {
System.out.println("before method " + m.getName() + "(" + Arrays.asList(args) + ")");
result = m.invoke(obj, args);

} catch (InvocationTargetException e) {
throw e.getTargetException();

} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());

} finally {
System.out.println("after method " + m.getName() + "(" + Arrays.asList(args) + ")");
}
return result;
}
}