Skip to content

Commit

Permalink
Merge pull request #19 from ebinase/feature/#11
Browse files Browse the repository at this point in the history
ターン実装
  • Loading branch information
ebinase authored Nov 14, 2021
2 parents 4f5fbce + 65c012f commit 21201b2
Show file tree
Hide file tree
Showing 11 changed files with 366 additions and 145 deletions.
181 changes: 100 additions & 81 deletions app/Console/Commands/OthelloCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Console\Command;
use Packages\Models\Board\Board;
use Packages\Models\Board\Position\Position;
use Packages\Models\Board\Position\PositionConverterTrait;
use Packages\Models\Bot\BotFactory;
use Packages\Models\Bot\Levels\LevelFactory;
Expand Down Expand Up @@ -34,9 +35,6 @@ class OthelloCommand extends Command
// ---------------------------------------
// 設定値
// ---------------------------------------
const COLOR_WHITE = 1;
const COLOR_BLACK = 2;

const MODE_PVP = '01';
const MODE_VS_BOT = '02';
const MODE_BOT_ONLY = '03';
Expand All @@ -46,8 +44,6 @@ class OthelloCommand extends Command
// ---------------------------------------
private string $gameMode;

private Board $board;

private PlayerInterface $whitePlayer;
private PlayerInterface $blackPlayer;

