Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) TicTacToe

 

STLQt CreatorLubuntu

 

TicTacToe is a class embodying the game logic of Tic-Tac-Toe.

 

TicTacToe is used by, among others:

Technical facts

 

 

 

 

 

 

./CppTicTacToe/CppTicTacToe.pri

 

INCLUDEPATH += \
    ../../Classes/CppTicTacToe

SOURCES += \
    ../../Classes/CppTicTacToe/tictactoewinner.cpp \
    ../../Classes/CppTicTacToe/tictactoesquare.cpp \
    ../../Classes/CppTicTacToe/tictactoeplayer.cpp \
    ../../Classes/CppTicTacToe/tictactoeboard.cpp \
    ../../Classes/CppTicTacToe/tictactoegame.cpp \
    ../../Classes/CppTicTacToe/tictactoehelper.cpp \
    ../../Classes/CppTicTacToe/tictactoewidget.cpp \
    ../../Classes/CppTicTacToe/tictactoekey.cpp \
    ../../Classes/CppTicTacToe/tictactoeai.cpp \
    ../../Classes/CppTicTacToe/tictactoeais.cpp

HEADERS  += \
    ../../Classes/CppTicTacToe/tictactoewinner.h \
    ../../Classes/CppTicTacToe/tictactoesquare.h \
    ../../Classes/CppTicTacToe/tictactoeplayer.h \
    ../../Classes/CppTicTacToe/tictactoeboard.h \
    ../../Classes/CppTicTacToe/tictactoegame.h \
    ../../Classes/CppTicTacToe/tictactoehelper.h \
    ../../Classes/CppTicTacToe/tictactoewidget.h \
    ../../Classes/CppTicTacToe/tictactoefwd.h \
    ../../Classes/CppTicTacToe/tictactoekey.h \
    ../../Classes/CppTicTacToe/tictactoeai.h \
    ../../Classes/CppTicTacToe/tictactoeais.h

OTHER_FILES += \
    ../../Classes/CppTicTacToe/Licence.txt

 

 

 

 

 

./CppTicTacToe/tictactoeai.h

 

#ifndef TICTACTOEAI_H
#define TICTACTOEAI_H

#include <string>
#include <utility>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"

#include "tictactoefwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

///An artifical intelligence for a tic tac toe game
struct Ai
{
  Ai() {}
  virtual ~Ai() {}
  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;
  virtual std::pair<int,int> SuggestMove(const Game& game) const = 0;
  virtual std::string ToStr() const noexcept = 0;
};

///Try to enforce a draw
struct AiEnforceDraw : public Ai
{
  std::pair<int,int> SuggestMove(const Game& game) const;
  std::string ToStr() const noexcept { return "enforce_draw"; }
};

///Try to enforce a win
struct AiEnforceWin : public Ai
{
  std::pair<int,int> SuggestMove(const Game& game) const;
  std::string ToStr() const noexcept { return "enforce_win"; }
};

///Play randomly
struct AiPlayRandom : public Ai
{
  std::pair<int,int> SuggestMove(const Game& game) const;
  std::string ToStr() const noexcept { return "play_random"; }
};

} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOEAI_H

 

 

 

 

 

./CppTicTacToe/tictactoeai.cpp

 

#include "tictactoeai.h"

#include <cassert>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/shared_ptr.hpp>

#include "tictactoeboard.h"
#include "tictactoegame.h"
#include "tictactoehelper.h"
#include "trace.h"
#pragma GCC diagnostic pop

std::string ribi::tictactoe::Ai::GetVersion() noexcept
{
  return "1.0";
}

std::vector<std::string> ribi::tictactoe::Ai::GetVersionHistory() noexcept
{
  return {
    "2014-03-21: version 1.0: initial version"
  };
}

std::vector<std::string> ribi::tictactoe::Ai::GetVersionHistory() noexcept;

std::pair<int,int> ribi::tictactoe::AiEnforceDraw::SuggestMove(
  const ribi::tictactoe::Game& game
) const
{
  boost::multi_array<int,2> like(boost::extents[3][3]);

  if (game.GetWinner() != Winner::no_winner)
  {
    throw std::logic_error("AiEnforceDraw::SuggestMove: Cannot suggest a move when there is a winner");
  }

  //Not interested in taken squares
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      like[col][row] = game.GetBoard()->GetSquare(col,row) != Square::empty
        || !game.CanDoMove(col,row)
      ? -1
      :  1;
    }
  }

  //Not interested in winning the game
  const auto me(game.GetCurrentPlayer());

  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      if (like[col][row] <= 0) continue;
      //Horizontal
      if ( game.GetBoard()->GetSquare((col + 1) % 3,(row + 0) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 0) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 0;
      }
      //Vertical
      if ( game.GetBoard()->GetSquare((col + 0) % 3,(row + 1) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 0) % 3,(row + 2) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 0;
      }
      //Diagonal, top-left to bottom-right
      if ( col == row
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 1) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 2) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 0;
      }
      //Diagonal, top-left to bottom-right
      if ( col == 2 - row
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 1) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 2) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 0;
      }
    }
  }

  //Interested in blocking the other
  const auto other(Helper().GetOtherPlayer(me));
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      if (like[col][row] <= 0) continue;
      //Horizontal
      if ( game.GetBoard()->GetSquare((col + 1) % 3,(row + 0) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 0) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
      //Vertical
      if ( game.GetBoard()->GetSquare((col + 0) % 3,(row + 1) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 0) % 3,(row + 2) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
      //Diagonal, top-left to bottom-right
      if ( col == row
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 1) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 2) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
      //Diagonal, top-left to bottom-right
      if ( col == 2 - row
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 1) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 2) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
    }
  }
  //Collect moves at likedness
  std::map<int,std::vector<std::pair<int,int>>> moves;
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      const int likedness = like[col][row];
      if (moves.count(likedness) == 0)
      {
        moves.insert(moves.begin(),std::make_pair(likedness,std::vector<std::pair<int,int>>()));
      }
      moves[ likedness ].push_back(std::make_pair(col,row));
    }
  }
  //Find moves with maximum likedness
  std::vector<std::pair<int,int>> best_liked( (*moves.rbegin()).second);
  //Choose one at random
  std::random_shuffle(best_liked.begin(),best_liked.end());
  assert(!best_liked.empty());
  assert(game.CanDoMove(best_liked[0].first,best_liked[0].second));
  return best_liked[0];
}

