コーディングせずに設計できる人はすごい

表題どおり。Waterfall型で実装せずに設計するやり方って多いと思うのだが、良くそんなことできるなあと思う。今、UNOのプログラムを新たに作っている。UNOのルールは簡単だ。こんな感じ。これが仕様書といっても良い。ゲームのルールなので曖昧さのない上質な仕様書といえる。これでもこれを実装で試しながら作っていくっていうことを禁止されたらとてもじゃないけど作れない気がする。たとえば、今のコアでゲーム実行するメイン流れのコードは以下のようだ。

    public boolean start(){;
    	log.info("Play Start!");
        do{
            Card  lastDiscardedCard = discardPile.getLastCard();
            Card  drawnCard = null;
            Card  receivedCard = null;
        	log.debug("lastDiscardedCard : " + lastDiscardedCard);
            currentPlayer = players.nextPlayer(lastDiscardedCard);
            boolean isReceived = false;
            while(true) {
            	drawnCard  = currentPlayer.drawCard(lastDiscardedCard);
            	if (drawnCard != null){
            		if (lastDiscardedCard.isAceptedAsNextDrawnCard(drawnCard)){
            			if ((WildCard.class.isAssignableFrom(drawnCard.getClass())) 
            					&& (!((WildCard)drawnCard).isColorDeclared())){
            				Color  color = currentPlayer.declareColor();
            				((WildCard)drawnCard).setColor(color);
            				log.debug(currentPlayer + " declares this card is " + color);
            			}
            			currentPlayer.handCard(discardPile, drawnCard);
            			break;
            		}
            	} else {
                	// If current player can't draw card, he must receive the all hands card.
            		extraReceivedCardNumber = discardPile.getExtraReceiveCardNumber();
            		if (extraReceivedCardNumber == 0) extraReceivedCardNumber = 1;
            		log.debug("extraReceiveCardNumber: " + extraReceivedCardNumber);
                	for (int i = 0; i < extraReceivedCardNumber ;i++){
                		receivedCard =  drawPile.getTopCard();
                		drawPile.handCard(currentPlayer,receivedCard);
            			isReceived = true;
            			discardPile.resetExtraReceiveCardNumber();
                		log.debug(currentPlayer +" receives " + receivedCard);
                	}
            		if ((isReceived) || (extraReceivedCardNumber != 1)) break;
                }
            };
            log.debug(currentPlayer + " "+ currentPlayer.getCardFolder().toString());
            log.debug("Draw " + drawPile.getCardFolder().toString());
            log.debug("Discard " + discardPile.getCardFolder().toString()+ "\n");
        } while(currentPlayer.isPlayerHandsOver());
        return false;
    }

こんな感じなのだが、このコアのメソッドとその先にあるクラスのコードを作るだけでも20時間以上かかっているが、これを実装せずに紙による設計だけではとてもじゃないけどムリ。頭の限界になる。もし、そうしろといわれたら、多分、きれいな重複のない拡張性の高いコードを書くことに頭を絞るというよりは、仕様の書き下しのようなコードにするしかないだろう。そうすると非常に無駄で長いコードになると思う。保守性も落ちるだろう。それでもそうやっている今のWaterfall型でやっている人はすごいなあと思う。

0    [main] DEBUG org.sakata.UNO.core.Play  - Player Dummy0 DB 5R 9G 9R SR DY 8Y 
2    [main] DEBUG org.sakata.UNO.core.Play  - Player Dummy1 5B 1G 5Y SY W4 8R 6B 
2    [main] DEBUG org.sakata.UNO.core.Play  - Player Dummy2 DR RB 7R 9R SG 4B 8B 
3    [main] DEBUG org.sakata.UNO.core.Play  - Player Dummy3 1B 5R 4Y 1B 5G 0G SB 
5    [main] DEBUG org.sakata.UNO.core.Play  - currentERCN: 0
5    [main] DEBUG org.sakata.UNO.core.Play  - Draw 6Y 3G 7R 8B DG RG 4G 1Y W SG 7G DB 2G 3B 4Y W4 RY 7Y 9B W4 W 3G 3R SR DY 0B RY W4 9Y 6G 3Y 1R SB 5B RG RR 2Y 6R 4B 7Y 6Y 9G 9B 2B SY RB RR DR 3B 7B 8G 4R 1G 6G 2R 9Y 1Y 6R 6B 0R 5Y 4R 8R 3R 2R 2G 8G 2Y 7G W W 2B 4G DG 5G 7B 1R 3Y 0Y 
5    [main] DEBUG org.sakata.UNO.core.Play  - Discard 8Y 

5    [main] INFO  org.sakata.UNO.core.Play  - Play Start!
6    [main] DEBUG org.sakata.UNO.core.Play  - lastDiscardedCard : 8Y
13   [main] DEBUG org.sakata.UNO.core.Players  - Player: Dummy1 turn: 1
13   [main] DEBUG org.sakata.UNO.core.player.DummyPlayer  - Dummy1 drawnCard : 5Y
13   [main] DEBUG org.sakata.UNO.core.Play  - currentERCN: 0
13   [main] DEBUG org.sakata.UNO.core.Play  - Dummy1 5B 1G SY W4 8R 6B 
14   [main] DEBUG org.sakata.UNO.core.Play  - Draw 6Y 3G 7R 8B DG RG 4G 1Y W SG 7G DB 2G 3B 4Y W4 RY 7Y 9B W4 W 3G 3R SR DY 0B RY W4 9Y 6G 3Y 1R SB 5B RG RR 2Y 6R 4B 7Y 6Y 9G 9B 2B SY RB RR DR 3B 7B 8G 4R 1G 6G 2R 9Y 1Y 6R 6B 0R 5Y 4R 8R 3R 2R 2G 8G 2Y 7G W W 2B 4G DG 5G 7B 1R 3Y 0Y 
14   [main] DEBUG org.sakata.UNO.core.Play  - Discard 8Y 5Y 

14   [main] DEBUG org.sakata.UNO.core.Play  - lastDiscardedCard : 5Y
14   [main] DEBUG org.sakata.UNO.core.Players  - Player: Dummy2 turn: 1
14   [main] DEBUG org.sakata.UNO.core.player.DummyPlayer  - Dummy2 drawnCard : null
14   [main] DEBUG org.sakata.UNO.core.Play  - extraReceiveCardNumber: 1
14   [main] DEBUG org.sakata.UNO.core.Play  - Dummy2 receives 6Y
14   [main] DEBUG org.sakata.UNO.core.Play  - Dummy2 DR RB 7R 9R SG 4B 8B 6Y 
14   [main] DEBUG org.sakata.UNO.core.Play  - Draw 3G 7R 8B DG RG 4G 1Y W SG 7G DB 2G 3B 4Y W4 RY 7Y 9B W4 W 3G 3R SR DY 0B RY W4 9Y 6G 3Y 1R SB 5B RG RR 2Y 6R 4B 7Y 6Y 9G 9B 2B SY RB RR DR 3B 7B 8G 4R 1G 6G 2R 9Y 1Y 6R 6B 0R 5Y 4R 8R 3R 2R 2G 8G 2Y 7G W W 2B 4G DG 5G 7B 1R 3Y 0Y 
14   [main] DEBUG org.sakata.UNO.core.Play  - Discard 8Y 5Y 

14   [main] DEBUG org.sakata.UNO.core.Play  - lastDiscardedCard : 5Y
14   [main] DEBUG org.sakata.UNO.core.Players  - Player: Dummy3 turn: 1
14   [main] DEBUG org.sakata.UNO.core.player.DummyPlayer  - Dummy3 drawnCard : 5R
14   [main] DEBUG org.sakata.UNO.core.Play  - currentERCN: 0
14   [main] DEBUG org.sakata.UNO.core.Play  - Dummy3 1B 4Y 1B 5G 0G SB 
15   [main] DEBUG org.sakata.UNO.core.Play  - Draw 3G 7R 8B DG RG 4G 1Y W SG 7G DB 2G 3B 4Y W4 RY 7Y 9B W4 W 3G 3R SR DY 0B RY W4 9Y 6G 3Y 1R SB 5B RG RR 2Y 6R 4B 7Y 6Y 9G 9B 2B SY RB RR DR 3B 7B 8G 4R 1G 6G 2R 9Y 1Y 6R 6B 0R 5Y 4R 8R 3R 2R 2G 8G 2Y 7G W W 2B 4G DG 5G 7B 1R 3Y 0Y 
15   [main] DEBUG org.sakata.UNO.core.Play  - Discard 8Y 5Y 5R 

今動いているログ。今はダミーのAIプレイヤが自動的に実行しているが、今後はUIパッケージを通じて人がWebでできる形のやつとか、AIプレイヤ作成SDKとかネットワークで複数プレイヤが対戦ができるようにする予定。そのためのアーキテクチャも今のところ完璧のはずだ。