Expand Down Expand Up @@ -76,11 +72,13 @@ public function __construct()
public function handle()
{
$gameMode = $this->choice("ゲームモードを選択してください。", [1 => 'プレイヤー対戦', 2 => 'Bot対戦', 3 => 'Bot対Bot(観戦)'], 1);
if ($gameMode === '1') {
if ($gameMode === 'プレイヤー対戦') {
$this->gameMode = self::MODE_PVP;
$this->whitePlayer = new NormalPlayer('01', 'player_01');
$this->blackPlayer = new NormalPlayer('02', 'player_02');
} elseif ($gameMode === '2') {
$this->playerList = [
Color::white()->toCode() => new NormalPlayer('01', 'player_01'),
Color::black()->toCode() => new NormalPlayer('02', 'player_02'),
];
} elseif ($gameMode === 'Bot対戦') {
$this->gameMode = self::MODE_VS_BOT;

$this->info('対戦するボットを選んでください。');
Expand All @@ -90,11 +88,15 @@ public function handle()

$choice = $this->choice("プレー順を選んでください", [1 => 'プレイヤー先攻', 2 => 'Bot先攻'], 1);
if ($choice === 'プレイヤー先攻') {
$this->whitePlayer = new NormalPlayer('01', 'player_01');
$this->blackPlayer = new BotPlayer('02', 'player_02', $id);
$this->playerList = [
Color::white()->toCode() => new NormalPlayer('01', 'player_01'),
Color::black()->toCode() => new BotPlayer('02', 'player_02', $id),
];
} else {
$this->whitePlayer = new BotPlayer('01', 'player_01');
$this->blackPlayer = new NormalPlayer('02', 'player_02', $id);
$this->playerList = [
Color::white()->toCode() => new BotPlayer('01', 'player_01', $id),
Color::black()->toCode() => new NormalPlayer('02', 'player_02'),
];
}
} else {
$this->info('対戦するボットを選んでください。');
Expand All @@ -103,91 +105,108 @@ public function handle()
$id = $this->ask('id');

$this->playerList = [
self::COLOR_WHITE => new BotPlayer('01', 'player_01', $id),
self::COLOR_BLACK => new BotPlayer('02', 'player_02', $id),
Color::white()->toCode() => new BotPlayer('01', 'player_01', $id),
Color::black()->toCode() => new BotPlayer('02', 'player_02', $id),
];
}

$this->board = Board::init();
$turn = Turn::init();
while (!$turn->finishedLastTurn() && $turn->isContinuable()) { // 最終ターンが終了しておらず、プレイが継続可能な状態の時
$this->info($turn->getTurnNumber() . 'ターン目');

$this->renderBoard($turn->getBoard());
$this->info(($turn->getTurnNumber() % 2 == 1 ? '' : '') . 'のターンです。');

// スキップの場合はすぐにcontinue
if ($turn->mustSkip()) {
$this->confirm('置ける場所がないためスキップします。', true);
$turn = $turn->next();
continue;
}

$turn = 1;
while (true) {
$activeColor = $turn%2 == 1 ? self::COLOR_WHITE : self::COLOR_BLACK;
// ボットのターンか判定
$activePlayer = $this->playerList[$turn->getPlayableColor()->toCode()];
if ($activePlayer->isBot()) {
$this->info('Bot思考中・・・');

$this->info($turn . 'ターン目');
$bot = BotFactory::make($id, $turn);

$view = [];
foreach ($this->board->toArray() as $rowNum => $row){
$view[$rowNum][] = $rowNum + 1;
foreach ($row as $value) {
$view[$rowNum][] = match ($value) {
self::COLOR_WHITE => '',
self::COLOR_BLACK => '',
default => ' ',
};
$bar = $this->output->createProgressBar(10);
$bar->start();
$count = 1;
while ($count <= 10) {
// usleep(50000);
$bar->advance();
$count++;
}
}
$bar->finish();
echo "\n\n\n";

$this->table(['', 1,2,3,4,5,6,7,8], $view);
$this->info(($turn%2 == 1 ? '' : '') . 'のターンです。');
$action = $bot->execute();
$this->error($action->toMatrixPosition()[0] . ',' . $action->toMatrixPosition()[1]);
$this->confirm('確認した', true);

if (!$this->board->isPlayable(Color::make($activeColor))) {
$this->confirm('置ける場所がないためスキップします。', true);
} else {
// ボットのターンか判定
$activePlayer = $this->playerList[$activeColor];
if ($activePlayer->isBot()) {
$this->info('Bot思考中・・・');

// $activePlayer->getPlayerType();
$turnObj = new Turn($turn, Color::make($activeColor), $this->board, 0);
$bot = BotFactory::make($id, $turnObj);

$bar = $this->output->createProgressBar(10);
$bar->start();
$count = 1;
while ($count <= 10) {
if ($count === 5) {
$action = $bot->execute();
}
// usleep(50000);
$bar->advance();
$count++;
}
$bar->finish();
echo "\n\n\n";
$action = $this->convertToMatrixPosition($action);
$this->error($action[0] . ',' . $action[1]);
$this->confirm('確認した', true);

} else {
while (true) {
$row = $this->ask('行を入力してください');
$col = $this->ask('列を入力してください');

if (!$this->board->isValid([$row, $col], Color::make($activeColor))) {
$this->error('その場所には置くことができません。');
continue;
}
break;
while (true) {
$row = $this->ask('行を入力してください');
$col = $this->ask('列を入力してください');

if (empty($row) || empty($col)) continue;

if (!$turn->getBoard()->isValid(Position::make([$row, $col]), $turn->getPlayableColor())) {
$this->error('その場所には置くことができません。');
continue;
}
$action = [$row, $col];
break;
}
$action = Position::make([$row, $col]);
}

$this->board = $this->board->update($action, Color::make($activeColor));
$turn = $turn->next($action);
} // while()

$this->error('=====================================');
$this->error('== ゲーム終了 ==');
$this->error('=====================================');
$this->renderBoard($turn->getBoard());

if ($turn->finishedLastTurn()) {
$whitePoint = $turn->getBoard()->getPoint(Color::white());
$blackPoint = $turn->getBoard()->getPoint(Color::black());
$this->info( "◯:$whitePoint 対 ●:$blackPoint");
if ($whitePoint === $blackPoint) {
$result = '引き分け';
} elseif ($whitePoint > $blackPoint){
$result = '◯の勝ち';
} else{
$result = '●の勝ち';
}
$this->error('=====================================');
$this->error('== '. $result .' ==');
$this->error('=====================================');

$turn++;
} elseif (!$turn->isContinuable()) {
echo 'スキップが2回続いたためNo Gameで終了';
} else {
echo '不明なエラー発生により終了';
}
return 0;
}

info($this->board->getRest());
if ($this->board->getRest() == 0) {
break;
private function renderBoard(Board $board)
{
$view = [];
foreach ($board->toArray() as $rowNum => $row){
$view[$rowNum][] = $rowNum + 1;
foreach ($row as $value) {
$view[$rowNum][] = match ($value) {
Color::white()->toCode() => '',
Color::black()->toCode() => '',
default => ' ',
};
}
}
$whitePoint = $this->board->getPoint(Color::white());
$blackPoint = $this->board->getPoint(Color::black());
info( "◯:$whitePoint 対 ●:$blackPoint");
echo $whitePoint > $blackPoint ? '◯の勝ち' : '●の勝ち';
return 0;

$this->table(['', 1,2,3,4,5,6,7,8], $view);
}
}
36 changes: 21 additions & 15 deletions packages/Models/Board/Board.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Packages\Models\Board;

use Packages\Models\Board\Color\Color;
use Packages\Models\Board\Position\Position;
use Packages\Models\Board\Position\PositionConverterTrait;
use Packages\Models\Common\Matrix\Matrix;

Expand All @@ -25,14 +26,14 @@ class Board
const BOARD_SIZE_ROWS = 8;
const BOARD_SIZE_COLS = 8;

public function __construct(array $board)
private function __construct(array $board)
{
$matrix = Matrix::make($board);

// 行数チェック
if ($matrix->dim() != self::BOARD_SIZE_ROWS) throw new \Exception('lack of row');
if ($matrix->dim() != self::BOARD_SIZE_ROWS) throw new \RuntimeException('lack of row');
// 各行の列数チェック
if ($matrix->size() != self::BOARD_SIZE_COLS) throw new \Exception('lack of column');
if ($matrix->size() != self::BOARD_SIZE_COLS) throw new \RuntimeException('lack of column');

$this->board = $matrix;
}
Expand All @@ -48,6 +49,11 @@ public static function init(): Board
return new Board($matrix->toArray());
}

public static function make(array $board): Board
{
return new Board($board);
}

public function toArray(): array
{
return $this->board->toArray();
Expand Down Expand Up @@ -88,12 +94,12 @@ public function getPoint(Color $color): int
* @param Color $color
* @return bool
*/
public function isPlayable(Color $color): bool
public function hasPlayablePosition(Color $color): bool
{
for ($row = 1; $row < $this->board->dim(); $row++) {
for ($col = 1; $col < $this->board->size(); $col++) {
for ($row = 1; $row <= $this->board->dim(); $row++) {
for ($col = 1; $col <= $this->board->size(); $col++) {
// 一つでもおけるマスがあったらtrueを返す
if ($this->isValid([$row, $col], $color)) return true;
if ($this->isValid(Position::make([$row, $col]), $color)) return true;
}
}
return false;
Expand Down Expand Up @@ -130,23 +136,23 @@ private function getFlipScore(array $position, Color $color): int
return $flipScore;
}

public function isValid(array $position, Color $color): bool
public function isValid(Position $position, Color $color): bool
{
if ($this->getFlipScore($position, $color) > 0) {
if ($this->getFlipScore($position->toMatrixPosition(), $color) > 0) {
return true;
}
return false;
}

public function update(array $position, Color $color): Board
public function update(Position $position, Color $color): Board
{
// 置けない場合は盤面に変更を加えず返す
// 置けない場合
if (!$this->isValid($position, $color)) {
return $this;
throw new \Exception();
}

// 更新された盤面を返す
$updatedBoard = $this->flipStones($position, $color);
$updatedBoard = $this->flipStones($position->toMatrixPosition(), $color);
return new Board($updatedBoard);
}

Expand Down Expand Up @@ -235,8 +241,8 @@ public function diff(Board $board)
public function analyze(Color $color)
{
$flipScores = [];
for ($row = 1; $row < $this->board->dim(); $row++) {
for ($col = 1; $col < $this->board->size(); $col++) {
for ($row = 1; $row <= $this->board->dim(); $row++) {
for ($col = 1; $col <= $this->board->size(); $col++) {
$score = $this->getFlipScore([$row, $col], $color);
if ($score > 0) {
$positionId = $this->convertToPositionId([$row, $col]);
Expand Down
2 changes: 1 addition & 1 deletion packages/Models/Bot/BotFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class BotFactory
* @var array<string, CalculatorInterface>
*/
private static array $botList = [
self::BOT_ID_RANDOM => \Packages\Domain\Bot\Bots\RandomBot::class
self::BOT_ID_RANDOM => \Packages\Models\Bot\Bots\RandomBot::class
];

public static function make(string $botId, Turn $turn): BotInterface
Expand Down
3 changes: 2 additions & 1 deletion packages/Models/Bot/BotInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

namespace Packages\Models\Bot;

use Packages\Models\Board\Position\Position;
use Packages\Models\Bot\Calculators\CalculatorInterface;
use Packages\Models\Bot\Levels\BotLevel;

interface BotInterface
{
public function execute(): int;
public function execute(): Position;

public static function getName(): string;
public static function getDescription(): string;
Expand Down
3 changes: 2 additions & 1 deletion packages/Models/Bot/Bots/RandomBot.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Packages\Models\Bot\Bots;

use Packages\Models\Board\Position\Position;
use Packages\Models\Bot\BotInterface;
use Packages\Models\Bot\Calculators\CalculatorInterface;
use Packages\Models\Bot\Calculators\Random\RandomCalculator;
Expand All @@ -27,7 +28,7 @@ public function __construct(
)
{}

public function execute(): int
public function execute(): Position
{
return $this->randomCalculator->culculate();
}
Expand Down
3 changes: 2 additions & 1 deletion packages/Models/Bot/Calculators/CalculatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace Packages\Models\Bot\Calculators;

use Packages\Models\Board\Position\Position;
use Packages\Models\Turn\Turn;

interface CalculatorInterface
{
public function __construct(Turn $turn);

public function culculate(): int;
public function culculate(): Position;
}
Loading

0 comments on commit 21201b2

Please sign in to comment.