dooooooduo

gomoku.php

May 20th, 2017
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 9.82 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  * 截止:2017年05月27日 23:59:59
  5.  * 分值:1.5分,酌情扣分
  6.  * 难度:★★☆☆☆
  7.  * 提交:私聊我提交答案
  8.  * 需求:完成Board类的isEnd方法;本程序仅能在php-cli环境下运行,别问我cli和五子棋是什么;请合理运用类中提供的常量
  9.  */
  10.  
  11. /**
  12.  * 五子棋程序
  13.  * @author 肚肚
  14.  */
  15.  
  16. if (php_sapi_name() !== 'cli') {
  17.     exit('请使用php-cli运行');
  18. }
  19.  
  20. /**
  21.  * 工具静态类
  22.  */
  23. class Tool {
  24.     private static $colors = [
  25.         'reset'   => "\033[0m",
  26.         'bold'    => "\033[1m",
  27.         'black'   => "\033[30m",
  28.         'red'     => "\033[31m",
  29.         'green'   => "\033[32m",
  30.         'yellow'  => "\033[33m",
  31.         'blue'    => "\033[34m",
  32.         'magenta' => "\033[35m",
  33.         'cyan'    => "\033[36m",
  34.         'white'   => "\033[37m"
  35.     ];
  36.  
  37.     public static function println ($msg, $params = [], $is_scan = FALSE, $color = FALSE) {
  38.         $msg = vsprintf($msg, $params);
  39.         if ($color && isset(self::$colors[$color]) && PHP_OS !== 'WINNT') {
  40.             $msg = self::$colors[$color] . $msg . self::$colors['reset'];
  41.         }
  42.         if ($is_scan) {
  43.             echo $msg;
  44.             return PHP_OS === 'WINNT' ? stream_get_line(STDIN, 1024, PHP_EOL) : readline('');
  45.         } else {
  46.             echo $msg . PHP_EOL;
  47.         }
  48.     }
  49. }
  50.  
  51. /**
  52.  * 语言包工厂类
  53.  */
  54. class Language {
  55.     const CURRENT_BOARD     = '目前棋盘:';
  56.     const ENTER_DIMENSION   = '请输入棋盘大小(范围为%d<=n<=%d,生成出的棋盘大小为n*n),或者输入q退出:';
  57.     const ENTER_POSITION    = '请输入您要下的位置(格式:行 列):';
  58.     const INVALID_FORMAT    = '非法格式!';
  59.     const INVALID_DIMENSION = '大小不合法!';
  60.     const INVALID_POSITION  = '非法位置!';
  61.     const MADE_MOVE         = '%s的决定:%d %d';
  62.     const WON               = '%s赢了!';
  63.     const DRAW              = '平局!';
  64.     const AI                = 'AI';
  65.     const YOU               = '你';
  66. }
  67.  
  68. /**
  69.  * 棋盘类
  70.  */
  71. class Board {
  72.     private $board     = [];            // 棋盘
  73.     private $n         = 0;             // 棋盘大小
  74.     private $player    = 0;             // 目前玩家
  75.     private $last_move = [null, null];  // 上一步
  76.  
  77.     const PLAYER_AI      = 0;   // AI编号
  78.     const PLAYER_ME      = 1;   // 玩家编号
  79.     const DIM_LOWER      = 8;   // 棋盘大小下限
  80.     const DIM_UPPER      = 15;  // 棋盘大小上限
  81.     const LABEL_EMPTY    = '-'; // 格子空值
  82.     const LABEL_AI       = 'X'; // 格子被AI占领的值
  83.     const LABEL_ME       = 'O'; // 格子被玩家占领的值
  84.     const DIR_VERTICAL   = 0;   // 纵向
  85.     const DIR_HORIZONTAL = 1;   // 横向
  86.     const DIR_DIAGONALL  = 2;   // 左斜
  87.     const DIR_DIAGONALR  = 3;   // 右斜
  88.     const STATE_ONGOING  = -1;  // 游戏未结束
  89.     const STATE_DRAW     = -2;  // 平局
  90.  
  91.     public function __construct ($n) {
  92.         $this->board  = array_fill(0, $n, array_fill(0, $n, Board::LABEL_EMPTY));
  93.         $this->n      = $n;
  94.         $this->player = mt_rand(self::PLAYER_AI, self::PLAYER_ME);
  95.     }
  96.  
  97.     /**
  98.      * 显示棋盘
  99.      */
  100.     public function display () {
  101.         echo Language::CURRENT_BOARD . PHP_EOL;
  102.         echo '  ';
  103.         for ($i = 0; $i < $this->n; ++$i) {
  104.             printf(' %2d', $i);
  105.         }
  106.         echo PHP_EOL;
  107.         for ($i = 0; $i < $this->n; ++$i) {
  108.             printf('%2d', $i);
  109.             for ($j = 0; $j < $this->n; ++$j) {
  110.                 printf(' %2s', $this->getCell($i, $j));
  111.             }
  112.             echo PHP_EOL;
  113.         }
  114.     }
  115.  
  116.     /**
  117.      * 获取对应格子的值
  118.      * @param  int    $row 行
  119.      * @param  int    $row 列
  120.      * @return string
  121.      */
  122.     private function getCell ($row, $col) {
  123.         return $this->board[$row][$col];
  124.     }
  125.  
  126.     /**
  127.      * 检查游戏是否结束
  128.      * @return mixed 如果游戏结束,则返回获胜玩家的编号,否则返回self::STATE_DRAW
  129.      *               如果游戏没结束,则返回self::STATE_ONGOING
  130.      */
  131.     public function isEnd () {
  132.  
  133.         $row_ls  = max($this->last_move[0] - 4, 0           );
  134.         $col_ls  = max($this->last_move[1] - 4, 0           );
  135.         $row_rs  = min($this->last_move[0] + 4, $this->n - 1);
  136.         $col_rs  = min($this->last_move[1] + 4, $this->n - 1);
  137.         $label   = $this->getCell($this->last_move[0], $this->last_move[1]);
  138.  
  139.         $options = [
  140.             [$row_ls, $row_rs, $this->last_move[1]],
  141.             [$col_ls, $col_rs, $this->last_move[0]],
  142.             [$row_ls, $row_rs,  1                 ],
  143.             [$row_rs, $row_ls, -1                 ]
  144.         ];
  145.  
  146.         for ($dir = self::DIR_VERTICAL; $dir <= self::DIR_HORIZONTAL; ++$dir) {
  147.             for ($start = $options[$dir][0], $idx2 = $options[$dir][2]; $options[$dir][1] - $start >= 4; ++$start) {
  148.                 $interrupted = FALSE;
  149.                 for ($idx = $start, $end = $idx + 4; $idx <= $end; ++$idx) {
  150.                     $val = $dir === self::DIR_VERTICAL ? $this->getCell($idx, $idx2) : $this->getCell($idx2, $idx);
  151.                     if ($val !== $label) {
  152.                         $interrupted = TRUE;
  153.                         break;
  154.                     }
  155.                 }
  156.                 if (!$interrupted) {
  157.                     return $this->player;
  158.                 }
  159.             }
  160.         }
  161.  
  162.         for ($dir = self::DIR_DIAGONALL; $dir <= self::DIR_DIAGONALR; ++$dir) {
  163.             $start_col = $col_ls;
  164.             $start_row = $options[$dir][0];
  165.             for (; $this->last_move[1] - $start_col >= 4; ++$start_col, $start_row += $options[$dir][2]) {
  166.                 $interrupted = FALSE;
  167.                 $row     = $start_row;
  168.                 $col     = $start_col;
  169.                 $end_row = $row + 4;
  170.                 $end_col = $col + 4;
  171.                 for (; $row <= $end_row && $col <= $end_col; ++$col, $row += $options[$dir][2]) {
  172.                     if ($this->getCell($row, $col) !== $label) {
  173.                         $interrupted = TRUE;
  174.                         break;
  175.                     }
  176.                 }
  177.                 if (!$interrupted) {
  178.                     return $this->player;
  179.                 }
  180.             }
  181.         }
  182.  
  183.         $board_str = json_encode($this->board);
  184.         return str_replace(self::LABEL_EMPTY, '', $board_str) === $board_str ? self::STATE_DRAW : self::STATE_ONGOING;
  185.     }
  186.  
  187.     /**
  188.      * 随机选择一个下棋的位置
  189.      * @return array 包含了行、列的数组
  190.      */
  191.     public function pickMove () {
  192.         do {
  193.             $row = mt_rand(0, $this->n - 1);
  194.             $col = mt_rand(0, $this->n - 1);
  195.         } while (!$this->isValidMove($row, $col));
  196.  
  197.         return [$row, $col];
  198.     }
  199.  
  200.     /**
  201.      * 下棋
  202.      * @param int $row 行
  203.      * @param int $col 列
  204.      */
  205.     public function makeMove ($row, $col) {
  206.         $this->board[$row][$col] = $this->player === self::PLAYER_AI ? self::LABEL_AI : self::LABEL_ME;
  207.         $this->last_move         = [$row, $col];
  208.         Tool::println(Language::MADE_MOVE, [$this->player === self::PLAYER_AI ? Language::AI : Language::YOU, $row, $col], FALSE, 'blue');
  209.     }
  210.  
  211.     /**
  212.      * 检查步数是否合法
  213.      * @param  int  $row 行
  214.      * @param  int  $col 列
  215.      * @return bool 是否合法
  216.      */
  217.     public function isValidMove ($row, $col) {
  218.         return isset($this->board[$row][$col]) && $this->board[$row][$col] === self::LABEL_EMPTY;
  219.     }
  220.  
  221.     /**
  222.      * 切换玩家
  223.      */
  224.     public function toNextPlayer () {
  225.         $this->player ^= 1;
  226.     }
  227.  
  228.  
  229.     /**
  230.      * 获取目前玩家
  231.      * @return int 玩家
  232.      */
  233.     public function getCurrentPlayer () {
  234.         return $this->player;
  235.     }
  236. }
  237.  
  238. while (TRUE) {
  239.     // 提示输入棋盘大小或者退出游戏
  240.     $n = Tool::println(Language::ENTER_DIMENSION, [Board::DIM_LOWER, Board::DIM_UPPER], TRUE, 'green');
  241.     if ($n === 'q') {
  242.         break;
  243.     }
  244.  
  245.     // 检查大小范围
  246.     $n = intval($n);
  247.     if ($n < Board::DIM_LOWER || $n > Board::DIM_UPPER) {
  248.         Tool::println(Language::INVALID_DIMENSION, FALSE, 'red');
  249.         continue;
  250.     }
  251.  
  252.     $board = new Board($n);
  253.     $board->display();
  254.     while (TRUE) {
  255.         $row = $col = 0;
  256.  
  257.         // 判断目前玩家并选择作出决定
  258.         switch ($board->getCurrentPlayer()) {
  259.             case Board::PLAYER_AI:
  260.                 list($row, $col) = $board->pickMove();
  261.                 break;
  262.             case Board::PLAYER_ME:
  263.                 while (TRUE) {
  264.                     $params = explode(' ', Tool::println(Language::ENTER_POSITION, [], TRUE, 'green'));
  265.                     if (count($params) !== 2) {
  266.                         Tool::println(Language::INVALID_FORMAT, FALSE, 'red');
  267.                         continue;
  268.                     }
  269.                     list($row, $col) = array_map('intval', $params);
  270.                     if (!$board->isValidMove($row, $col)) {
  271.                         Tool::println(Language::INVALID_POSITION, FALSE, 'red');
  272.                         continue;
  273.                     }
  274.                     break;
  275.                 }
  276.                 break;
  277.         }
  278.         $board->makeMove($row, $col);
  279.         $board->display();
  280.  
  281.         // 检查游戏是否结束
  282.         if (($state = $board->isEnd()) !== Board::STATE_ONGOING) {
  283.             switch ($state) {
  284.                 case Board::STATE_DRAW:
  285.                     Tool::println(Language::DRAW, [], FALSE, 'magenta');
  286.                     break;
  287.                 default:
  288.                     Tool::println(Language::WON, [$state == Board::PLAYER_AI ? Language::AI : Language::YOU], FALSE, 'magenta');
  289.                     break;
  290.             }
  291.             break;
  292.         }
  293.  
  294.         // 移动到下个玩家
  295.         $board->toNextPlayer();
  296.     }
  297.  
  298. }
Advertisement
Add Comment
Please, Sign In to add comment