std::pair<int,int> ribi::tictactoe::AiEnforceWin::SuggestMove(
  const ribi::tictactoe::Game& game
) const
{
  boost::multi_array<int,2> like(boost::extents[3][3]);

  if (game.GetWinner() != Winner::no_winner)
  {
    throw std::logic_error("AiEnforceDraw::SuggestMove: Cannot suggest a move when there is a winner");
  }

  //Not interested in taken squares
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      like[col][row] = game.GetBoard()->GetSquare(col,row) != Square::empty
        || !game.CanDoMove(col,row)
      ? -1
      :  1;
    }
  }

  //Interested in winning the game
  const auto me(game.GetCurrentPlayer());

  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      if (like[col][row] <= 0) continue;
      //Horizontal
      if ( game.GetBoard()->GetSquare((col + 1) % 3,(row + 0) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 0) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 3;
      }
      //Vertical
      if ( game.GetBoard()->GetSquare((col + 0) % 3,(row + 1) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 0) % 3,(row + 2) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 3;
      }
      //Diagonal, top-left to bottom-right
      if ( col == row
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 1) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 2) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 3;
      }
      //Diagonal, top-left to bottom-right
      if ( col == 2 - row
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 1) % 3) == Helper().PlayerToSquare(me)
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 2) % 3) == Helper().PlayerToSquare(me)
      )
      {
        like[col][row] = 3;
      }
    }
  }

  //Interested in blocking the other
  const auto other(Helper().GetOtherPlayer(me));
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      if (like[col][row] <= 0) continue;
      //Horizontal
      if ( game.GetBoard()->GetSquare((col + 1) % 3,(row + 0) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 0) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
      //Vertical
      if ( game.GetBoard()->GetSquare((col + 0) % 3,(row + 1) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 0) % 3,(row + 2) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
      //Diagonal, top-left to bottom-right
      if ( col == row
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 1) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 2) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
      //Diagonal, top-left to bottom-right
      if ( col == 2 - row
        && game.GetBoard()->GetSquare((col + 2) % 3,(row + 1) % 3) == Helper().PlayerToSquare(other)
        && game.GetBoard()->GetSquare((col + 1) % 3,(row + 2) % 3) == Helper().PlayerToSquare(other)
      )
      {
        like[col][row] = 2;
      }
    }
  }
  //Collect moves at likedness
  std::map<int,std::vector<std::pair<int,int>>> moves;
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      const int likedness = like[col][row];
      if (moves.count(likedness) == 0)
      {
        moves.insert(moves.begin(),std::make_pair(likedness,std::vector<std::pair<int,int>>()));
      }
      moves[ likedness ].push_back(std::make_pair(col,row));
    }
  }

  //Find moves with maximum likedness
  std::vector<std::pair<int,int>> best_liked( (*moves.rbegin()).second);
  //Choose one at random
  std::random_shuffle(best_liked.begin(),best_liked.end());
  assert(!best_liked.empty());
  assert(game.CanDoMove(best_liked[0].first,best_liked[0].second));
  return best_liked[0];
}

std::pair<int,int> ribi::tictactoe::AiPlayRandom::SuggestMove(
  const ribi::tictactoe::Game& game
) const
{
  boost::multi_array<int,2> like(boost::extents[3][3]);

  if (game.GetWinner() != Winner::no_winner)
  {
    throw std::logic_error("AiEnforceDraw::SuggestMove: Cannot suggest a move when there is a winner");
  }

  //Not interested in taken squares
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      like[col][row] = game.GetBoard()->GetSquare(col,row) != Square::empty
        || !game.CanDoMove(col,row)
      ? -1
      :  1;
    }
  }

  //Collect moves at likedness
  std::map<int,std::vector<std::pair<int,int>>> moves;
  for (int row=0; row!=3; ++row)
  {
    for (int col=0; col!=3; ++col)
    {
      const int likedness = like[col][row];
      if (moves.count(likedness) == 0)
      {
        moves.insert(moves.begin(),std::make_pair(likedness,std::vector<std::pair<int,int>>()));
      }
      moves[ likedness ].push_back(std::make_pair(col,row));
    }
  }

  //Find moves with maximum likedness
  std::vector<std::pair<int,int>> best_liked( (*moves.rbegin()).second);
  //Choose one at random
  std::random_shuffle(best_liked.begin(),best_liked.end());
  assert(!best_liked.empty());
  assert(game.CanDoMove(best_liked[0].first,best_liked[0].second));
  return best_liked[0];
}

 

 

 

 

 

./CppTicTacToe/tictactoeais.h

 

#ifndef TICTACTOEAIS_H
#define TICTACTOEAIS_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/shared_ptr.hpp>
#include "tictactoefwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

///To work on Ai's
struct Ais
{
  Ais() {}
  std::vector<boost::shared_ptr<Ai>> GetAll() const noexcept;
};

} //~namespace tictactoe
} //~namespace ribi


#endif // TICTACTOEAIS_H

 

 

 

 

 

./CppTicTacToe/tictactoeais.cpp

 

#include "tictactoeais.h"

#include <cassert>
#include "tictactoeai.h"

std::vector<boost::shared_ptr<ribi::tictactoe::Ai>> ribi::tictactoe::Ais::GetAll() const noexcept
{
  std::vector<boost::shared_ptr<Ai>> v;
  {
    boost::shared_ptr<Ai> p(new AiEnforceDraw);
    assert(p);
    v.push_back(p);
  }
  {
    boost::shared_ptr<Ai> p(new AiEnforceWin);
    assert(p);
    v.push_back(p);
  }
  {
    boost::shared_ptr<Ai> p(new AiPlayRandom);
    assert(p);
    v.push_back(p);
  }
  return v;
}

 

 

 

 

 

