Skip to content

Commit

Permalink
Turnを復活、初期化メソッドGreen #55
Browse files Browse the repository at this point in the history
  • Loading branch information
ebinase committed May 19, 2022
1 parent 4fe3c7e commit 9139a9b
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 98 deletions.
94 changes: 94 additions & 0 deletions packages/Models/Othello/Othello/Turn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Packages\Models\Othello\Othello;

use Packages\Models\Othello\Board\Board;
use Packages\Models\Othello\Board\Color\Color;
use Packages\Models\Othello\Board\Position\Position;

class Turn
{
private function __construct(
public readonly int $turnNumber,
public readonly Color $playableColor,
public readonly Board $board,
)
{
if ($turnNumber <= 0) {
throw new \InvalidArgumentException();
}
}

public static function init(): Turn
{
return new Turn(
turnNumber: 1,
playableColor: Color::white(),
board: Board::init(),
);
}

public static function make(int $turnNumber, Color $playableColor, Board $board): Turn
{
return new Turn(
turnNumber: $turnNumber,
playableColor: $playableColor,
board: $board,
);
}

/**
* 次のターンへ
* ターン数:+1
* 色:反対の色へ
* 盤面:コマを置いて更新。スキップの場合は現在のものをそのまま設定
*/
public function next(?Position $position = null): Turn
{
// ゲームが終了している場合
if ($this->finishedLastTurn()) throw new \RuntimeException();
// ゲームが続行不能な場合
if (!$this->isContinuable()) throw new \RuntimeException();

if ($this->mustSkip()) {

}

// コマを置くことができる場合、盤面を更新してスキップカウントをリセット
// 必須チェック
if (!isset($position)) throw new \Exception('コマを置くことができるマスがある場合、スキップはできません。');
// 指定された場所にコマを置くことができるか確認
if (!$this->board->isValid($position, $this->playableColor)) throw new \Exception('指定された場所に置くことができません。');

return new Turn(
$this->turnNumber + 1,
$this->playableColor->opposite(),
$this->board->update($position, $this->playableColor),
);
}

public function skip()
{
return new Turn(
$this->turnNumber + 1,
$this->playableColor->opposite(),
$this->board,
);
}

/**
* 最終ターンが終了しているか(=ゲームが正常終了しているか)判定
* @return bool
*/
public function finishedLastTurn(): bool
{
return $this->board->isFulfilled();
}

public function mustSkip(): bool
{
return !$this->board->hasPlayablePosition($this->playableColor);
}


}
132 changes: 34 additions & 98 deletions tests/Feature/Models/Othello/Turn/TurnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Packages\Models\Othello\Board\Board;
use Packages\Models\Othello\Board\Color\Color;
use Packages\Models\Othello\Board\Position\Position;
use Packages\Models\Othello\Othello\Othello;
use Packages\Models\Othello\Othello\Turn;
use Tests\TestCase;

class TurnTest extends TestCase
Expand All @@ -15,110 +15,61 @@ class TurnTest extends TestCase
public function ターン初期化()
{
// given:

// when:
$turn = Othello::init();
$turn = Turn::init();

// then:
self::assertSame(1, $turn->getTurnNumber(), 'ターン数は1から始まる');
self::assertSame(true, Color::white()->equals($turn->getPlayableColor()), '白プレイヤーが先攻');
self::assertSame(true, Board::init()->equals($turn->getBoard()), '初期盤面チェック');
self::assertSame(0, $turn->getSkipCount(), 'スキップカウントは0');
self::assertSame(1, $turn->turnNumber, 'ターン数は1から始まる');
self::assertSame(true, Color::white()->equals($turn->playableColor), '白プレイヤーが先攻');
self::assertSame(true, Board::init()->equals($turn->board), '初期盤面チェック');
}

// ---------------------------------------
// コマを置く
// ---------------------------------------

/** @test */
public function ターン更新()
{
// given:
$turn = Othello::init(); // 1ターン目
$turn = Turn::init(); // 1ターン目
$move = Position::make([4, 6]); // 先行プレイヤーの1ターン目の指した場所
// when:
$next = $turn->next($move); // ターンを進める
// then:
// 2ターン目の盤面
$boardAtSecondTurn = Board::init()->update($move, Color::white());
self::assertSame(2, $next->getTurnNumber(), 'ターン数は2');
self::assertSame(true, Color::Black()->equals($next->getPlayableColor()), '後攻のプレイヤーに交代');
self::assertSame(true, $boardAtSecondTurn->equals($next->getBoard()), '盤面更新');
self::assertSame(0, $next->getSkipCount(), 'スキップカウントは0');
self::assertSame(2, $next->turnNumber, 'ターン数は2');
self::assertSame(true, Color::Black()->equals($next->playableColor), '後攻のプレイヤーに交代');
self::assertSame(true, $boardAtSecondTurn->equals($next->board), '盤面更新');
}

/** @test */
public function おけない場所を指定された時は例外を出す()
{
// given:
$turn = Othello::init(); // 1ターン目
$turn = Turn::init(); // 1ターン目
// when:
$move = Position::make([1, 1]); // おけない場所
// then:
$this->expectException(\Exception::class);
$turn->next($move); // ターンを進める
}

// ---------------------------------------
// 終了条件系
// ---------------------------------------
/** @test */
public function 盤面に空いているマスがなくなった時が最後のターン()
{
// given:
$fullBoard = Board::make(Matrix::init(8, 8, Color::white()->toCode())->toArray());

// when:
$firstTurn = Othello::init();
$lastTurn = Othello::make(20, Color::white(), $fullBoard, 0);
// then:
self::assertSame(false, $firstTurn->finishedLastTurn());
self::assertSame(true, $lastTurn->finishedLastTurn());
}

/** @test */
public function スキップが2ターン続いたらそれ以降は一切更新不可()
{
// given:
$position = Position::make([4, 6]);

// when:
$turnSkip0 = Othello::make(20, Color::white(), Board::init(), 0);
$turnSkip1 = Othello::make(20, Color::white(), Board::init(), 1);
$turnSkip2 = Othello::make(20, Color::white(), Board::init(), 2);

// then:
// isContinuable()テスト
self::assertSame(true, $turnSkip0->isContinuable());
self::assertSame(true, $turnSkip1->isContinuable());
self::assertSame(false, $turnSkip2->isContinuable());
// skipカウントとupdateのテスト
self::assertSame(true, !empty($turnSkip0->next($position))); // next()が問題なく実行できればOK
self::assertSame(true, !empty($turnSkip1->next($position))); // next()が問題なく実行できればOK
// スキップが2回続いたターンを更新しようとすると例外
self::expectException(\Exception::class);
$turnSkip2->next($position);
}

// ---------------------------------------
// スキップ系
// ---------------------------------------
/** @test */
public function 盤面における場所がなかったらスキップカウントがアップ()
public function スキップ()
{
// given:
$w = Color::white()->toCode();
// 白も黒も置ける場所がない盤面
$board = [
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,$w,$w,0,0,0,],
[0,0,0,$w,$w,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
];

$board = Board::make($board);

// when:
$turn1 = Othello::make(1, Color::black(), $board, 0);
$turn1 = Turn::make(1, Color::black(), $board, 0);
$turn2 = $turn1->next();
$turn3 = $turn2->next();

Expand All @@ -134,48 +85,33 @@ public function 盤面における場所がなかったらスキップカウン
}

/** @test */
public function スキップされなかった時、カウントは0に戻る()
public function 置ける場所があるのにスキップしようとした場合は例外を出す()
{
// given:
$w = Color::white()->toCode();
$b = Color::black()->toCode();
// 白も黒も置ける場所がない盤面
$board = [
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,$b,$w,$b,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
[0,0,0,0,0,0,0,0,],
];
$board = Board::make($board);

// when:
// 黒のターンとして生成
$turn1 = Othello::make(1, Color::black(), $board, 0);
// 黒が行動(スキップ)
$turn2 = $turn1->next();
// 白が行動
$turn3 = $turn2->next(Position::make([4, 2]));

$turn = Turn::init(); // 1ターン目
// then:
// 2ターン目(黒の行動後の白のターン)
self::assertSame(1, $turn2->getSkipCount(), 'スキップしたのでカウントアップ');
// 3ターン目(白の行動後の黒のターン)
self::assertSame(0, $turn3->getSkipCount(), '色に関わらず行動できたときはリセット');
$this->expectException(\Exception::class);
// 置ける場所があるのに場所指定なしで更新した場合
$turn->next();
}

// ---------------------------------------
// 終了条件系
// ---------------------------------------
/** @test */
public function 置ける場所があるのにスキップしようとした場合は例外を出す()
public function 盤面に空いているマスがなくなった時が最後のターン()
{
// given:
$fullBoard = Board::make(Matrix::init(8, 8, Color::white()->toCode())->toArray());

// when:
$turn = Othello::init(); // 1ターン目
$firstTurn = Turn::init();
$lastTurn = Turn::make(20, Color::white(), $fullBoard, 0);
// then:
$this->expectException(\Exception::class);
// 置ける場所があるのに場所指定なしで更新した場合
$turn->next();
self::assertSame(false, $firstTurn->finishedLastTurn());
self::assertSame(true, $lastTurn->finishedLastTurn());
}


}

0 comments on commit 9139a9b

Please sign in to comment.