用Dart写的黑白棋游戏

2013年11月,Dart语言1.0稳定版SDK发布,普天同庆。从此,网页编程不再纠结了。

在我看来,Dart语法简直就是C#的升级版,太像了。之所以喜欢Ruby的一个重要理由是支持mixin功能,而Dart也引入了mixin特性。

最棒的是Google提供了集成开发环境——Dart Editor和Dartium,有非常强大的编辑和调试功能,既能编写网页程序,也能编写服务器端程序。

把网站上的主要文档看完一遍后,再把SDK下载解压,就能用Dart编程了。

第一个程序写什么呢?做了一个黑白棋游戏。

直接上代码:

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Reversi</title>
    <link rel="stylesheet" href="reversi.css">
  </head>
  <body>
    <h1>黑白棋</h1>
    
    <p id="score"><span class="black piece"></span>&nbsp;<span id="blackScore">2</span>
      <span class="spacer"></span><span class="white piece"></span>&nbsp;<span id="whiteScore">2</span></p>
      
    <table id="board">
    </table>
    
    <p id="message"></p>

    <script type="application/dart" src="reversi_ui.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

棋盘界面的生成和用户事件处理:

import 'dart:html';
import 'reversi_game.dart';

var chess = new ChessBoard();
var cells = new Map<DivElement,BoardCell>();
var blackScore = querySelector("#blackScore");
var whiteScore = querySelector("#whiteScore");
var message = querySelector("#message");

void main() {
  TableElement board = querySelector("#board");
  for(int i=0;i<ChessBoard.BoardSize;i++){
    var row = board.addRow();
    for(int j=0;j<ChessBoard.BoardSize;j++){
       var cell = row.addCell();
       var piece = new DivElement();
       cell.children.add(piece);
       piece.onClick.listen(placePiece);
       cells[piece] = chess.cells[i][j];
    }
  }
  updateCells();
}

void updateCells(){
  cells.forEach((piece, boardCell){
    piece.classes.clear();
    if(!boardCell.isEmpty){
      piece.classes.add(boardCell.piece);
    }
  });
  blackScore.text = chess.blackScore.toString();
  whiteScore.text = chess.whiteScore.toString();
  message.text = chess.message;
}

void placePiece(MouseEvent event) {
  var piece = event.target;
  var boardCell = cells[piece];
  if(chess.placePiece(boardCell)){
    updateCells();
    piece.classes.add('last');
  }else if(boardCell.isEmpty){
    message.text = chess.message;
    piece.classes.add('last');
  }
}

游戏的逻辑部分:

class BoardCell {
  int row;
  int col;
  var piece;

  BoardCell(this.row, this.col);

  bool get isEmpty{
    return piece == null;
  }
}

class CellPos {
  int row;
  int col;
  CellPos(this.row, this.col);
}

class ChessBoard {
  static const BoardSize = 8;
  static const white = 'white';
  static const black = 'black';

  var currentTurn = black;
  List<List<BoardCell>> cells;
  int blackScore = 2;
  int whiteScore = 2;

  bool gameOver = false;
  String tip;

  ChessBoard(){
    cells = new List<List<BoardCell>>();
    for(int i=0;i<BoardSize;i++){
      var row = new List<BoardCell>();
      for(int j=0;j<BoardSize;j++){
        var cell = new BoardCell(i, j);
        row.add(cell);
      }
      cells.add(row);
    }

    cells[3][3].piece = white;
    cells[3][4].piece = black;
    cells[4][3].piece = black;
    cells[4][4].piece = white;

    tip = "游戏开始";
  }

  switchTurn(){
    currentTurn = getReverse(currentTurn);
  }

  String getReverse(String piece){
    if(piece == black){
      return white;
    }else if(piece == white){
      return black;
    }else{
      return null;
    }
  }

  bool placePiece(BoardCell cell){
    if(cell.isEmpty){
      var success = reverseOpponents(cell, currentTurn);
      if(success){
        cell.piece = currentTurn;
        calculateScore();
        switchTurn();
        if(canPlacePiece(currentTurn)){
          tip = null;
        }else{
          switchTurn();
          if(canPlacePiece(currentTurn)){
            tip = "${players[getReverse(currentTurn)]}无棋";
          }else{
            gameOver = true;
          }
        }
        return true;
      }else{
        tip = "落子无效";
      }
    }
    return false;
  }

  static List<CellPos> w(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int j = col - 1; j >= 0; j--){
      adjacentCells.add(new CellPos(row, j));
    }
    return adjacentCells;
  }

  static List<CellPos> e(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int j = col + 1; j < BoardSize; j++){
      adjacentCells.add(new CellPos(row, j));
    }
    return adjacentCells;
  }

  static List<CellPos> n(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int i = row - 1; i >= 0; i--){
      adjacentCells.add(new CellPos(i, col));
    }
    return adjacentCells;
  }

  static List<CellPos> s(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int i = row + 1; i < BoardSize; i++){
      adjacentCells.add(new CellPos(i, col));
    }
    return adjacentCells;
  }

  static List<CellPos> ne(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int i = row - 1, j = col + 1; i >= 0 && j < BoardSize; i--, j++){
      adjacentCells.add(new CellPos(i, j));
    }
    return adjacentCells;
  }

  static List<CellPos> se(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int i = row + 1, j = col + 1; i < BoardSize && j < BoardSize; i++, j++){
      adjacentCells.add(new CellPos(i, j));
    }
    return adjacentCells;
  }

  static List<CellPos> sw(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int i = row + 1, j = col - 1; i < BoardSize && j >= 0; i++, j--){
      adjacentCells.add(new CellPos(i, j));
    }
    return adjacentCells;
  }

  static List<CellPos> nw(int row, int col){
    List<CellPos> adjacentCells = [];
    for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
      adjacentCells.add(new CellPos(i, j));
    }
    return adjacentCells;
  }

  var directions = [n, ne, e, se, s, sw, w, nw];

  List<BoardCell> findReverible(BoardCell cell, String piece){
    List<BoardCell> allOpponents = [];
    for(var direction in directions){
      List<BoardCell> opponents = [];
      for(var cellPos in direction(cell.row, cell.col)){
        var nextCell = cells[cellPos.row][cellPos.col];
        if(nextCell.piece == getReverse(piece)){
          opponents.add(nextCell);
        }else{
          if(nextCell.piece == piece){
            allOpponents.addAll(opponents);
          }
          break;
        }
      }
    }
    return allOpponents;
  }

  bool reverseOpponents(BoardCell cell, String piece){
    List<BoardCell> allOpponents = findReverible(cell, piece);
    if(allOpponents.length > 0){
      allOpponents.forEach((opp){opp.piece=piece;});
      return true;
    }else{
      return false;
    }
  }

  bool canPlacePiece(String piece){
    for(int i=0;i<BoardSize;i++){
      for(int j=0;j<BoardSize;j++){
        var cell = cells[i][j];
        if(cell.isEmpty &&
            findReverible(cell, piece).length > 0){
          return true;
        }
      }
    }
    return false;
  }

  calculateScore(){
    whiteScore = 0;
    blackScore = 0;
    for(int i=0;i<BoardSize;i++){
      for(int j=0;j<BoardSize;j++){
        var piece = cells[i][j].piece;
        if(piece == white){
          whiteScore++;
        }else if(piece == black){
          blackScore++;
        }
      }
    }
  }

  var players = {black:"黑方", white:"白方"};

  String get message {
    if(gameOver){
      if(whiteScore > blackScore){
        return "白方胜!";
      }else if(whiteScore < blackScore){
        return "黑方胜!";
      }else{
        return "双方平局!";
      }
    }
    if(tip == null){
      return "${players[currentTurn]}走棋";
    }else{
      return "$tip,${players[currentTurn]}走棋";
    }
  }
}
View Code

开发完成后,用dart2js转成javascript,然后拷贝到网站服务器上,就可以在线玩了。

点此打开

以上只是一个简单的界面和走棋逻辑,接下来要做的是加入人工智能,可以人机对战;使用WebSocket通信,实现联网对战。

这部分的代码就不贴上来了。
点此打开试玩

联网对弈功能需要运行游戏服务器程序,目前只能在局域网使用。

想要完整源代码的朋友可以发邮件给我:251024877@qq.com。不要在评论中留下email,这种的我不会回的。

posted @ 2014-01-20 23:06 刘俊峰 阅读(...) 评论(...) 编辑 收藏