./CppTicTacToe/tictactoeboard.h

 

//---------------------------------------------------------------------------
/*
TicTacToe, tic-tac-toe game class
Copyright (C) 2010-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppTicTacToe.htm
//---------------------------------------------------------------------------
#ifndef TICTACTOEBOARD_H
#define TICTACTOEBOARD_H

#include <iosfwd>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/multi_array.hpp>

#include "tictactoeplayer.h"
#include "tictactoesquare.h"
#include "tictactoewinner.h"
#include "tictactoefwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

///Board is a dumb tic-tac-toe board class: it detects the winner, but does not
///know whose turn it is. TicTacToeGame does know this
struct Board
{
  ///TicTacToe default contructor creates an empty board,
  ///where the current turn is to player1.
  Board() noexcept;

  ///TicTacToe contructor from summized state integer.
  explicit Board(const int state);

  ///Can a player put his/her character at (x,y)?
  bool CanDoMove(const int x, const int y) const noexcept;

  ///Does the square exist?
  bool CanGetSquare(const int x, const int y) const noexcept;

  ///DoMove lets a player put his/her token at a certain position on the board.
  void DoMove(const int x, const int y, const Player player) noexcept;

  ///GetBoard returns the tic-tac-toe board.
  const boost::multi_array<Square,2>& GetBoard() const noexcept { return m_board; }

  ///GetSquare returns the content at square (x,y)
  Square GetSquare(const int x, const int y) const noexcept;

  ///GetSummarizedState returns an integer summarizing the
  ///state, which is both tic-tac-toe board and whose turn it is.
  ///In trinary, for lowest order digit:\n
  ///# : content\n
  ///0 : content of (0,0)\n
  ///1 : content of (1,0)\n
  ///2 : content of (2,0)\n
  ///3 : content of (0,1)\n
  ///4 : content of (1,1)\n
  ///5 : content of (2,1)\n
  ///6 : content of (0,2)\n
  ///7 : content of (1,2)\n
  ///8 : content of (2,2)\n
  ///9 : current turn\n
  ///Content is stored as:
  ///[#] : description\n
  /// 0  : empty\n
  /// 1  : player1\n
  /// 2  : player2\n
  int GetSummarizedState() const noexcept;

  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;
  Winner GetWinner() const noexcept;
  void Restart() noexcept;

  ///SetBoard sets a tic-tac-toe board.
  void SetBoard(const boost::multi_array<Square,2>& board) noexcept;

  ///SetSquare sets the value of square (x,y).
  void SetSquare(const int x, const int y, const Square square_state) noexcept;

  ///SetSummarizedState sets the board its state
  void SetSummarizedState(const int state) noexcept;

  boost::shared_ptr<TextCanvas> ToTextCanvas() const noexcept;

  boost::signals2::signal<void(Board*)> m_signal_changed;

  private:

  ///m_board stores the board in an x-y-order
  boost::multi_array<Square,2> m_board;

  ///NoEmptySquares determines whether there are no empty squares left.
  bool NoEmptySquares() const noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  friend std::ostream& operator<<(std::ostream& os,const Board& t) noexcept;
};

std::ostream& operator<<(std::ostream& os,const Board& t) noexcept;

bool operator==(const Board& lhs, const Board& rhs) noexcept;


} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOEBOARD_H

 

 

 

 

 

./CppTicTacToe/tictactoeboard.cpp

 

//---------------------------------------------------------------------------
/*
TicTacToe, tic-tac-toe game class
Copyright (C) 2010-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppTicTacToe.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "tictactoeboard.h"

#include <algorithm>
#include <cassert>
#include <cmath>

#include <iostream>
#include <stdexcept>

#include "testtimer.h"
#include "textcanvas.h"
#include "tictactoehelper.h"
#include "trace.h"

#pragma GCC diagnostic pop

///TicTacToe default contructor creates an empty board,
///where the current turn is to player1.
ribi::tictactoe::Board::Board() noexcept
  : m_signal_changed{},
    m_board(boost::extents[3][3])
    //m_current_player(TicTacToePlayer::player1)
{
  #ifndef NDEBUG
  Test();
  #endif

  for (int i=0; i!=9; ++i)
  {
    m_board[i/3][i%3] = Square::empty;
  }
}

ribi::tictactoe::Board::Board(const int state)
  : m_signal_changed{},
    m_board(boost::extents[3][3])
{
  SetSummarizedState(state);
}

bool ribi::tictactoe::Board::CanDoMove(const int x, const int y) const noexcept
{
  if (!CanGetSquare(x,y)) return false;
  if (m_board[x][y] != Square::empty) return false;
  return true;
}

bool ribi::tictactoe::Board::CanGetSquare(const int x, const int y) const noexcept
{
  if (x < 0) return false;
  if (y < 0) return false;
  if (x >= 3) return false;
  if (y >= 3) return false;
  return true;
}

void ribi::tictactoe::Board::DoMove(const int x, const int y, const Player player) noexcept
{
  assert(CanDoMove(x,y));
  //std::clog << "Player " << m_current_player
  //  << ": (" << x << "," << y << ")\n";
  m_board[x][y] = Helper().PlayerToSquare(player);
  m_signal_changed(this);
}


ribi::tictactoe::Square ribi::tictactoe::Board::GetSquare(const int x, const int y) const noexcept
{
  assert(CanGetSquare(x,y));
  return m_board[x][y];
}

int ribi::tictactoe::Board::GetSummarizedState() const noexcept
{
  int z = SquareToState(m_board[0][0]);
  for (int i=1; i!=9; ++i)
  {
    z += (SquareToState(m_board[i/3][i%3]) * Helper().IntPower(3,i));
  }
  return z;
}

std::string ribi::tictactoe::Board::GetVersion() noexcept
{
  return "1.6";
}

std::vector<std::string> ribi::tictactoe::Board::GetVersionHistory() noexcept
{
  std::vector<std::string> v {
    "2010-09-19: version 1.3: made CanDoMove member function a const member function",
    "2014-01-27: version 1.4: added ToTextCanvas",
    "2014-02-03: version 1.5: added m_signal_changed",
    "2014-03-17: version 1.6: use enum classes, use of noexcept, extracted Game"
  };
  return v;
}

ribi::tictactoe::Winner ribi::tictactoe::Board::GetWinner() const noexcept
{
  //Check rows
  for (int y=0; y!=3; ++y)
  {
    if (m_board[0][y] != Square::empty
     && m_board[0][y] == m_board[1][y]
     && m_board[1][y] == m_board[2][y])
      return Helper().SquareToWinner(m_board[0][y]);
  }
  //Check collumns
  for (int x=0; x!=3; ++x)
  {
    if (m_board[x][0] != Square::empty
     && m_board[x][0] == m_board[x][1]
     && m_board[x][1] == m_board[x][2])
       return Helper().SquareToWinner(m_board[x][0]);
  }
  //Check diagonal
  if (m_board[0][0] != Square::empty
   && m_board[0][0] == m_board[1][1]
   && m_board[1][1] == m_board[2][2])
    return Helper().SquareToWinner(m_board[1][1]);
  //Check other diagonal
  if (m_board[0][2] != Square::empty
   && m_board[0][2] == m_board[1][1]
   && m_board[1][1] == m_board[2][0])
    return Helper().SquareToWinner(m_board[1][1]);
  //Check for draw
  if (NoEmptySquares()) return Winner::draw;
  //No winner
  return Winner::no_winner;
}

bool ribi::tictactoe::Board::NoEmptySquares() const noexcept
{
  for (int i=0; i!=9; ++i)
  {
    if (m_board[i/3][i%3] == Square::empty) return false;
  }
  return true;
}



void ribi::tictactoe::Board::Restart() noexcept
{
  if (GetSummarizedState() == 0) return;

  for (int i=0; i!=9; ++i)
  {
    m_board[i/3][i%3] = Square::empty;
  }
  m_signal_changed(this);
}

void ribi::tictactoe::Board::SetBoard(const boost::multi_array<Square,2>& board) noexcept
{
  if (m_board == board) return;
  m_board = board;
  m_signal_changed(this);
}

void ribi::tictactoe::Board::SetSquare(
  const int x, const int y, const Square square_state) noexcept
{
  if (m_board[x][y] == square_state) return;

  m_board[x][y] = square_state;

  //Internal test
  assert(GetSquare(x,y)==square_state);

  m_signal_changed(this);
}

void ribi::tictactoe::Board::SetSummarizedState(const int original_state) noexcept
{
  if (GetSummarizedState() == original_state) return;

  assert(original_state >= 0);
  #ifndef NDEBUG
  if (original_state >= Helper().IntPower(3,9))
  {
    TRACE("ERROR");
  }
  #endif
  assert(original_state < Helper().IntPower(3,9));

  int s = original_state;
  for (int i=0; i!=9; ++i)
  {
    m_board[i/3][i%3] = StateToSquare(s % 3);
    s/=3;
  }

  //Internal check
  assert(GetSummarizedState()==original_state);

  m_signal_changed(this);
}





#ifndef NDEBUG
void ribi::tictactoe::Board::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  {
    //Check empty board state
    {
      Board t;
      const int s = t.GetSummarizedState();
      Board u(s);
      assert(u == t);
    }
    //Check one-move states
    for (int i=0; i!=9; ++i)
    {
      Board t;
      t.DoMove(i/3,i%3,Player::player1);
      const int s = t.GetSummarizedState();
      Board u(s);
      assert(u == t);
    }
    //Check two-move states
    for (int i=0; i!=8; ++i)
    {
      Board t;
      t.DoMove(i/3,i%3,Player::player1);
      t.DoMove(i/3,(i+1)%3,Player::player2);
      const int s = t.GetSummarizedState();
      Board u(s);
      assert(u == t);
    }
    //Check draw detection
    {
      Board t;
      t.DoMove(1,1,Player::player1);
      t.DoMove(0,0,Player::player2);
      t.DoMove(1,2,Player::player1);
      t.DoMove(1,0,Player::player2);
      t.DoMove(2,0,Player::player1);
      t.DoMove(0,2,Player::player2);
      t.DoMove(0,1,Player::player1);
      t.DoMove(2,1,Player::player2);
      t.DoMove(2,2,Player::player1);
      assert(t.GetWinner() == Winner::draw);
    }
    //Check player1 wins horizontally detection
    {
      Board t;
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,0,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,0,Player::player2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,1,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,1,Player::player2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,2,Player::player1);
      assert(t.GetWinner() == Winner::player1);
    }
    //Check player2 wins vertically detection
    {
      Board t;
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,0,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,0,Player::player2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,1,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,1,Player::player2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(2,2,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,2,Player::player2);
      assert(t.GetWinner() == Winner::player2);
    }
    //Check player1 wins diagonally detection
    {
      Board t;
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,0,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,0,Player::player2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,1,Player::player1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,2,Player::player2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(2,2,Player::player1);
      assert(t.GetWinner() == Winner::player1);
    }
    //Check no-winner detection
    {
      Board t;
      t.DoMove(1,1,Player::player1);
      t.DoMove(0,0,Player::player2);
      t.DoMove(1,2,Player::player1);
      t.DoMove(1,0,Player::player2);
      t.DoMove(2,0,Player::player1);
      t.DoMove(0,2,Player::player2);
      t.DoMove(0,1,Player::player1);
      t.DoMove(2,1,Player::player2);
      //t.DoMove(2,2); //Final move to make a draw
      assert(t.GetWinner() == Winner::no_winner);
    }
    //Check CanDoMove
    for (int i=0; i!=9; ++i)
    {
      Board t;
      t.DoMove(i/3,i%3,Player::player1);
      assert(!t.CanDoMove(i/3,i%3));
    }
    //Check all states
    for (int i=0; i!=Helper().IntPower(3,9); ++i)
    {
      try
      {
        Board t(i);
        assert(t.GetSummarizedState() == i);
      }
      catch (std::exception&)
      {
        //No problem
      }
    }
  }
}
#endif

boost::shared_ptr<ribi::TextCanvas> ribi::tictactoe::Board::ToTextCanvas() const noexcept
{
  boost::shared_ptr<TextCanvas> c {
    new TextCanvas(3,3)
  };
  for (int y=0; y!=3; ++y)
  {
    for (int x=0; x!=3; ++x)
    {
      c->PutText(x,y,SquareToStr(GetSquare(x,y)));
    }
  }
  return c;
}


std::ostream& ribi::tictactoe::operator<<(std::ostream& os,const ribi::tictactoe::Board& t) noexcept
{
  os
    << SquareToStr(t.GetSquare(0,0))
    << SquareToStr(t.GetSquare(1,0))
    << SquareToStr(t.GetSquare(2,0))
    << '\n'
    << SquareToStr(t.GetSquare(0,1))
    << SquareToStr(t.GetSquare(1,1))
    << SquareToStr(t.GetSquare(2,1))
    << '\n'
    << SquareToStr(t.GetSquare(0,2))
    << SquareToStr(t.GetSquare(1,2))
    << SquareToStr(t.GetSquare(2,2))
  ;
  return os;
}

bool ribi::tictactoe::operator==(const ribi::tictactoe::Board& lhs, const ribi::tictactoe::Board& rhs) noexcept
{
  return lhs.GetBoard() == rhs.GetBoard();
}

 

 

 

 

 

./CppTicTacToe/tictactoefwd.h

 

#ifndef TICTACTOEFWD_H
#define TICTACTOEFWD_H

namespace ribi {

struct Canvas;
struct TextCanvas;
struct QtCanvas;

namespace tictactoe {

struct Ai;
struct Board;
struct Game;
struct Widget;

struct QtTicTacToeWidget;

} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOEFWD_H

 

 

 

 

 

./CppTicTacToe/tictactoegame.h

 

#ifndef TICTACTOEGAME_H
#define TICTACTOEGAME_H

#include <iostream>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>

#include "tictactoeplayer.h"
#include "tictactoewinner.h"
#include "tictactoefwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

///Manages a Board to follow the rules
struct Game
{
  Game() noexcept;

  ///Can the current player put his/her character at (x,y)?
  bool CanDoMove(const int x, const int y) const noexcept;

  ///DoMove lets the current player put his/her token at a certain position on the board.
  void DoMove(const int x, const int y) noexcept;

  boost::shared_ptr<const Board> GetBoard() const noexcept;

  ///GetCurrentPlayer returns whose turn it is.
  Player GetCurrentPlayer() const noexcept { return m_current_player; }

  ///GetCurrentTurn returns the turn number.
  int GetCurrentTurn() const noexcept;

  ///GetSummarizedState returns an integer summarizing the
  ///state, which is both tic-tac-toe board and whose turn it is.
  ///In trinary, for lowest order digit:\n
  ///# : content\n
  ///0-8: board
  ///9 : current turn\n
  ///The current turn is stored as:\n
  ///[#] : description\n
  /// 0  : ERROR\n
  /// 1  : player1\n
  /// 2  : player2\n
  int GetSummarizedState() const noexcept;

  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;

  Winner GetWinner() const noexcept;

  void Restart() noexcept;

  ///SetSummarizedState sets the game its state
  void SetSummarizedState(const int state) noexcept;

  boost::shared_ptr<TextCanvas> ToTextCanvas() const noexcept;

  boost::signals2::signal<void(Game*)> m_signal_changed;

  private:
  boost::shared_ptr<Board> m_board;
  Player m_current_player;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

std::ostream& operator<<(std::ostream& os,const Game& t) noexcept;
bool operator==(const Game& lhs, const Game& rhs) noexcept;

} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOEGAME_H

 

 

 

 

 

./CppTicTacToe/tictactoegame.cpp

 

#include "tictactoegame.h"

#include <cassert>

#include "testtimer.h"
#include "textcanvas.h"
#include "tictactoeai.h"
#include "tictactoeboard.h"
#include "tictactoehelper.h"
#include "trace.h"

ribi::tictactoe::Game::Game() noexcept
  : m_signal_changed{},
    m_board(new Board),
    m_current_player(Player::player1)

{
  #ifndef NDEBUG
  Test();
  #endif

  assert(GetCurrentTurn() == 0);
}

bool ribi::tictactoe::Game::CanDoMove(const int x, const int y) const noexcept
{
  return GetBoard()->CanDoMove(x,y);
}

void ribi::tictactoe::Game::DoMove(const int x, const int y) noexcept
{
  assert(CanDoMove(x,y));
  m_board->DoMove(x,y,m_current_player);
  m_current_player = (m_current_player == Player::player1 ? Player::player2 : Player::player1);
}

boost::shared_ptr<const ribi::tictactoe::Board> ribi::tictactoe::Game::GetBoard() const noexcept
{
  assert(m_board);
  return m_board;
}

int ribi::tictactoe::Game::GetCurrentTurn() const noexcept
{
  int turn = 0;
  for (int i=0; i!=9; ++i)
  {
    if (GetBoard()->GetSquare(i/3,i%3) != Square::empty) ++turn;
  }
  return turn;
}

int ribi::tictactoe::Game::GetSummarizedState() const noexcept
{
  //MSB: player
  //LSB: board
  int z = m_board->GetSummarizedState();
  z += (PlayerToState(m_current_player) * Helper().IntPower(3,9));
  return z;
}

std::string ribi::tictactoe::Game::GetVersion() noexcept
{
  return "1.0";
}

std::vector<std::string> ribi::tictactoe::Game::GetVersionHistory() noexcept
{
  return {
    "2014-03-17: version 1.0: initial version, extracted from Board"
  };
}

ribi::tictactoe::Winner ribi::tictactoe::Game::GetWinner() const noexcept
{
  return GetBoard()->GetWinner();
}

void ribi::tictactoe::Game::Restart() noexcept
{
  m_board->Restart();

  if (m_current_player != Player::player1)
  {
    m_current_player = Player::player1;
    m_signal_changed(this);
  }
}

void ribi::tictactoe::Game::SetSummarizedState(const int original_state) noexcept
{
  if (GetSummarizedState() == original_state) return;

  //MSB: player
  //LSB: board

  //9 for the board, 1 for the current player
  assert(original_state < Helper().IntPower(3,9 + 1));

  int s = original_state;
  m_board->SetSummarizedState(s % Helper().IntPower(3,9));
  s /= Helper().IntPower(3,9);
  m_current_player = StateToPlayer(s);

  //Internal check
  assert(GetSummarizedState()==original_state);

  m_signal_changed(this);
}


#ifndef NDEBUG
void ribi::tictactoe::Game::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    Board();
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  {
    //Check draw detection
    {
      Game t;
      assert(t.GetCurrentPlayer() == Player::player1);
      assert(t.GetCurrentTurn()   == 0);
      t.DoMove(1,1);
      assert(t.GetCurrentPlayer() == Player::player2);
      assert(t.GetCurrentTurn()   == 1);
      t.DoMove(0,0);
      assert(t.GetCurrentPlayer() == Player::player1);
      assert(t.GetCurrentTurn()   == 2);
      t.DoMove(1,2);
      assert(t.GetCurrentPlayer() == Player::player2);
      assert(t.GetCurrentTurn()   == 3);
      t.DoMove(1,0);
      assert(t.GetCurrentPlayer() == Player::player1);
      assert(t.GetCurrentTurn()   == 4);
      t.DoMove(2,0);
      assert(t.GetCurrentPlayer() == Player::player2);
      assert(t.GetCurrentTurn()   == 5);
      t.DoMove(0,2);
      assert(t.GetCurrentPlayer() == Player::player1);
      assert(t.GetCurrentTurn()   == 6);
      t.DoMove(0,1);
      assert(t.GetCurrentPlayer() == Player::player2);
      assert(t.GetCurrentTurn()   == 7);
      t.DoMove(2,1);
      assert(t.GetCurrentPlayer() == Player::player1);
      assert(t.GetCurrentTurn()   == 8);
      t.DoMove(2,2);
      assert(t.GetCurrentPlayer() == Player::player2);
      assert(t.GetCurrentTurn()   == 9);
      assert(t.GetWinner() == Winner::draw);
    }
    //Check player1 wins horizontally detection
    {
      Game t;
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,0);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,0);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,2);
      assert(t.GetWinner() == Winner::player1);
    }
    //Check player2 wins vertically detection
    {
      Game t;
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,0);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,0);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(2,2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,2);
      assert(t.GetWinner() == Winner::player2);
    }
    //Check player1 wins diagonally detection
    {
      Game t;
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(0,0);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,0);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,1);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(1,2);
      assert(t.GetWinner() == Winner::no_winner);
      t.DoMove(2,2);
      assert(t.GetWinner() == Winner::player1);
    }
    //Check no-winner detection
    {
      Game t;
      t.DoMove(1,1);
      t.DoMove(0,0);
      t.DoMove(1,2);
      t.DoMove(1,0);
      t.DoMove(2,0);
      t.DoMove(0,2);
      t.DoMove(0,1);
      t.DoMove(2,1);
      //t.DoMove(2,2); //Final move to make a draw
      assert(t.GetWinner() == Winner::no_winner);
    }
    //Check CanDoMove
    for (int i=0; i!=9; ++i)
    {
      Game t;
      t.DoMove(i/3,i%3);
      assert(t.CanDoMove(i/3,i%3)==false);
    }
    //Check AI's
    {
      for (int a = 0; a!=3; ++a)
      {
        for (int b = 0; b!=3; ++b)
        {
          boost::shared_ptr<Ai> c;
          switch (a)
          {
            case 0: c.reset(new AiEnforceDraw); break;
            case 1: c.reset(new AiEnforceWin); break;
            case 2: c.reset(new AiPlayRandom); break;
          }
          assert(c);
          boost::shared_ptr<Ai> d;
          switch (b)
          {
            case 0: d.reset(new AiEnforceDraw); break;
            case 1: d.reset(new AiEnforceWin); break;
            case 2: d.reset(new AiPlayRandom); break;
          }
          assert(d);
          Game g;
          while (g.GetWinner() == Winner::no_winner)
          {
            const std::pair<int,int> move_1(c->SuggestMove(g));
            assert(g.CanDoMove(move_1.first,move_1.second));
            g.DoMove(move_1.first,move_1.second);
            if (g.GetWinner() != Winner::no_winner) break;
            const std::pair<int,int> move_2(c->SuggestMove(g));
            assert(g.CanDoMove(move_2.first,move_2.second));
            g.DoMove(move_2.first,move_2.second);
          }
          if (verbose) { TRACE(WinnerToName(g.GetWinner())); }
        }
      }
    }
  }
}
#endif

boost::shared_ptr<ribi::TextCanvas> ribi::tictactoe::Game::ToTextCanvas() const noexcept
{
  return GetBoard()->ToTextCanvas();
}


std::ostream& ribi::tictactoe::operator<<(std::ostream& os,const ribi::tictactoe::Game& t) noexcept
{
  os << (*t.GetBoard());
  return os;
}

bool ribi::tictactoe::operator==(const ribi::tictactoe::Game& lhs, const ribi::tictactoe::Game& rhs) noexcept
{
  return *lhs.GetBoard() == *rhs.GetBoard()
      && lhs.GetCurrentPlayer() == rhs.GetCurrentPlayer()
  ;
}

 

 

 

 

 

./CppTicTacToe/tictactoehelper.h

 

#ifndef TICTACTOEHELPER_H
#define TICTACTOEHELPER_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "tictactoeplayer.h"
#include "tictactoesquare.h"
#include "tictactoewinner.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

struct Helper
{
  Helper() {}

  Player GetOtherPlayer(const Player player) const noexcept;

  ///From http://www.richelbilderbeek.nl/CppIntPower.htm
  int IntPower(const int base, const int exponent) const noexcept;


  Square PlayerToSquare(const Player player) const noexcept;
  Player SquareToPlayer(const Square square) const;
  Winner SquareToWinner(const Square square) const;
};

} //~namespace tictactoe
} //~namespace ribi


#endif // TICTACTOEHELPER_H

 

 

 

 

 

./CppTicTacToe/tictactoehelper.cpp

 

#include "tictactoehelper.h"

#include <cassert>
#include <stdexcept>

ribi::tictactoe::Player ribi::tictactoe::Helper::GetOtherPlayer(
  const ribi::tictactoe::Player player
) const noexcept
{
  switch (player)
  {
    case Player::player1: return Player::player2;
    case Player::player2: return Player::player1;
  }
  assert(!"Should not get here");
  throw std::logic_error("GetOtherPlayer: unknown player");
}

int ribi::tictactoe::Helper::IntPower(const int base, const int exponent) const noexcept
{
  assert(exponent != 0
    && "When calculating IntPower(x,0) the result "
       "might be zero or one, depending on the context");
  assert(exponent > 0);

  int result = base;
  for (int i=1; i!=exponent; ++i)
  {
    result*=base;
  }
  return result;
}

ribi::tictactoe::Square ribi::tictactoe::Helper::PlayerToSquare(const ribi::tictactoe::Player player) const noexcept
{
  switch (player)
  {
    case Player::player1: return Square::player1;
    case Player::player2: return Square::player2;
  }
  assert(!"Should not get here");
  throw std::logic_error("PlayerToSquare: unknown player");
}

ribi::tictactoe::Player ribi::tictactoe::Helper::SquareToPlayer(const ribi::tictactoe::Square square) const
{
  switch (square)
  {
    case Square::empty  : assert(!"Cannot convert empty square to player");
    case Square::player1: return Player::player1;
    case Square::player2: return Player::player2;
  }
  assert(!"Should not get here");
  throw std::logic_error("SquareToPlayer: unknown square");
}


ribi::tictactoe::Winner ribi::tictactoe::Helper::SquareToWinner(const ribi::tictactoe::Square square) const
{
  switch (square)
  {
    case Square::empty  : assert(!"Cannot convert empty square to winner");
    case Square::player1: return Winner::player1;
    case Square::player2: return Winner::player2;
  }
  assert(!"Should not get here");
  throw std::logic_error("SquareToWinner: unknown square");
}

 

 

 

 

 

./CppTicTacToe/tictactoekey.h

 

#ifndef TICTACTOEKEY_H
#define TICTACTOEKEY_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

enum class Key { up, right, down, left, select };

} //~namespace tictactoe
} //~namespace ribi


#endif // TICTACTOEKEY_H

 

 

 

 

 

./CppTicTacToe/tictactoekey.cpp

 

#include "tictactoekey.h"

 

 

 

 

 

./CppTicTacToe/tictactoeplayer.h

 

#ifndef TICTACTOEPLAYER_H
#define TICTACTOEPLAYER_H

namespace ribi {
namespace tictactoe {

enum class Player
{
  player1,
  player2
};

int PlayerToState(const Player player) noexcept;
Player StateToPlayer(const int state);

} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOEPLAYER_H

 

 

 

 

 

./CppTicTacToe/tictactoeplayer.cpp

 

#include "tictactoeplayer.h"

#include <cassert>
#include <stdexcept>

int ribi::tictactoe::PlayerToState(const ribi::tictactoe::Player player) noexcept
{
  switch (player)
  {
    case Player::player1: return 1;
    case Player::player2: return 2;
  }
  assert(!"Should not get here");
  throw std::logic_error("TicTacToe::PlayerToState: unknown player");
}

ribi::tictactoe::Player ribi::tictactoe::StateToPlayer(const int state)
{
  switch (state)
  {
    case 1: return Player::player1;
    case 2: return Player::player2;
  }
  assert(!"Should not get here");
  throw std::logic_error("TicTacToe::StateToPlayer: invalid state");
}

 

 

 

 

 

./CppTicTacToe/tictactoesquare.h

 

#ifndef TICTACTOESQUARE_H
#define TICTACTOESQUARE_H

#include <iostream>
#include <string>

namespace ribi {
namespace tictactoe {

enum class Square
{
  empty,
  player1,
  player2
};

std::string SquareToStr(const Square square) noexcept;
int SquareToState(const Square square) noexcept;
Square StateToSquare(const int state);

} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOESQUARE_H

 

 

 

 

 

./CppTicTacToe/tictactoesquare.cpp

 

#include "tictactoesquare.h"

#include <cassert>
#include <stdexcept>

int ribi::tictactoe::SquareToState(const ribi::tictactoe::Square square) noexcept
{
  switch (square)
  {
    case Square::empty  : return 0;
    case Square::player1: return 1;
    case Square::player2: return 2;
  }
  assert(!"Should not get here");
  throw std::logic_error("TicTacToe::SquareToState: unknown square");
}

std::string ribi::tictactoe::SquareToStr(const tictactoe::Square square) noexcept
{
  switch (square)
  {
    case Square::empty  : return " ";
    case Square::player1: return "X";
    case Square::player2: return "O";
  }
  assert(!"Should not get here");
  throw std::logic_error("TicTacToe::SquareToStr: unknown square");
}

ribi::tictactoe::Square ribi::tictactoe::StateToSquare(const int state)
{
  switch (state)
  {
    case 0: return Square::empty;
    case 1: return Square::player1;
    case 2: return Square::player2;
  }
  assert(!"Should not get here");
  throw std::logic_error("TicTacToe::StateToSquare: unknown state");

}

 

 

 

 

 

./CppTicTacToe/tictactoewidget.h

 

#ifndef TICTACTOEWIDGET_H
#define TICTACTOEWIDGET_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include "tictactoefwd.h"
#include "tictactoekey.h"
#include "tictactoeplayer.h"
#include "tictactoewinner.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace tictactoe {

///User interaction focused TicTacToe game
struct Widget
{
  Widget();

  bool CanSelect(const int x, const int y) const noexcept;

  void DoMove() noexcept;

  Player GetCurrentPlayer() const noexcept;

  boost::shared_ptr<const Game> GetGame() const noexcept { return m_game; }

  int GetSummarizedState() const noexcept;

  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;

  Winner GetWinner() const noexcept;

  void PressKey(const Key key) noexcept;

  void Restart() noexcept;

  void Select(const int x, const int y) noexcept;

  boost::shared_ptr<TextCanvas> ToTextCanvas() const noexcept;

  ///Signal emitted when the widget is changed
  boost::signals2::signal<void ()> m_signal_changed;

  private:

  const boost::shared_ptr<Game> m_game;

  int m_x;
  int m_y;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace tictactoe
} //~namespace ribi


#endif // TICTACTOEWIDGET_H

 

 

 

 

 

./CppTicTacToe/tictactoewidget.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "tictactoewidget.h"

#include <cassert>
#include "textcanvas.h"
#include "tictactoegame.h"
#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::tictactoe::Widget::Widget()
  : m_signal_changed{},
    m_game{new Game},
    m_x{1},
    m_y{1}
{
  #ifndef NDEBUG
  Test();
  #endif
}

bool ribi::tictactoe::Widget::CanSelect(const int x, const int y) const noexcept
{
  return GetGame()->CanDoMove(x,y);
}

void ribi::tictactoe::Widget::DoMove() noexcept
{
  assert(GetGame()->CanDoMove(m_x,m_y));
  m_game->DoMove(m_x,m_y);
}

ribi::tictactoe::Player ribi::tictactoe::Widget::GetCurrentPlayer() const noexcept
{
  return GetGame()->GetCurrentPlayer();
}

int ribi::tictactoe::Widget::GetSummarizedState() const noexcept
{
  return GetGame()->GetSummarizedState();
}

std::string ribi::tictactoe::Widget::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::tictactoe::Widget::GetVersionHistory() noexcept
{
  std::vector<std::string> v {
    "2014-xx-xx: version 1.0: initial version, in GameTicTacToe folder",
    "2014-03-17: version 1.1: moved from the game TicTacToe to CppTicTacToeFolder, improved interface"
  };
  return v;
}

ribi::tictactoe::Winner ribi::tictactoe::Widget::GetWinner() const noexcept
{
  return GetGame()->GetWinner();
}

void ribi::tictactoe::Widget::PressKey(const ribi::tictactoe::Key key) noexcept
{
  switch (key)
  {
    case Key::up    : if (m_y > 0) --m_y; m_signal_changed(); break;
    case Key::right : if (m_x < 2) ++m_x; m_signal_changed(); break;
    case Key::down  : if (m_y < 2) ++m_y; m_signal_changed(); break;
    case Key::left  : if (m_x > 0) --m_x; m_signal_changed(); break;
    case Key::select:
    {
      if (m_game->CanDoMove(m_x,m_y))
      {
        m_game->DoMove(m_x,m_y);
        m_signal_changed();
      }
    }
    break;
  }
}

void ribi::tictactoe::Widget::Restart() noexcept
{
  m_game->Restart();
}

void ribi::tictactoe::Widget::Select(const int x, const int y) noexcept
{
  assert(CanSelect(x,y));
  if (m_x != x || m_y != y)
  {
    m_x = x;
    m_y = y;
    m_signal_changed();
  }
}

#ifndef NDEBUG
void ribi::tictactoe::Widget::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  Widget w;
  assert(!w.GetVersion().empty());
}
#endif

boost::shared_ptr<ribi::TextCanvas> ribi::tictactoe::Widget::ToTextCanvas() const noexcept
{
  const boost::shared_ptr<TextCanvas> canvas {
    m_game->ToTextCanvas()
  };
  assert(canvas);

  if (m_game->GetWinner() == Winner::player1
   || m_game->GetWinner() == Winner::player2)
  {
    for (int i=0; i!=3; ++i)
    {
      for (int j=0; j!=3; ++j)
      {
        canvas->PutChar(i,j, m_game->GetWinner() == Winner::player1 ? 'X': 'O');
      }
    }
  }
  const char c = canvas->GetChar(m_x,m_y);
  char d = ' ';
  switch (c)
  {
    case ' ': d = '.'; break;
    case '.': d = ' '; break;
    case 'O': d = 'o'; break;
    case 'X': d = 'x'; break;
    case 'o': d = 'O'; break;
    case 'x': d = 'X'; break;
  }
  canvas->PutChar(m_x,m_y,d);
  return canvas;
}

 

 

 

 

 

./CppTicTacToe/tictactoewinner.h

 

#ifndef TICTACTOEWINNER_H
#define TICTACTOEWINNER_H

#include <string>

namespace ribi {
namespace tictactoe {

enum class Winner
{
  player1,
  player2,
  draw,
  no_winner
};

std::string WinnerToStr(const Winner winner) noexcept;
std::string WinnerToName(const Winner winner) noexcept;

} //~namespace tictactoe
} //~namespace ribi

#endif // TICTACTOEWINNER_H

 

 

 

 

 

./CppTicTacToe/tictactoewinner.cpp

 

#include "tictactoewinner.h"

#include <cassert>
#include <stdexcept>

std::string ribi::tictactoe::WinnerToName(const ribi::tictactoe::Winner winner) noexcept
{
  switch (winner)
  {
    case Winner::player1  : return "player1";
    case Winner::player2  : return "player2";
    case Winner::draw     : return "draw";
    case Winner::no_winner: return "no_winner";
  }
  assert(!"Should not get here");
  throw std::logic_error("ribi::WinnerToName: unknown winner");
}

std::string ribi::tictactoe::WinnerToStr(const ribi::tictactoe::Winner winner) noexcept
{
  switch (winner)
  {
    case Winner::player1  : return "X";
    case Winner::player2  : return "O";
    case Winner::draw     : //return "*";
    case Winner::no_winner: //return " ";
      assert(!"Should not get here");
      throw std::logic_error("ribi::WinnerToStr: unknown winner");
  }
  assert(!"Should not get here");
  throw std::logic_error("ribi::WinnerToStr: unknown winner");
}

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml