Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) Pylos

 

STLQt CreatorLubuntu

 

The Pylos classes are used for the game Pylos (also called 'Pyraos').

 

The Pylos classes are tested by TestPylos page.

Technical facts

 

 

 

 

 

 

./CppPylos/CppPylos.pri

 

INCLUDEPATH += \
    ../../Classes/CppPylos

SOURCES += \
    ../../Classes/CppPylos/pylosboard.cpp \
    ../../Classes/CppPylos/pyloscoordinat.cpp \
    ../../Classes/CppPylos/pyloscurrentmovestate.cpp \
    ../../Classes/CppPylos/pylosgame.cpp \
    ../../Classes/CppPylos/pylosmove.cpp \
    ../../Classes/CppPylos/pylosmustremovestate.cpp \
    ../../Classes/CppPylos/pylosplayer.cpp \
    ../../Classes/CppPylos/pylospositionstate.cpp \
    ../../Classes/CppPylos/pyloswinner.cpp

HEADERS  += \
    ../../Classes/CppPylos/pylosboard.h \
    ../../Classes/CppPylos/pyloscoordinat.h \
    ../../Classes/CppPylos/pyloscurrentmovestate.h \
    ../../Classes/CppPylos/pylosfwd.h \
    ../../Classes/CppPylos/pylosgame.h \
    ../../Classes/CppPylos/pylosmove.h \
    ../../Classes/CppPylos/pylosmustremovestate.h \
    ../../Classes/CppPylos/pylosplayer.h \
    ../../Classes/CppPylos/pylospositionstate.h \
    ../../Classes/CppPylos/pyloswinner.h

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

 

 

 

 

 

./CppPylos/pylosboard.h

 

//---------------------------------------------------------------------------
/*
pylos::Board, class for a Pylos/Phyraos board
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSBOARD_H
#define PYLOSBOARD_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <boost/checked_delete.hpp>
#include <boost/shared_ptr.hpp>
#include "pylosfwd.h"
#include "pylosmove.h"
#include "pyloswinner.h"
#include "pylosplayer.h"
#include "pylospositionstate.h"
#pragma GCC diagnostic pop

namespace ribi {

struct TextCanvas;

namespace pylos {

struct Board
{
  typedef std::vector<std::vector<PositionState> > Layer;

  Board() noexcept;
  virtual ~Board();

  ///CanDo determines if a Pylos notation move is valid
  bool CanDo(const std::string& s, const Player player) const;

  ///CanDo determines if a Pylos move is valid
  bool CanDo(const Move& m, const Player player) const;

  ///CanDoSomething specifies if there is a possible move to do for a player
  bool CanDoSomething(const Player& player) const;

  ///CanRemove specifies if one or
  ///two marble(s) can be removed at the requested position(s).
  bool CanRemove(const std::vector<Coordinat>& v, const Player player) const;

  ///CanMove specifies the marble at the specified coordinat can be tranferred
  bool CanTransfer(const Coordinat& c, const Player player) const;

  ///CanMove specifies if the marble can be tranferred to the new (higher) position
  bool CanTransfer(
    const Coordinat& from,
    const Coordinat& to, const Player player) const;

  ///CanSet specifies if a marble can be placed at the coordinat.
  ///It is chosen to let the client specify a player, to
  ///emphasise that a player's marble is placed, instead
  ///of a PositionState::empty
  bool CanSet(const Coordinat& c, const Player player) const;

  ///Clone a derived class of Board.
  virtual std::unique_ptr<Board> Clone() const = 0;

  ///Count counts the requested state at the specified coordinats
  int Count(const std::vector<Coordinat>& coordinats, const PositionState state) const;

  ///Count counts the requested state.
  ///Valid states are empty, player1 and player2
  int Count(const PositionState state) const;

  ///Create a BoardAdvanced
  static std::unique_ptr<Board> CreateAdvancedBoard() noexcept;

  ///Create a BoardBasic
  static std::unique_ptr<Board> CreateBasicBoard() noexcept;

  ///Do performs a move in Pylos notation
  void Do(const std::string& s, const Player player);

  ///Do performs a Pylos move
  void Do(const Move& m, const Player player);

  ///Get returns the state of the requested location
  PositionState Get(const Coordinat& c) const noexcept;

  ///GetAllPossibleMoves returns all moves valid for the selected player
  const std::vector<Move> GetAllPossibleMoves(const Player& player) const noexcept;

  ///GetLayerSize returns how many marbles this is wide/height.
  ///For example; layer 0 has 4x4 marbles, so GetLayerSize
  ///will return 4.
  int GetLayerSize(const int layer) const noexcept;

  ///Obtain this class its version
  static std::string GetVersion() noexcept;

  ///Obtain this class its version history
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Return the possible winner
  Winner GetWinner() const noexcept;

  ///PlayRandomPylosGame plays a random Pylos game and returns the winner.
  static Winner PlayRandomPylosGame(const boost::shared_ptr<Board>& board_original = boost::shared_ptr<Board>()) noexcept;

  ///Remove removes one or two marbles.
  void Remove(const std::vector<Coordinat>& v, const Player player);

  ///Restart sets the board in its initial position.
  void Restart();

  ///Set sets the state of the given location.
  ///must_remove is set to the MustRemoveState who must remove one or two marbles.
  virtual void Set(
    const Coordinat& c,
    const Player player,
    MustRemoveState& must_remove
  ) = 0;


  ///Display the board as a std::string
  std::string ToStr() const noexcept;

  ///Display the board as a 2D std::string
  std::vector<std::string> ToText() const noexcept;

  ///Display the board as a 2D std::string
  boost::shared_ptr<TextCanvas> ToTextCanvas() const noexcept;

  ///Transfer lets current player transfer his marble to a new, higher position
  void Transfer(
    const Coordinat& from,
    const Coordinat& to,
    MustRemoveState& must_remove);


  protected:

  ///m_board holds the board structure.\n
  ///m_board[0]: bottom 4x4 layer\n
  ///m_board[1]: 3x3 layer\n
  ///m_board[2]: 2x2 layer\n
  ///m_board[3]: top 1x1 layer
  std::vector<Layer> m_board;

  ///CanRemove specifies if current player can remove
  ///the marble at the requested position.
  bool CanRemove(const Coordinat& c, const Player player) const;

  ///RemoveMarble removes one marble.
  void Remove(const Coordinat& c, const Player player);

  private:
  ///CreateEmptyBoard created an empty board.
  std::vector<Layer> CreateEmptyBoard() const noexcept;

  ///CreateLayer creates an empty layer.
  const Layer CreateLayer(const int sz) const;

  #ifndef NDEBUG
  ///Test this class
  static void Test() noexcept;
  #endif

  //Friends
  //friend void boost::checked_delete<>(Board* x);
  friend bool operator==(const Board& lhs, const Board& rhs) noexcept;
};

///A BoardAdvanced lets a player remove one or two marbles when
///- a player creates a 2x2 square of marbles of his/her color
///- a player creates a 1x4 (bottom layer) or 1x3 (one-but-bottom layer)
///  line of marbles of his/her color
struct BoardAdvanced final : public Board
{
  BoardAdvanced() noexcept;
  ~BoardAdvanced();

  ///Clone a derived class of Board.
  std::unique_ptr<Board> Clone() const noexcept override;

  ///Load loads a game in Pylos notation
  //void Load(const std::string& s);

  ///Save saves the current game in Pylos notation
  //void Save(const std::string& s) const;

  ///ToStr converts Pylos its contents to a std::string
  //const std::string ToStr() const;

  private:
  ///Set sets the state of the given location.
  ///must_remove is set to true if the current player is allowed
  ///to remove one or two marbles.
  void Set(
    const Coordinat& c,
    const Player state,
    MustRemoveState& must_remove);
};

///A BoardBasic lets a player remove one or two marbles when
///- a player creates a 2x2 square of marbles of his/her color
struct BoardBasic final : public Board
{
  BoardBasic() noexcept;
  ~BoardBasic();

  ///Clone a derived class of Pylos.
  std::unique_ptr<Board> Clone() const noexcept override;

  ///Load loads a game in Pylos notation
  //void Load(const std::string& s);

  ///Save saves the current game in Pylos notation
  //void Save(const std::string& s) const;

  ///ToStr converts Pylos its contents to a std::string
  //const std::string ToStr() const;

  private:
  ///Set sets the state of the given location.
  ///must_remove is set to true if the current player is allowed
  ///to remove one or two marbles.
  void Set(
    const Coordinat& c,
    const Player state,
    MustRemoveState& must_remove);
};

///A BoardBeginner lets a player remove one or two marbles when
///- never


bool operator==(const Board& lhs, const Board& rhs) noexcept;
bool operator!=(const Board& lhs, const Board& rhs) noexcept;
std::ostream& operator<<(std::ostream& os,const Board& p) noexcept;

} //~namespace Pylos
} //~namespace ribi

#endif // PYLOSBOARD_H

 

 

 

 

 

./CppPylos/pylosboard.cpp

 

//---------------------------------------------------------------------------
/*
pylos::Board, class for a Pylos/Phyraos board
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/CppPylos.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "pylosboard.h"

#include "pyloscoordinat.h"
#include "pylosmove.h"
#include "pylosmustremovestate.h"
#include "pylosplayer.h"
#include "pylospositionstate.h"
#include "pyloswinner.h"
#include "textcanvas.h"
#include "trace.h"

#include <boost/numeric/conversion/cast.hpp>

#ifdef PYLOSGAME_H
#error pylos::Game must not be defined for a pylos::Board
#endif

#pragma GCC diagnostic pop

ribi::pylos::Board::Board() noexcept
  : m_board(CreateEmptyBoard())
{
  #ifndef NDEBUG
  Test();
  #endif

  //Assume correct board sizes
  assert(m_board.size() == 4);
  assert(m_board[0].size() == 4);
  assert(m_board[0][0].size() == 4);
  assert(m_board[0][1].size() == 4);
  assert(m_board[0][2].size() == 4);
  assert(m_board[0][3].size() == 4);
  assert(m_board[1].size() == 3);
  assert(m_board[1][0].size() == 3);
  assert(m_board[1][1].size() == 3);
  assert(m_board[1][2].size() == 3);
  assert(m_board[2].size() == 2);
  assert(m_board[2][0].size() == 2);
  assert(m_board[2][1].size() == 2);
  assert(m_board[3].size() == 1);
  assert(m_board[3][0].size() == 1);
}

ribi::pylos::Board::~Board()
{
  //OK
}

bool ribi::pylos::Board::CanDo(const pylos::Move& m, const Player player) const
{
  assert(m.IsValid());
  if (m.m_move.size()==1)
  {
    //Placement
    if (!CanSet(m.m_move[0],player)) return false;
  }
  else
  {
    assert(m.m_move.size() == 2);
    if (!CanTransfer(m.m_move[0],m.m_move[1],player)) return false;
  }
  //Make a copy Pylos
  boost::shared_ptr<Board> p = Clone();
  assert(p && "Assume cloning succeeded");
  assert(p.get() != this && "Assume clone is in different memory location");
  #define DEBUG_TEMP_CHECK_27364864984376597625482762367528
  #ifdef DEBUG_TEMP_CHECK_27364864984376597625482762367528
  if(*p != *this)
  {
    TRACE(*p);
    TRACE(*this);
  }
  #endif
  assert(*p == *this && "Assumes clone is identical");
  //Do the move
  MustRemoveState must_remove = MustRemoveState::no;
  if (m.m_move.size()==1)
  {
    p->Set(m.m_move[0],player,must_remove);
  }
  else
  {
    p->Transfer(m.m_move[0],m.m_move[1],must_remove);
  }
  //Check if marbles need to be removed
  if (must_remove != MustRemoveState::no)
  {
    //Move must not lack one/two marbles to be removed
    if (m.m_remove.empty()) return false;
    //Must remove marbles
    return p->CanRemove(m.m_remove,player);
  }
  else
  {
    //Must not remove marbles, so the move is valid if
    //m_remove is empty
    return m.m_remove.empty();
  }
}

bool ribi::pylos::Board::CanDo(const std::string& s, const Player player) const
{
  try
  {
    return CanDo(pylos::Move(s),player);
  }
  catch (std::exception& e)
  {
    return false;
  }
}

bool ribi::pylos::Board::CanDoSomething(const Player& player) const
{
  return !GetAllPossibleMoves(player).empty();
}

bool ribi::pylos::Board::CanRemove(const std::vector<Coordinat>& v, const Player player) const
{
  assert(v.size() == 1 || v.size() == 2);
  if (v.size() == 1) return CanRemove(v[0],player);
  assert(v.size() == 2);
  //Disallow selecting the same marble twice
  if (v[0] == v[1]) return false;
  //Disallow selecting empty spots
  if ( Get(v[0]) == PositionState::empty
    || Get(v[1]) == PositionState::empty) return false;
  //Disallow selecting marbles of different colors
  if (Get(v[0]) != Get(v[1])) return false;
  //Keep one ordering, c2 must be above c1
  if (v[0].GetLayer() == v[1].GetLayer())
  {
    return CanRemove(v[0],player) && CanRemove(v[1],player);
  }
  if (v[0].GetLayer() > v[1].GetLayer())
  {
    std::vector<Coordinat> w(v);
    std::swap(w[0],w[1]);
    return CanRemove(w,player);
  }
  //Assert proper ordering
  assert(v[0].GetLayer() < v[1].GetLayer());
  //If the top marble cannot be removed,
  //the lower cannot either
  if (!CanRemove(v[1],player)) return false;
  //The bottom marble can be removed if above are only empty spots, or only the top marble
  //Clone the board, remove the top marble of the clone and test if the bottom marble can be removed
  boost::shared_ptr<Board> b = this->Clone();
  assert(b->CanRemove(v[1],player));
  b->Remove(v[1],player);
  return b->CanRemove(v[0],player);
}

bool ribi::pylos::Board::CanRemove(const Coordinat& c, const Player player) const
{
  //Cannot remove an empty spot
  if (Get(c)==PositionState::empty) return false;

  //Cannot remove marble of another player
  if (Get(c)!=ToPositionState(player)) return false;

  //Cannot remove from top layer, because then the player at the top has already won
  if (c.GetLayer() == 3) return false;

  //A marble can be removed if all positions above are empty
  const std::vector<Coordinat> v = GetAbove(c);
  return Count(v,PositionState::empty) == static_cast<int>(v.size());
}

bool ribi::pylos::Board::CanTransfer(const Coordinat& c, const Player player) const
{
  //Cannot transfer an empty spot
  if (Get(c)==PositionState::empty) return false;

  //Cannot transfer marble of other color
  if (Get(c)!=ToPositionState(player)) return false;

  //Cannot transfer from top layer, because then the player at the top has already won
  if (c.GetLayer() == 3) return false;

  //A marble can be transferred if all positions above are empty
  const std::vector<Coordinat> v = GetAbove(c);
  return Count(v,PositionState::empty) == static_cast<int>(v.size());
}

bool ribi::pylos::Board::CanTransfer(const Coordinat& from,
  const Coordinat& to,
  const Player player) const
{
  //The source and target must differ
  if (from == to) return false;

  //The to position must be higher
  if (from.GetLayer() >= to.GetLayer()) return false;

  //The from position must be the player
  if (Get(from) != ToPositionState(player)) return false;

  //The to position must be empty
  if (Get(to) != PositionState::empty) return false;

  //Transfer the marble by cloning the board, removing it and placing it
  boost::shared_ptr<Board> b = this->Clone();

  assert(player == ToPlayer(b->Get(from))); //Added check for line below
  //const Player player = ToPlayer(b->Get(from)); //Removed

  assert(b->CanRemove(from,player));
  b->Remove(from,player);
  return b->CanSet(to,player);
}

#ifdef NDEBUG
bool ribi::pylos::Board::CanSet(const Coordinat& c, const Player) const
#else
bool ribi::pylos::Board::CanSet(const Coordinat& c, const Player player) const
#endif
{
  assert(player == Player::player1 || player == Player::player2); //Prevent compiler from complaining
  if (Get(c) != PositionState::empty) return false;
  //Player can always place a marble at an empty spot at the bottom
  if (c.GetLayer() == 0) return true;
  //Player wants to place a marbles at non-bottom layer,
  //which is valid if none of the spots below are empty
  const std::vector<Coordinat> v = GetBelow(c);
  assert(v.size() == 4);
  return Count(v,PositionState::empty) == 0;
}

int ribi::pylos::Board::Count(const std::vector<Coordinat>& coordinats, const PositionState state) const
{
  return std::count_if(coordinats.begin(),coordinats.end(),
    [this,state](const Coordinat& c)
    {
      return Get(c) == state;
    }
  );
}

int ribi::pylos::Board::Count(const PositionState state) const
{
  const std::vector<Coordinat> v = pylos::GetAllCoordinats();
  return Count(v,state);
}

std::unique_ptr<ribi::pylos::Board> ribi::pylos::Board::CreateAdvancedBoard() noexcept
{
  return std::make_unique<BoardAdvanced>();
}

std::unique_ptr<ribi::pylos::Board> ribi::pylos::Board::CreateBasicBoard() noexcept
{
  return std::make_unique<BoardBasic>();
}

std::vector<ribi::pylos::Board::Layer> ribi::pylos::Board::CreateEmptyBoard() const noexcept
{
  std::vector<Layer> v;
  v.push_back(CreateLayer(4));
  v.push_back(CreateLayer(3));
  v.push_back(CreateLayer(2));
  v.push_back(CreateLayer(1));
  return v;
}

const ribi::pylos::Board::Layer ribi::pylos::Board::CreateLayer(const int sz) const
{
  #ifndef NDEBUG
  if(sz <= 0)
  {
    TRACE("ERROR");
    TRACE(sz);
  }
  #endif
  assert(sz > 0);
  return std::vector<std::vector<PositionState> > (
    sz,std::vector<PositionState>(sz,PositionState::empty));
}

void ribi::pylos::Board::Do(const std::string& s, const Player player)
{
  Do(pylos::Move(s),player);
}

void ribi::pylos::Board::Do(const pylos::Move& m, const Player player)
{
  assert(CanDo(m,player));
  MustRemoveState must_remove = MustRemoveState::no;
  if (m.m_move.size() == 1)
  {
    this->Set(m.m_move[0],player,must_remove);
  }
  else
  {
    Transfer(m.m_move[0],m.m_move[1],must_remove);
  }
  if (!m.m_remove.empty())
  {
    assert(must_remove != MustRemoveState::no);
    std::for_each(m.m_remove.begin(),m.m_remove.end(),
      [this,&must_remove,player](const Coordinat& c)
      {
        Remove(c,player);
      }
    );
  }
  else
  {
    assert(!must_remove);
  }
}

ribi::pylos::PositionState ribi::pylos::Board::Get(const Coordinat& c) const noexcept
{
  //Just checking, a constructed Coordinat should pass all asserts
  assert(c.IsValid());
  assert(c.IsValid());
  assert(c.GetLayer() < static_cast<int>(m_board.size()));
  assert(!m_board.empty());
  assert(c.GetX() < static_cast<int>(m_board[c.GetLayer()].size()));
  assert(!m_board[c.GetLayer()].empty());
  assert(c.GetY() < static_cast<int>(m_board[c.GetLayer()][c.GetX()].size()));
  return m_board[c.GetLayer()][c.GetX()][c.GetY()];
}

const std::vector<ribi::pylos::Move> ribi::pylos::Board::GetAllPossibleMoves(const Player& player) const noexcept
{
  const std::vector<Coordinat> v = pylos::GetAllCoordinats();
  std::vector<Move> w;
  const auto j = v.end();
  for (auto i = v.begin(); i!=j; ++i)
  {
    //Check for set
    if (this->CanSet(*i,player))
    {
      assert(this->Get(*i) == PositionState::empty);
      if (CanDo(i->ToStr(),player))
      {
        //Check for simple set (that is, without removal
        w.push_back(Move(i->ToStr()));
      }
      else
      {
        //Check first removal coordinat
        for (auto r1 = v.begin(); r1!=j; ++r1)
        {
          //Check one-marble remove
          if (CanDo(Move( {*i}, {*r1} ),player))
          {
            w.push_back(Move( {*i}, {*r1} ));
            //Check two-marble remove
            for (auto r2 = r1; r2!=j; ++r2)
            {
              if (r1 == r2) continue; //Checked by CanDo
              if (CanDo(Move( {*i}, {*r1,*r2} ),player)) w.push_back(Move( {*i}, {*r1,*r2} ));
            }
          }
        }
      }
    }
    //Check for transfer
    else if (this->CanTransfer(*i,player))
    {
      assert(this->Get(*i) == ToPositionState(player));
      for (auto to = i; to!=j; ++to)
      {
        if (CanDo(Move( {*i,*to}, {} ),player))
        {
          //Check for simple transfer (that is, without removal
          w.push_back(Move( {*i,*to}, {} ));
        }
        else
        {
          //Check first removal coordinat
          for (auto r1 = v.begin(); r1!=j; ++r1)
          {
            //Check one-marble remove
            if (CanDo(Move( {*i,*to}, {*r1} ),player))
            {
              w.push_back(Move( {*i,*to}, {*r1} ));
              //Check two-marble remove
              for (auto r2 = r1; r2!=j; ++r2)
              {
                if (r1 == r2) continue; //Checked by CanDo
                if (CanDo(Move( {*i,*to}, {*r1,*r2} ),player)) w.push_back(Move( {*i,*to}, {*r1,*r2} ));
              }
            }
          }
        }
      }
    }
  }
  return w;
}

int ribi::pylos::Board::GetLayerSize(const int layer) const noexcept
{
  assert(layer >= 0);
  assert(layer < boost::numeric_cast<int>(m_board.size()));
  return boost::numeric_cast<int>(m_board[layer].size());
}

std::string ribi::pylos::Board::GetVersion() noexcept
{
  return "2.0";
}

std::vector<std::string> ribi::pylos::Board::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2012-05-05: version 2.0: initial release version");
  return v;
}

ribi::pylos::Winner ribi::pylos::Board::GetWinner() const noexcept
{
  if (Get(Coordinat(3,0,0)) != PositionState::empty)
  {
    switch (Get(Coordinat(3,0,0)))
    {
      case PositionState::player1: return Winner::player1;
      case PositionState::player2: return Winner::player2;
      default: assert(!"Should not get here");
    }
  }
  if (Count(PositionState::player1)==15  && !CanDoSomething(Player::player1)) return Winner::player2;
  if (Count(PositionState::player2)==15  && !CanDoSomething(Player::player2)) return Winner::player1;
  return Winner::none;
}

ribi::pylos::Winner ribi::pylos::Board::PlayRandomPylosGame(const boost::shared_ptr<Board>& board_original) noexcept
{
  boost::shared_ptr<Board> board;
  if (board_original)
  {
    board = board_original->Clone();
  }
  else
  {
    if ((std::rand() >> 4) % 2)
      board = ribi::pylos::Board::CreateAdvancedBoard();
    else
      board = ribi::pylos::Board::CreateBasicBoard();
  }
  Player player = Player::player1;

  while (1)
  {
    if (board->GetWinner() != Winner::none)
    {
      return board->GetWinner();
    }
    //Use random move from GetAllPossibleMoves
    const std::vector<Move> v = board->GetAllPossibleMoves(player);
    const int i = std::rand() % v.size();
    board->Do(v[i],player);
    //Do not always toggle the player
    if ((std::rand() >> 4) % 2) Toggle(player);
  }
}

#ifdef NDEBUG
void ribi::pylos::Board::Remove(const Coordinat& c, const Player)
#else
void ribi::pylos::Board::Remove(const Coordinat& c, const Player player)
#endif
{
  assert(CanRemove(c,player));
  m_board[c.GetLayer()][c.GetX()][c.GetY()] = PositionState::empty;
}

void ribi::pylos::Board::Remove(const std::vector<Coordinat>& v, const Player player)
{
  assert(CanRemove(v,player));
  //Proper ordering: v[0] must be marble above
  if (v.size() == 2 && v[0].GetLayer() < v[1].GetLayer())
  {
    std::vector<Coordinat> w(v);
    std::swap(w[0],w[1]);
    Remove(w,player);
    return;
  }
  //Assert proper ordering
  assert(v.size() == 1 || v[0].GetLayer() >= v[1].GetLayer());
  std::for_each(v.begin(),v.end(),
    [this,player](const Coordinat& c)
    {
      Remove(c,player);
    }
  );
}

#ifndef NDEBUG
void ribi::pylos::Board::Test() noexcept
{
  {
    static bool tested = false;
    if (tested) return;
    tested = true;
  }
  const bool verbose{false};
  const int testing_depth = 1;

  if (verbose) { TRACE("Test operator=="); }
  {
    boost::shared_ptr<BoardBasic> a(new BoardBasic);
    boost::shared_ptr<BoardBasic> b(new BoardBasic);
    assert(*a == *b);
    a->Do("(0,0,0)",Player::player1);
    assert(*a != *b);
    b->Do("(0,0,0)",Player::player1);
    assert(*a == *b);
  }
  //Test operator==
  {
    boost::shared_ptr<BoardAdvanced> a(new BoardAdvanced);
    boost::shared_ptr<BoardAdvanced> b(new BoardAdvanced);
    assert(*a == *b);
    a->Do("(0,0,0)",Player::player1);
    assert(*a != *b);
    b->Do("(0,0,0)",Player::player1);
    assert(*a == *b);
  }
  {
    boost::shared_ptr<BoardAdvanced> a(new BoardAdvanced);
    boost::shared_ptr<BoardBasic> b(new BoardBasic);
    assert(*a != *b);
    a->Do("(0,0,0)",Player::player1);
    assert(*a != *b);
    b->Do("(0,0,0)",Player::player1);
    assert(*a != *b);
  }
  if (verbose) { TRACE("Test Game::Clone of GameBasic"); }
  {
    const boost::shared_ptr<Board> a(new BoardBasic);
    const boost::shared_ptr<Board> b(a->Clone());
    const boost::shared_ptr<Board> c(new BoardAdvanced);
    assert(*a == *b);
    assert(*a != *c);
    a->Do("(0,0,0)",Player::player1);
    assert(*a != *b);
    assert(*a != *c);
    b->Do("(0,0,0)",Player::player1);
    assert(*a == *b);
    assert(*a != *c);
  }
  if (verbose) { TRACE("Test Game::Clone of GameAdvanced"); }
  {
    const boost::shared_ptr<Board> a(new BoardAdvanced);
    const boost::shared_ptr<Board> b(a->Clone());
    const boost::shared_ptr<Board> c(new BoardBasic);
    assert(*a == *b);
    assert(*a != *c);
    a->Do("(0,0,0)",Player::player1);
    assert(*a != *b);
    assert(*a != *c);
    b->Do("(0,0,0)",Player::player1);
    assert(*a == *b);
    assert(*a != *c);
  }
  if (verbose) { TRACE("Test Clone of played GameBasic"); }
  {
    const boost::shared_ptr<Board> a(new BoardBasic);
    a->Do("(0,0,0)",Player::player1);
    const boost::shared_ptr<Board> b(a->Clone());
    assert(*a == *b);
  }
  if (verbose) { TRACE("Test Clone of played BoardAdvanced"); }
  {
    const boost::shared_ptr<Board> a(new BoardAdvanced);
    a->Do("(0,0,0)",Player::player1);
    const boost::shared_ptr<Board> b(a->Clone());
    assert(*a == *b);
  }
  if (verbose) { TRACE("Test conversion of Board to text"); }
  {
    const boost::shared_ptr<Board> a(new BoardAdvanced);
    const boost::shared_ptr<Board> b(new BoardBasic);
    a->Do("(0,0,0)",Player::player1);
    b->Do("(0,0,0)",Player::player1);
    assert(a->ToStr() == std::string(
      "X . . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());
    a->Do("(0,1,0)",Player::player2);
    b->Do("(0,1,0)",Player::player2);
    assert(a->ToStr() == std::string(
      "X O . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());

    a->Do("(0,1,1)",Player::player1);
    b->Do("(0,1,1)",Player::player1);
    if (verbose) { TRACE(a->ToStr()); }
    assert(a->ToStr() == std::string(
      "X O . .\n"
      " . . . \n"
      ". X . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());

    a->Do("(0,0,1)",Player::player2);
    b->Do("(0,0,1)",Player::player2);
    if (verbose) { TRACE(a->ToStr()); }
    assert(a->ToStr() == std::string(
      "X O . .\n"
      " . . . \n"
      "O X . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());

    a->Do("(1,0,0)",Player::player1);
    b->Do("(1,0,0)",Player::player1);
    if (verbose) { TRACE(a->ToStr()); }
    assert(a->ToStr() == std::string(
      "X O . .\n"
      " X . . \n"
      "O X . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());

    a->Do("(0,2,0)",Player::player2);
    b->Do("(0,2,0)",Player::player2);
    if (verbose) { TRACE(a->ToStr()); }
    assert(a->ToStr() == std::string(
      "X O O .\n"
      " X . . \n"
      "O X . .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());

    a->Do("(0,2,1)",Player::player1);
    b->Do("(0,2,1)",Player::player1);
    if (verbose) { TRACE(a->ToStr()); }
    assert(a->ToStr() == std::string(
      "X O O .\n"
      " X . . \n"
      "O X X .\n"
      " . . . \n"
      ". . . .\n"
      " . . . \n"
      ". . . ."));
    assert(a->ToStr() == b->ToStr());
  }
  if (verbose) { TRACE("Test horizontal detection of line in both boards"); }
  {
    const boost::shared_ptr<Board> a(new BoardAdvanced);
    const boost::shared_ptr<Board> b(new BoardBasic);
    MustRemoveState must_remove = MustRemoveState::no;
    a->Set(Coordinat("(0,0,0)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,0)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,1,0)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,1,0)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,0)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,2,0)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,3,0)"),Player::player1,must_remove); assert( must_remove != MustRemoveState::no);
    b->Set(Coordinat("(0,3,0)"),Player::player1,must_remove); assert(!must_remove); //Basic does not test for lines

    a->Set(Coordinat("(0,0,2)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,2)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,1,2)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,1,2)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,2)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,2,2)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,3,2)"),Player::player2,must_remove); assert(must_remove != MustRemoveState::no);
    b->Set(Coordinat("(0,3,2)"),Player::player2,must_remove); assert(!must_remove); //Basic does not test for lines
  }
  if (verbose) { TRACE("Test horizontal detection of line in both boards"); }
  {
    const boost::shared_ptr<Board> a(new BoardAdvanced);
    const boost::shared_ptr<Board> b(new BoardBasic);
    MustRemoveState must_remove = MustRemoveState::no;
    a->Set(Coordinat("(0,2,0)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,2,0)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,1)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,2,1)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,2)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,2,2)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,3)"),Player::player1,must_remove); assert( must_remove != MustRemoveState::no);
    b->Set(Coordinat("(0,2,3)"),Player::player1,must_remove); assert(!must_remove); //Basic does not test for lines

    a->Set(Coordinat("(0,0,0)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,0)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,0,1)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,1)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,0,2)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,2)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,0,3)"),Player::player2,must_remove); assert( must_remove != MustRemoveState::no);
    b->Set(Coordinat("(0,0,3)"),Player::player2,must_remove); assert(!must_remove); //Basic does not test for lines
  }
  if (verbose) { TRACE("Test block detection of line in both boards"); }
  {
    const boost::shared_ptr<Board> a(new BoardAdvanced);
    const boost::shared_ptr<Board> b(new BoardBasic);
    MustRemoveState must_remove = MustRemoveState::no;
    a->Set(Coordinat("(0,0,0)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,0)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,1,0)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,1,0)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,0,1)"),Player::player1,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,0,1)"),Player::player1,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,1,1)"),Player::player1,must_remove); assert( must_remove != MustRemoveState::no);
    b->Set(Coordinat("(0,1,1)"),Player::player1,must_remove); assert( must_remove != MustRemoveState::no);

    a->Set(Coordinat("(0,3,3)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,3,3)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,3,2)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,3,2)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,3)"),Player::player2,must_remove); assert(!must_remove);
    b->Set(Coordinat("(0,2,3)"),Player::player2,must_remove); assert(!must_remove);
    a->Set(Coordinat("(0,2,2)"),Player::player2,must_remove); assert( must_remove != MustRemoveState::no);
    b->Set(Coordinat("(0,2,2)"),Player::player2,must_remove); assert( must_remove != MustRemoveState::no);
  }
  if (verbose) { TRACE("Board test transfer of marbles"); }
  {
    boost::shared_ptr<Board> a = CreateAdvancedBoard();
    boost::shared_ptr<Board> b = CreateBasicBoard();
    a->Do(Move("(0,0,0)"),Player::player1);
    b->Do(Move("(0,0,0)"),Player::player1);
    assert(!a->CanDo(Move("(0,0,0)"),Player::player1));
    assert(!b->CanDo(Move("(0,0,0)"),Player::player1));
    assert(!a->CanDo(Move("(0,0,0)"),Player::player2));
    assert(!b->CanDo(Move("(0,0,0)"),Player::player2));
    assert(!a->CanDo(Move("(0,0,0)->(0,0,1)"),Player::player1));
    assert(!b->CanDo(Move("(0,0,0)->(0,0,1)"),Player::player1));
    assert(!a->CanDo(Move("(0,0,0)->(0,0,1)"),Player::player2));
    assert(!b->CanDo(Move("(0,0,0)->(0,0,1)"),Player::player2));
  }
  if (verbose) { TRACE("Board test stacking by setting"); }
  {
    boost::shared_ptr<Board> a = CreateAdvancedBoard();
    boost::shared_ptr<Board> b = CreateBasicBoard();
    const std::vector<Coordinat> v =
      {
        Coordinat("(0,0,0)"), Coordinat("(0,1,0)"), Coordinat("(0,2,0)"), Coordinat("(0,3,0)"),
        Coordinat("(0,3,1)"), Coordinat("(0,2,1)"), Coordinat("(0,1,1)"), Coordinat("(0,0,1)"),
        Coordinat("(0,0,2)"), Coordinat("(0,1,2)"), Coordinat("(0,2,2)"), Coordinat("(0,3,2)"),
        Coordinat("(0,3,3)"), Coordinat("(0,2,3)"), Coordinat("(0,1,3)"), Coordinat("(0,0,3)"),
        Coordinat("(1,0,0)"), Coordinat("(1,1,0)"), Coordinat("(1,2,0)"),
        Coordinat("(1,0,1)"), Coordinat("(1,1,1)"), Coordinat("(1,2,1)"),
        Coordinat("(1,0,2)"), Coordinat("(1,1,2)"), Coordinat("(1,2,2)"),
        Coordinat("(2,0,0)"), Coordinat("(2,1,0)"),
        Coordinat("(2,0,1)"), Coordinat("(2,1,1)"),
        Coordinat("(3,0,0)")
      };
    Player player = Player::player1;
    std::for_each(v.begin(),v.end(),
      [a,b,&player](const Coordinat& c)
      {
        MustRemoveState must_remove = MustRemoveState::no;
        assert(a->CanSet(c,player));
        assert(b->CanSet(c,player));
        a->Set(c,player,must_remove); assert(!must_remove);
        b->Set(c,player,must_remove); assert(!must_remove);
        Toggle(player);
      }
    );
    assert(a->GetWinner() != Winner::none);
    assert(b->GetWinner() != Winner::none);
  }

  if (verbose) { TRACE("Board test #2"); }
  {
    boost::shared_ptr<Board> a = CreateAdvancedBoard();
    boost::shared_ptr<Board> b = CreateBasicBoard();
    const std::vector<Move> v =
      {
        Move("(0,0,0)"), Move("(0,0,1)"), Move("(0,1,1)"), Move("(0,1,0)"),
        Move("(1,0,0)"), Move("(0,0,2)"), Move("(0,1,2)"), Move("(0,2,0)"),
        Move("(0,2,2)"), Move("(0,3,0)")
      };
    Player player = Player::player1;
    std::for_each(v.begin(),v.end(),
      [a,b,&player](const Move& m)
      {
        #ifndef NDEBUG
        if (!a->CanDo(m,player)) TRACE(m);
        if (!b->CanDo(m,player)) TRACE(m);
        #endif
        assert(a->CanDo(m,player));
        assert(b->CanDo(m,player));
        a->Do(m,player);
        b->Do(m,player);
        Toggle(player);
      }
    );
    assert(!a->CanDo(Move("(0,2,1)"),Player::player1));
    assert(!a->CanDo(Move("(0,2,1) !(0,0,0)"),Player::player1));
    assert( a->CanDo(Move("(0,2,1) !(1,0,0)"),Player::player1));
    assert( a->CanDo(Move("(0,2,1) !(0,0,0) !(1,0,0)"),Player::player1));
    assert( a->CanDo(Move("(0,2,1) !(1,0,0) !(0,0,0)"),Player::player1));
    assert(!a->CanDo(Move("(0,2,1) !(0,1,1)"),Player::player1));
    assert( a->CanDo(Move("(0,2,1) !(0,1,1) !(1,0,0)"),Player::player1));
    assert( a->CanDo(Move("(0,2,1) !(1,0,0) !(0,1,1)"),Player::player1));

    assert(!b->CanDo(Move("(0,2,1)"),Player::player1));
    assert(!b->CanDo(Move("(0,2,1) !(0,0,0)"),Player::player1));
    assert( b->CanDo(Move("(0,2,1) !(1,0,0)"),Player::player1));
    assert( b->CanDo(Move("(0,2,1) !(0,0,0) !(1,0,0)"),Player::player1));
    assert( b->CanDo(Move("(0,2,1) !(1,0,0) !(0,0,0)"),Player::player1));
    assert(!b->CanDo(Move("(0,2,1) !(0,1,1)"),Player::player1));
    assert( b->CanDo(Move("(0,2,1) !(0,1,1) !(1,0,0)"),Player::player1));
    assert( b->CanDo(Move("(0,2,1) !(1,0,0) !(0,1,1)"),Player::player1));
  }

  if (verbose) { TRACE("Board test #3"); }
  {
    boost::shared_ptr<Board> a = CreateAdvancedBoard();
    boost::shared_ptr<Board> b = CreateBasicBoard();
    const std::vector<Move> v =
      {
        Move("(0,0,0)"), Move("(0,1,0)"), Move("(0,0,1)"), Move("(0,1,1)"),
        Move("(0,0,2)"), Move("(0,1,2)")
      };
    Player player = Player::player1;
    std::for_each(v.begin(),v.end(),
      [a,b,&player](const Move& m)
      {
        #ifndef NDEBUG
        if (!a->CanDo(m,player)) TRACE(m);
        if (!b->CanDo(m,player)) TRACE(m);
        #endif
        assert(a->CanDo(m,player));
        assert(b->CanDo(m,player));
        a->Do(m,player);
        b->Do(m,player);
        Toggle(player);
      }
    );

    assert( a->CanDo(Move("(0,0,2)->(1,0,0)"),Player::player1)); //1
    assert(!a->CanDo(Move("(0,0,1)->(1,0,1)"),Player::player1)); //2
    assert(!a->CanDo(Move("(0,0,0)->(0,2,0)"),Player::player1)); //3

    assert( b->CanDo(Move("(0,0,2)->(1,0,0)"),Player::player1)); //1
    assert(!b->CanDo(Move("(0,0,1)->(1,0,1)"),Player::player1)); //2
    assert(!b->CanDo(Move("(0,0,0)->(0,2,0)"),Player::player1)); //3
  }


  if (verbose) { TRACE("Test ribi::pylos::Board::GetAllPossibleMoves simple transfer"); }
  {
    boost::shared_ptr<Board> a = CreateAdvancedBoard();
    boost::shared_ptr<Board> b = CreateBasicBoard();
    assert(a->GetAllPossibleMoves(Player::player1).size() == 16);
    assert(b->GetAllPossibleMoves(Player::player1).size() == 16);
    assert(a->GetAllPossibleMoves(Player::player2).size() == 16);
    assert(b->GetAllPossibleMoves(Player::player2).size() == 16);
    a->Do(Move("(0,0,0)"),Player::player1); //1
    b->Do(Move("(0,0,0)"),Player::player1);
    assert(a->GetAllPossibleMoves(Player::player1).size() == 15);
    assert(b->GetAllPossibleMoves(Player::player1).size() == 15);
    assert(a->GetAllPossibleMoves(Player::player2).size() == 15);
    assert(b->GetAllPossibleMoves(Player::player2).size() == 15);
    a->Do(Move("(0,0,1)"),Player::player2); //2
    b->Do(Move("(0,0,1)"),Player::player2);
    a->Do(Move("(0,1,1)"),Player::player1); //3
    b->Do(Move("(0,1,1)"),Player::player1);
    a->Do(Move("(0,1,0)"),Player::player2); //4
    b->Do(Move("(0,1,0)"),Player::player2);
    assert(a->Count(PositionState::player1) == 2);
    assert(b->Count(PositionState::player1) == 2);
    assert(a->Count(PositionState::player2) == 2);
    assert(b->Count(PositionState::player2) == 2);
    assert(a->GetAllPossibleMoves(Player::player1).size() == 13); //13
    assert(b->GetAllPossibleMoves(Player::player1).size() == 13); //12 on bottom layer
    assert(a->GetAllPossibleMoves(Player::player2).size() == 13); //1 on one-but-buttom layer
    assert(b->GetAllPossibleMoves(Player::player2).size() == 13);
    a->Do(Move("(0,2,0)"),Player::player1);
    b->Do(Move("(0,2,0)"),Player::player1);
    a->Do(Move("(0,2,1)"),Player::player2);
    b->Do(Move("(0,2,1)"),Player::player2);
    assert(a->Get(Coordinat(0,2,1)) == PositionState::player2);
    assert(b->Get(Coordinat(0,2,1)) == PositionState::player2);
    assert(a->GetAllPossibleMoves(Player::player1).size() == 14); //13
    assert(b->GetAllPossibleMoves(Player::player1).size() == 14); //10 on bottom layer
    assert(a->GetAllPossibleMoves(Player::player2).size() == 14); //2 on one-but-buttom layer
    assert(b->GetAllPossibleMoves(Player::player2).size() == 14); //2 transfers
  }
  if (verbose) { TRACE("Test ribi::pylos::Board::GetAllPossibleMoves simple remove"); }
  {
    boost::shared_ptr<Board> a = CreateAdvancedBoard();
    boost::shared_ptr<Board> b = CreateBasicBoard();
    a->Do(Move("(0,0,0)"),Player::player1);
    b->Do(Move("(0,0,0)"),Player::player1);
    a->Do(Move("(0,0,1)"),Player::player1);
    b->Do(Move("(0,0,1)"),Player::player1);
    a->Do(Move("(0,1,0)"),Player::player1);
    b->Do(Move("(0,1,0)"),Player::player1);
    //After placing (0,1,1), there are one (4 ways) or two marbles (6 ways) to be removed
    //Next to this, there are 12 empty spots
    assert(a->GetAllPossibleMoves(Player::player1).size() == 22);
    assert(b->GetAllPossibleMoves(Player::player1).size() == 22);
    assert(a->GetAllPossibleMoves(Player::player2).size() == 13);
    assert(b->GetAllPossibleMoves(Player::player2).size() == 13);
  }

  if (testing_depth < 2) return;

  if (verbose) { TRACE("Filling up 5 basic Pylos boards randomly"); }
  for (int i=0; i!=5; ++i)
  {
    ribi::pylos::Board::PlayRandomPylosGame(pylos::Board::CreateBasicBoard());
  }
  if (verbose) { TRACE("Filling up 5 advanced Pylos boards randomly"); }
  for (int i=0; i!=5; ++i)
  {
    ribi::pylos::Board::PlayRandomPylosGame(ribi::pylos::Board::CreateAdvancedBoard());
  }
  if (verbose) { TRACE("Filling up 5 Pylos boards randomly"); }
  for (int i=0; i!=5; ++i)
  {
    ribi::pylos::Board::PlayRandomPylosGame();
  }
}
#endif

std::string ribi::pylos::Board::ToStr() const noexcept
{
  const std::vector<std::string> v = this->ToText();
  std::string s;
  std::for_each(v.begin(),v.end(),[&s](const std::string& t) { s += t; s += '\n'; } );
  s.resize( s.size() - 1);
  return s;
}

std::vector<std::string> ribi::pylos::Board::ToText() const noexcept
{
  std::vector<std::string> v(7,std::string(7,' '));
  for (int layer = 0; layer!=4; ++layer)
  {
    for (int y=0; y!=4-layer; ++y)
    {
      for (int x=0; x!=4-layer; ++x)
      {
        const int x_co = layer + (x * 2);
        const int y_co = layer + (y * 2);
        const Coordinat c(layer,x,y);
        assert(c.GetX() == x);
        assert(c.GetY() == y);
        assert(c.GetLayer() == layer);
        const char c_old = v[y_co][x_co];
        const char c_new = ToChar(Get(c));
        //Higher empty positions must not overwrite occupied lower ones
        if (c_new == ToChar(PositionState::empty)
          && ( c_old == ToChar(PositionState::player1)
            || c_old == ToChar(PositionState::player2) ) )
        {
          continue;
        }
        v[y_co][x_co] = c_new;
      }
    }
  }
  return v;
}

boost::shared_ptr<ribi::TextCanvas> ribi::pylos::Board::ToTextCanvas() const noexcept
{
  boost::shared_ptr<TextCanvas> canvas {
    new TextCanvas(7,7)
  };
  for (int layer = 0; layer!=4; ++layer)
  {
    for (int y=0; y!=4-layer; ++y)
    {
      for (int x=0; x!=4-layer; ++x)
      {
        const int x_co = layer + (x * 2);
        const int y_co = layer + (y * 2);
        const Coordinat c(layer,x,y);
        assert(c.GetX() == x);
        assert(c.GetY() == y);
        assert(c.GetLayer() == layer);
        const char c_old = canvas->GetChar(x_co,y_co);
        const char c_new = ToChar(Get(c));
        //Higher empty positions must not overwrite occupied lower ones
        if (c_new == ToChar(PositionState::empty)
          && ( c_old == ToChar(PositionState::player1)
            || c_old == ToChar(PositionState::player2) ) )
        {
          continue;
        }
        canvas->PutChar(x_co,y_co,c_new);
      }
    }
  }

  return canvas;
}


void ribi::pylos::Board::Transfer(
    const Coordinat& from,
    const Coordinat& to,
    MustRemoveState& must_remove)
{
  const PositionState state = Get(from);
  assert(state!=PositionState::empty);
  const Player player = ToPlayer(state);
  assert(CanTransfer(from,player));
  assert(CanSet(to,player));
  assert(CanTransfer(from,to,player));
  Remove(from,player);
  Set(to,player,must_remove);
}

bool ribi::pylos::operator==(const Board& lhs, const Board& rhs) noexcept
{
  //Determine if types are equal
  if (typeid(lhs)!=typeid(rhs)) return false;

  return lhs.m_board == rhs.m_board;
}

bool ribi::pylos::operator!=(const Board& lhs, const Board& rhs) noexcept
{
  return !(lhs==rhs);
}

std::ostream& ribi::pylos::operator<<(std::ostream& os,const Board& p) noexcept
{
  os << p.ToStr();
  return os;
}

ribi::pylos::BoardAdvanced::BoardAdvanced() noexcept
  : Board()
{

}

ribi::pylos::BoardAdvanced::~BoardAdvanced()
{
  //OK
}

std::unique_ptr<ribi::pylos::Board> ribi::pylos::BoardAdvanced::Clone() const noexcept
{
  return std::make_unique<BoardAdvanced>(*this);
}

void ribi::pylos::BoardAdvanced::Set(
  const Coordinat& c,
  const Player player,
  MustRemoveState& must_remove)
{
  assert(CanSet(c,player));

  const PositionState state = ToPositionState(player);
  m_board[c.GetLayer()][c.GetX()][c.GetY()] = state;
  assert(Get(c)==state);

  must_remove = MustRemoveState::no;

  //Check for squares
  {
    typedef std::vector<Coordinat> Square;
    const std::vector<Square> v = GetSquares(c);
    assert(v.size() < 5);
    bool do_return = false;
    std::for_each(v.begin(),v.end(),
      [this,&do_return,state](const Square& s)
      {
        if (!do_return)
        {
          bool success = true;
          std::for_each(s.begin(),s.end(),
            [this,&success,state](const Coordinat& d)
            {
              if (success) { if (Get(d)!=state) { success = false; } }
            }
          );
          if (success) { do_return = true; }
        }
      }
    );
    if (do_return) { must_remove = ToMustRemoveState(player); return; }
  }
  //Check for lines
  {
    typedef std::vector<Coordinat> Line;
    const std::vector<Line> v = GetLines(c);
    ///There will be zero (layer 2 and 3)
    ///or two lines (layer 0 and 1)
    assert(v.size() == 0 || v.size() == 2);
    if (std::find_if(v.begin(),v.end(),
      [this,state](const Line& s)
      {
        return std::find_if(
          s.begin(),s.end(),
            [this,state](const Coordinat& d)
            {
              return Get(d)!=state;
            }
          ) == s.end();
      }
    ) != v.end()) { must_remove = ToMustRemoveState(player); return; }
  }

}

ribi::pylos::BoardBasic::BoardBasic() noexcept
  : Board()
{

}

ribi::pylos::BoardBasic::~BoardBasic()
{
  //OK
}

std::unique_ptr<ribi::pylos::Board> ribi::pylos::BoardBasic::Clone() const noexcept
{
  return std::make_unique<BoardBasic>(*this);
}

void ribi::pylos::BoardBasic::Set(
  const Coordinat& c,
  const Player player,
  MustRemoveState& must_remove)
{
  assert(CanSet(c,player));

  const PositionState state = ToPositionState(player);
  m_board[c.GetLayer()][c.GetX()][c.GetY()] = state;
  assert(Get(c)==state);

  //Check for squares
  {
    typedef std::vector<Coordinat> Square;
    const std::vector<Square> v = GetSquares(c);
    if (std::find_if(
      v.begin(),v.end(),
        [this,state](const Square& s)
        {
          return std::find_if(
            s.begin(),s.end(),
            [this,state](const Coordinat& d)
            {
              return Get(d) != state;
            }
          ) == s.end();
        }
      ) != v.end())
    {
      must_remove = ToMustRemoveState(player);
      return;
    }
  }
  must_remove = MustRemoveState::no;
  return;
}

 

 

 

 

 

./CppPylos/pyloscoordinat.h

 

//---------------------------------------------------------------------------
/*
pylos::Coordinat, Pylos/Phyraos coordinat 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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSCOORDINAT_H
#define PYLOSCOORDINAT_H

#include <iosfwd>
#include <string>
#include <vector>

namespace ribi {

namespace pylos {

struct Coordinat
{
  Coordinat(const int layer, const int x, const int y);

  ///Construct a Coordinat from a std::string.
  ///For example, (0,1,2) is the coordinat Z=0=bottom,X=1=leftmost-but-one,Y=2=frontmost-but-two
  Coordinat(const std::string& s);

  ///Obtain the layer or Z-coordinat
  ///0: bottom 4x4 layer, 3: top 1x1 layer
  int GetLayer() const noexcept { return m_layer; }

  ///Obtain this class its version
  static std::string GetVersion() noexcept;

  ///Obtain this class its version history
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Obtain this coordinat its X coordinat
  ///0: Left
  int GetX() const noexcept { return m_x; }

  ///Obtain this coordinat its Y coordinat
  ///0: Top
  int GetY() const noexcept { return m_y; }

  ///IsValid returns if the proposed coordinat is a valid Pylos coordinat
  static bool IsValid(const int layer, const int x, const int y) noexcept;

  ///IsValid returns if this coordinat is a valid Pylos coordinat
  bool IsValid() const noexcept;

  ///ToStr() converts the coordinat to a std::string
  ///of the form '(layer,x,y)'.
  std::string ToStr() const noexcept;

  private:
  ///Not const, so that copying is possible
  int m_layer;
  int m_x;
  int m_y;

  #ifndef NDEBUG
  ///Test this class
  static void Test() noexcept;
  #endif

};

bool operator==(const Coordinat& lhs, const Coordinat& rhs) noexcept;
bool operator!=(const Coordinat& lhs, const Coordinat& rhs) noexcept;
std::ostream& operator<<(std::ostream& os,const Coordinat& c) noexcept;

///GetAbove returns the coordinats physically
///above the entered coordinat
const std::vector<Coordinat> GetAbove(
  const Coordinat& c);

///GetAllPylosCoordinats returns all possible PylosCoordinats
const std::vector<Coordinat> GetAllCoordinats() noexcept;

///GetBelow returns the four coordinats physically
///below the entered coordinat
const std::vector<Coordinat> GetBelow(
  const Coordinat& c);

///GetLines returns the possible 2x2 squares around the coordinat
const std::vector<std::vector<Coordinat> > GetLines(
  const Coordinat& c);

///GetRandomPylosCoordinat returns a random valid PylosCoordinat
const Coordinat GetRandomCoordinat() noexcept;

///GetSquares returns the possible 2x2 squares around the coordinat
const std::vector<std::vector<Coordinat> > GetSquares(
  const Coordinat& c);

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSCOORDINAT_H

 

 

 

 

 

./CppPylos/pyloscoordinat.cpp

 

//---------------------------------------------------------------------------
/*
pylos::Coordinat, Pylos/Phyraos coordinat 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/CppPylos.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "pyloscoordinat.h"

#include <algorithm>
#include <cassert>

#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>

#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::pylos::Coordinat::Coordinat(
  const int layer,
  const int x,
  const int y)
  : m_layer(layer),
    m_x(x),
    m_y(y)
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(IsValid(layer,x,y));
}

ribi::pylos::Coordinat::Coordinat(const std::string& s)
  : m_layer{boost::lexical_cast<int>(s.at(1))},
    m_x{boost::lexical_cast<int>(s.at(3))},
    m_y{boost::lexical_cast<int>(s.at(5))}
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(s.size() == 7);
  #ifndef DEBUG
  if (!IsValid(m_layer,m_x,m_y)) { TRACE(s); }
  #endif
  assert(IsValid(m_layer,m_x,m_y));
}

bool ribi::pylos::Coordinat::IsValid(const int layer, const int x, const int y) noexcept
{
  return layer >= 0 && layer < 4
    && x >= 0 && x < 4-layer
    && y >= 0 && y < 4-layer;
}

const std::vector<ribi::pylos::Coordinat> ribi::pylos::GetAllCoordinats() noexcept
{
  std::vector<Coordinat> v;
  for (int z=0; z!=4; ++z)
  {
    for (int y=0; y!=4-z; ++y)
    {
      for (int x=0; x!=4-z; ++x)
      {
        v.push_back(Coordinat(z,x,y));
      }
    }
  }
  assert(v.size() == 30);
  return v;
}

std::string ribi::pylos::Coordinat::GetVersion() noexcept
{
  return "2.0";
}

std::vector<std::string> ribi::pylos::Coordinat::GetVersionHistory() noexcept
{
  return {
    "2012-05-05: version 2.0: initial release version"
  };
}

bool ribi::pylos::Coordinat::IsValid() const noexcept
{
  return IsValid(m_layer,m_x,m_y);
}

std::string ribi::pylos::Coordinat::ToStr() const noexcept
{
  return "("
    + boost::lexical_cast<std::string>(m_layer)
    + ","
    + boost::lexical_cast<std::string>(m_x)
    + ","
    + boost::lexical_cast<std::string>(m_y)
    + ")";

}

const std::vector<ribi::pylos::Coordinat> ribi::pylos::GetAbove(
  const Coordinat& c)
{
  const int layer = c.GetLayer() + 1;
  const int x = c.GetX();
  const int y = c.GetY();
  //Just get all coordinats
  std::vector<Coordinat> v;
  if (ribi::pylos::Coordinat::IsValid(layer,x-0,y-0)) v.push_back(Coordinat(layer,x-0,y-0));
  if (ribi::pylos::Coordinat::IsValid(layer,x-0,y-1)) v.push_back(Coordinat(layer,x-0,y-1));
  if (ribi::pylos::Coordinat::IsValid(layer,x-1,y-0)) v.push_back(Coordinat(layer,x-1,y-0));
  if (ribi::pylos::Coordinat::IsValid(layer,x-1,y-1)) v.push_back(Coordinat(layer,x-1,y-1));
  return v;
}

const std::vector<ribi::pylos::Coordinat> ribi::pylos::GetBelow(
  const Coordinat& c)
{
  assert(c.IsValid());
  assert(c.GetLayer()!=0);
  const int layer = c.GetLayer() - 1;
  const int x = c.GetX();
  const int y = c.GetY();
  std::vector<Coordinat> v;
  v.push_back(Coordinat(layer,x+0,y+0));
  v.push_back(Coordinat(layer,x+1,y+0));
  v.push_back(Coordinat(layer,x+0,y+1));
  v.push_back(Coordinat(layer,x+1,y+1));
  return v;
}

const std::vector<std::vector<ribi::pylos::Coordinat> > ribi::pylos::GetLines(
  const Coordinat& c)
{
  typedef std::vector<Coordinat> Line;
  std::vector<Line> v;
  const int layer = c.GetLayer();
  if (layer == 2 || layer == 3) return v;
  const int x = c.GetX();
  const int y = c.GetY();
  {
    Line horizontal;
    Line vertical;
    for (int i=0; i!=4-layer; ++i)
    {
      horizontal.push_back(Coordinat(layer,i,y));
      vertical.push_back(Coordinat(layer,x,i));
    }
    assert(boost::numeric_cast<int>(horizontal.size()) == 4-layer);
    assert(boost::numeric_cast<int>(vertical.size()) == 4-layer);
    v.push_back(horizontal);
    v.push_back(vertical);
  }
  assert(v.size() == 2);
  return v;
}

const ribi::pylos::Coordinat ribi::pylos::GetRandomCoordinat() noexcept
{
  const int layer = ( std::rand() >> 4) % 4;
  const int x = ( std::rand() >> 4) % (4 - layer);
  const int y = ( std::rand() >> 4) % (4 - layer);
  assert(ribi::pylos::Coordinat::IsValid(layer,x,y));
  return Coordinat(layer,x,y);
}

const std::vector<std::vector<ribi::pylos::Coordinat> > ribi::pylos::GetSquares(
  const Coordinat& c)
{
  typedef std::vector<Coordinat> Square;
  std::vector<Square> v;
  const int layer = c.GetLayer();
  assert(layer >= 0);
  assert(layer < 4);
  const int x = c.GetX();
  const int y = c.GetY();
  const int sz = 4 - layer;


  if (x > 0 && y > 0)
  {
    Square s;
    s.push_back(Coordinat(layer,x-0,y-0));
    s.push_back(Coordinat(layer,x-0,y-1));
    s.push_back(Coordinat(layer,x-1,y-0));
    s.push_back(Coordinat(layer,x-1,y-1));
    v.push_back(s);
  }
  if (x + 1 < sz && y > 0)
  {
    Square s;
    s.push_back(Coordinat(layer,x+0,y-0));
    s.push_back(Coordinat(layer,x+0,y-1));
    s.push_back(Coordinat(layer,x+1,y-0));
    s.push_back(Coordinat(layer,x+1,y-1));
    v.push_back(s);
  }
  if (x > 0 && y + 1 < sz)
  {
    Square s;
    s.push_back(Coordinat(layer,x-0,y+0));
    s.push_back(Coordinat(layer,x-0,y+1));
    s.push_back(Coordinat(layer,x-1,y+0));
    s.push_back(Coordinat(layer,x-1,y+1));
    v.push_back(s);
  }
  if (x + 1 < sz && y + 1 < sz)
  {
    Square s;
    s.push_back(Coordinat(layer,x+0,y+0));
    s.push_back(Coordinat(layer,x+0,y+1));
    s.push_back(Coordinat(layer,x+1,y+0));
    s.push_back(Coordinat(layer,x+1,y+1));
    v.push_back(s);
  }
  return v;
}

#ifndef NDEBUG
void ribi::pylos::Coordinat::Test() noexcept
{
  {
    static bool tested = false;
    if (tested) return;
    tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};

  if (verbose) { TRACE("Test PylosCoordinat operators"); }
  {
    const Coordinat c1(0,2,2);
    const Coordinat c2(0,2,3);
    const Coordinat c3(0,3,2);
    const Coordinat c1_too(0,2,2);
    assert(c1 != c2);
    assert(c1 != c3);
    assert(c1 == c1_too);
    assert(c2 != c3);
  }
  if (verbose) { TRACE("Test Coordinat GetBelow function on (1,0,1)"); }
  {
    const std::vector<Coordinat> v
      = GetBelow(Coordinat(1,0,1));
    assert(v.size() == 4);
    assert(std::find(v.begin(),v.end(),Coordinat(0,0,1))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(0,0,2))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(0,1,1))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(0,1,2))
      != v.end());
  }
  if (verbose) { TRACE("Test Coordinat GetBelow function on (1,0,2)"); }
  {
    const std::vector<Coordinat> v
      = GetBelow(Coordinat(1,0,2));
    assert(v.size() == 4);
    assert(std::find(v.begin(),v.end(),Coordinat(0,0,2))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(0,0,3))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(0,1,2))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(0,1,3))
      != v.end());
  }
  if (verbose) { TRACE("Test Coordinat GetAbove function on (0,0,0)"); }
  {
    const std::vector<Coordinat> v
      = GetAbove(Coordinat(0,0,0));
    assert(v.size() == 1);
    assert(std::find(v.begin(),v.end(),Coordinat(1,0,0))
      != v.end());
  }
  if (verbose) { TRACE("Test Coordinat GetAbove function on (0,1,2)"); }
  {
    const std::vector<Coordinat> v
      = GetAbove(Coordinat(0,1,2));
    assert(v.size() == 4);
    assert(std::find(v.begin(),v.end(),Coordinat(1,0,1))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(1,0,2))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(1,1,1))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(1,1,2))
      != v.end());
  }
  if (verbose) { TRACE("Test Coordinat GetAbove function on (1,2,1)"); }
  {
    const std::vector<Coordinat> v
      = GetAbove(Coordinat(1,2,1));
    assert(v.size() == 2);
    assert(std::find(v.begin(),v.end(),Coordinat(2,1,0))
      != v.end());
    assert(std::find(v.begin(),v.end(),Coordinat(2,1,1))
      != v.end());
  }
  if (verbose) { TRACE("Test Coordinat GetAbove function on (2,0,0)"); }
  {
    const std::vector<Coordinat> v
      = GetAbove(Coordinat(2,0,0));
    assert(v.size() == 1);
    assert(std::find(v.begin(),v.end(),Coordinat(3,0,0))
      != v.end());
  }
}
#endif

bool ribi::pylos::operator==(const Coordinat& lhs, const Coordinat& rhs) noexcept
{
  return lhs.GetLayer() == rhs.GetLayer()
    && lhs.GetX() == rhs.GetX()
    && lhs.GetY() == rhs.GetY();
}

bool ribi::pylos::operator!=(const Coordinat& lhs, const Coordinat& rhs) noexcept
{
  return !(lhs == rhs);
}

std::ostream& ribi::pylos::operator<<(std::ostream& os,const Coordinat& c) noexcept
{
  os << c.ToStr();
  return os;
}

 

 

 

 

 

./CppPylos/pyloscurrentmovestate.h

 

//---------------------------------------------------------------------------
/*
pylos::CurrentMoveState, Pylos/Phyraos current move state 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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSCURRENTMOVESTATE_H
#define PYLOSCURRENTMOVESTATE_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/checked_delete.hpp>

#include "pyloscoordinat.h"
#include "pylosmove.h"
#include "pylosmustremovestate.h"
#pragma GCC diagnostic pop

namespace ribi {

namespace pylos {

///CurrentMoveState contains the state of the current Move.
///CurrentMoveState is used by pylos::Game, to incrementally keep track of the
///ongoing move
struct CurrentMoveState
{
  CurrentMoveState() noexcept;

  ///Obtain the current Move in process
  const Move& GetMove() const noexcept { return m_current_move; }

  ///Obtain if the first part of the move has taken place and
  ///if the current player must remove one or two marbles
  MustRemoveState GetMustRemove() const noexcept;

  ///Obtain this class its version
  static std::string GetVersion() noexcept;

  ///Obtain this class its version history
  static std::vector<std::string> GetVersionHistory() noexcept;


  ///Is it unknown what kind of Move this is?
  bool IsMoveUnknown() const;
  bool IsMoveMove() const;
  bool IsMovePlace() const;

  void Restart() noexcept;

  ///Set a complete Move directly
  void SetMove(const Move& move);

  ///Set the transfer part of a move,enabling
  void SetMoveTransfer(const Coordinat& from, const Coordinat& to);

  void SetMoveSet(const Coordinat& c);

  ///Set the duty of one or none of the players
  void SetMustRemove(const MustRemoveState must_remove);
  void SetRemove(const std::vector<Coordinat>& v);


  private:
  ~CurrentMoveState() noexcept {}
  friend void boost::checked_delete<>(      CurrentMoveState*);
  friend void boost::checked_delete<>(const CurrentMoveState*);

  Move m_current_move;
  MustRemoveState m_must_remove;

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

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

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSCURRENTMOVESTATE_H

 

 

 

 

 

./CppPylos/pyloscurrentmovestate.cpp

 

//---------------------------------------------------------------------------
/*
pylos::CurrentMoveState, Pylos/Phyraos current move state 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/CppPylos.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "pyloscurrentmovestate.h"

#include <cassert>

#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::pylos::CurrentMoveState::CurrentMoveState() noexcept
  : m_current_move{},
    m_must_remove(MustRemoveState::no)
{
  #ifndef NDEBUG
  Test();
  #endif

  assert(IsMoveUnknown());
  //assert(IsRemoveUnknown());
}

std::string ribi::pylos::CurrentMoveState::GetVersion() noexcept
{
  return "2.0";
}

std::vector<std::string> ribi::pylos::CurrentMoveState::GetVersionHistory() noexcept
{
  return {
    "2012-05-05: version 2.0: initial release version"
  };
}

bool ribi::pylos::CurrentMoveState::IsMoveMove() const
{
  assert(m_current_move.m_move.size() < 3);
  return m_current_move.m_move.size() == 2;
}

bool ribi::pylos::CurrentMoveState::IsMovePlace() const
{
  assert(m_current_move.m_move.size() < 3);
  return m_current_move.m_move.size() == 1;
}

bool ribi::pylos::CurrentMoveState::IsMoveUnknown() const
{
  assert(m_current_move.m_move.size() < 3);
  return m_current_move.m_move.size() == 0;
}

ribi::pylos::MustRemoveState ribi::pylos::CurrentMoveState::GetMustRemove() const noexcept
{
  return m_must_remove;
}

void ribi::pylos::CurrentMoveState::Restart() noexcept
{
  m_must_remove = MustRemoveState::no;
  m_current_move = Move();
  assert(IsMoveUnknown());
}

void ribi::pylos::CurrentMoveState::SetMove(const Move& move)
{
  assert(m_current_move.m_move.empty());
  assert(m_current_move.m_remove.empty());
  m_current_move = move;
  m_must_remove = MustRemoveState::no;
}

void ribi::pylos::CurrentMoveState::SetMoveTransfer(const Coordinat& from, const Coordinat& to)
{
  assert(IsMoveUnknown());
  assert(m_current_move.m_move.empty());
  m_current_move.m_move.push_back(from);
  m_current_move.m_move.push_back(to);
}

void ribi::pylos::CurrentMoveState::SetMoveSet(const Coordinat& c)
{
  assert(IsMoveUnknown());
  assert(m_current_move.m_move.empty());
  m_current_move.m_move.push_back(c);
}

void ribi::pylos::CurrentMoveState::SetMustRemove(const MustRemoveState must_remove)
{
  assert(!(must_remove == MustRemoveState::player2
    &&   m_must_remove == MustRemoveState::player1)
    && "Cannot set duty to remove from player 1 to player 2");
  assert(!(must_remove == MustRemoveState::player1
    &&   m_must_remove == MustRemoveState::player2)
    && "Cannot set duty to remove from player 2 to player 1");
  m_must_remove = must_remove;
}

void ribi::pylos::CurrentMoveState::SetRemove(const std::vector<Coordinat>& v)
{
  assert(m_must_remove != MustRemoveState::no);
  m_current_move.m_remove = v;
}

#ifndef NDEBUG
void ribi::pylos::CurrentMoveState::Test() noexcept
{
  {
    static bool tested = false;
    if (tested) return;
    tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};

  if (verbose) { TRACE("Test PylosCurrentMoveState"); }
  {
    const Coordinat c(0,1,1);
    pylos::CurrentMoveState s;
    assert(s.IsMoveUnknown());
    assert(!s.GetMustRemove());
    s.SetMoveSet(c);
    assert(!s.IsMoveUnknown());
    const pylos::Move m = s.GetMove();
    assert(m.m_move.size() == 1);
    assert(m.m_move[0] == c);
    assert(m.m_remove.empty());
    s.Restart();
    pylos::CurrentMoveState t;
    assert(s == t);
  }
}
#endif

bool ribi::pylos::operator==(const CurrentMoveState& lhs, const CurrentMoveState& rhs) noexcept
{
  return lhs.GetMustRemove() == rhs.GetMustRemove()
    && lhs.GetMove() == rhs.GetMove();
}

 

 

 

 

 

./CppPylos/pylosfwd.h

 

//---------------------------------------------------------------------------
/*
pylosfwd.h, forward declarations of Pylos/Phyraos classes
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSFWD_H
#define PYLOSFWD_H

namespace ribi {
namespace pylos {

enum class Player;
enum class PositionState;
enum class MustRemoveState;

struct QtSprites;

} //~namespace Pylos
} //~namespace ribi

#endif // PYLOSFWD_H

 

 

 

 

 

./CppPylos/pylosgame.h

 

//---------------------------------------------------------------------------
/*
pylos::Game, class for a game of Pylos/Phyraos
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSGAME_H
#define PYLOSGAME_H

#include <iosfwd>
#include <vector>

#include <boost/shared_ptr.hpp>

#include "pylosboard.h"
#include "pyloscurrentmovestate.h"
#include "pylosfwd.h"
#include "pylosplayer.h"
#include "pylospositionstate.h"
#include "pyloswinner.h"

namespace ribi {
namespace pylos {

///Pylos is a class for storing a Pylos game.
///Games can be player in:\n
///- Pylos notation: (Can)Do\n
///- using Coordinats: (Can)Place, (Can)Move, (Can)Remove\n
///Games can also be saved and loaded.
struct Game
{
  ///Copy constructor
  Game(const Game& rhs);

  ///Create an Pylos game in the starting position.
  Game(const boost::shared_ptr<Board>& board);

  ///Create a mid-game Pylos.
  Game(
    const boost::shared_ptr<Board>& board,
    const Player current_player,
    const std::vector<pylos::Move>& move_history,
    const boost::shared_ptr<CurrentMoveState>& current_move);

  ///CanDo determines if a Pylos notation move is valid
  bool CanDo(const std::string& s) const;

  ///CanDo determines if a Pylos move is valid
  bool CanDo(const pylos::Move& m) const;

  ///CanRemove specifies if current player can remove one or
  ///two marble(s) at the requested position(s).
  bool CanRemove(const std::vector<Coordinat>& v) const;

  ///CanSet tests if the current player can be set at the Coordinat
  bool CanSet(const Coordinat& c) const;

  ///CanTransfer specifies if current player can transfer
  ///the marble at the specified coordinat for movement
  bool CanTransfer(const Coordinat& c) const;

  ///CanTransfer specifies if current player can transfer his marble
  ///to a new, higher position
  bool CanTransfer(
    const Coordinat& from,
    const Coordinat& to) const;

  ///Create an advanced game at its initial position
  static boost::shared_ptr<Game> CreateAdvancedGame();

  ///Create a basic game at its initial position
  static boost::shared_ptr<Game> CreateBasicGame();

  ///Do performs a move in Pylos notation
  void Do(const std::string& s);

  ///Do performs a Pylos move
  void Do(const Move& m);

  ///GetAllPossibleMoves returns all moves valid for the active player
  const std::vector<Move> GetAllPossibleMoves() const noexcept;

  ///GetBoard returns the board.
  const boost::shared_ptr<Board>& GetBoard() const noexcept { return m_board; }

  ///Obtain the current move's state
  const boost::shared_ptr<CurrentMoveState> GetCurrentMove() const noexcept { return m_current_move; }

  ///GetCurrentTurn returns whose turn it is now
  Player GetCurrentTurn() const noexcept;

  const std::vector<pylos::Move>& GetMoveHistory() const noexcept
  {
    return m_move_history;
  }

  ///MustRemove returns whether the current player
  ///must remove one or two marbles
  MustRemoveState GetMustRemove() const noexcept;

  ///Obtain this class its version
  static std::string GetVersion() noexcept;

  ///Obtain this class its version history
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///GetWinner returns the winner.
  Winner GetWinner() const noexcept;

  ///IsValid returns if the current Pylos game is valid
  //bool IsValid() const; //?Not used?

  ///Load loads a game in Pylos notation
  //void Load(const std::string& s) = 0;

  ///PlayRandomPylosGame plays a random Pylos game and returns the winner.
  static Winner PlayRandomGame(const boost::shared_ptr<Board>& board = boost::shared_ptr<Board>());


  ///Remove lets the current player remove one or two marbles
  void Remove(const std::vector<Coordinat>& v);

  ///Restart sets the game in its initial position.
  void Restart();

  ///Set makes m_current_players place his marble
  ///at the specified position. After Place,
  ///MustRemoveMarbles must be called to determine if
  ///the current player must remove some marbles
  void Set(const Coordinat& c);


  ///ToStr converts Game its contents to a std::string
  //const std::string ToStr() const = 0;

  ///Transfer lets current player tranfer his marble to a new, higher position
  void Transfer(
    const Coordinat& from,
    const Coordinat& to);

  private:
  Game() = delete;
  ~Game() noexcept {}
  friend void boost::checked_delete<>(      Game*);
  friend void boost::checked_delete<>(const Game*);

  boost::shared_ptr<Board> m_board;
  boost::shared_ptr<CurrentMoveState> m_current_move;
  Player m_current_player;
  std::vector<pylos::Move> m_move_history;

  ///CanRemove specifies if current player can remove
  ///the marble at the requested position.
  bool CanRemove(const Coordinat& c) const;

  ///Remove lets the current player remove one marble.
  void Remove(const Coordinat& c);

  #ifndef NDEBUG
  ///Test the Game class
  static void Test() noexcept;
  #endif

  ///TogglePlayer toggles between player1 and player2 and saves the current
  ///move to the move history
  void TogglePlayer();

  friend bool operator==(const Game& lhs, const Game& rhs);
};

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

///CanLoadPylos determines if loading a Pylos game from file
///will be successfull
bool CanLoadPylos(const std::string& filename);

///LoadPylos loads a Pylos game from file
boost::shared_ptr<Game> LoadPylos(const std::string& filename);

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSGAME_H

 

 

 

 

 

./CppPylos/pylosgame.cpp

 

//---------------------------------------------------------------------------
/*
pylos::Game, class for a game of Pylos/Phyraos
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/CppPylos.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"

#include "pylosgame.h"

#include <algorithm>
#include <cassert>
#include <iostream>

#include <boost/numeric/conversion/cast.hpp>
#include <boost/shared_ptr.hpp>

#include "pylosboard.h"
#include "pyloscurrentmovestate.h"
#include "testtimer.h"
#include "pylosmove.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::pylos::Game::Game(const Game& rhs)
  : m_board(rhs.m_board->Clone()),
    m_current_move(new CurrentMoveState(*rhs.m_current_move)),
    m_current_player(rhs.m_current_player),
    m_move_history(rhs.m_move_history)
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(m_board != rhs.m_board);
  assert(m_current_move != rhs.m_current_move);
}

ribi::pylos::Game::Game(const boost::shared_ptr<Board> &board)
  : m_board(board),
    m_current_move(new CurrentMoveState),
    m_current_player(Player::player1),
    m_move_history{}
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(m_board);
}

bool ribi::pylos::Game::CanDo(const pylos::Move& m) const
{
  return m_board->CanDo(m,m_current_player);
}

bool ribi::pylos::Game::CanDo(const std::string& s) const
{
  return m_board->CanDo(s,m_current_player);
}

bool ribi::pylos::Game::CanRemove(const Coordinat& c) const
{
  if (m_board->Get(c) != m_current_player) return false;

  return m_board->CanRemove( std::vector<Coordinat>(1,c), m_current_player);
}

bool ribi::pylos::Game::CanRemove(const std::vector<Coordinat>& v) const
{
  assert(!v.empty());
  if (std::count_if(v.begin(),v.end(),
    [this](const Coordinat& c)
    {
      return m_board->Get(c) != m_current_player;
    }
  ) > 0) return false;

  return m_board->CanRemove(v, m_current_player);
}

bool ribi::pylos::Game::CanSet(const Coordinat& c) const
{
  return m_board->CanSet(c,m_current_player);
}

bool ribi::pylos::Game::CanTransfer(const Coordinat& c) const
{
  if (m_board->Get(c) != GetCurrentTurn()) return false;

  return m_board->CanTransfer(c,m_current_player);
}

bool ribi::pylos::Game::CanTransfer(const Coordinat& from,
  const Coordinat& to) const
{
  if (m_board->Get(from) != GetCurrentTurn()) return false;

  return m_board->CanTransfer(from,to,m_current_player);
}

void ribi::pylos::Game::Do(const std::string& s)
{
  #ifndef NDEBUG
  if (!CanDo(s)) TRACE(s);
  #endif
  assert(CanDo(s));
  Do(pylos::Move(s));
}

void ribi::pylos::Game::Do(const pylos::Move& m)
{
  assert(CanDo(m));
  m_board->Do(m,m_current_player);
  m_current_move->SetMove(m);
  TogglePlayer();
}

boost::shared_ptr<ribi::pylos::Game> ribi::pylos::Game::CreateAdvancedGame()
{
  boost::shared_ptr<Board> board(Board::CreateAdvancedBoard());
  return boost::shared_ptr<Game>(new Game(board));
}

boost::shared_ptr<ribi::pylos::Game> ribi::pylos::Game::CreateBasicGame()
{
  boost::shared_ptr<Board> board(Board::CreateBasicBoard());
  return boost::shared_ptr<Game>(new Game(board));
}

const std::vector<ribi::pylos::Move> ribi::pylos::Game::GetAllPossibleMoves() const noexcept
{
  return m_board->GetAllPossibleMoves(m_current_player);
}

ribi::pylos::Player ribi::pylos::Game::GetCurrentTurn() const noexcept
{
  return m_current_player;
}

std::string ribi::pylos::Game::GetVersion() noexcept
{
  return "2.0";
}

std::vector<std::string> ribi::pylos::Game::GetVersionHistory() noexcept
{
  return {
    "2010-09-19: version 0.1: initial version",
    "2010-09-21: version 0.2: use of Coordinat",
    "2010-09-21: version 0.3: added Move",
    "2010-09-22: version 1.0: fixed bug in Coordinat, added move history",
    "2012-05-05: version 2.0: major achitectural rewrite"
  };
}

ribi::pylos::Winner ribi::pylos::Game::GetWinner() const noexcept
{
  return m_board->GetWinner();
}

ribi::pylos::MustRemoveState ribi::pylos::Game::GetMustRemove() const noexcept
{
  return m_current_move->GetMustRemove();
}

ribi::pylos::Winner ribi::pylos::Game::PlayRandomGame(const boost::shared_ptr<Board>& board)
{
  boost::shared_ptr<Game> p;
  if (board)
  {
    p.reset(new Game(board->Clone()));
  }
  else
  {
    if ((std::rand() >> 4) % 2)
      p.reset(new Game(Board::CreateAdvancedBoard()));
    else
      p.reset(new Game(Board::CreateBasicBoard()));
  }
  while (1)
  {
    if (p->GetWinner() != Winner::none)
    {
      return p->GetWinner();
    }
    //Choose algorithm to draw next move
    if ((std::rand() >> 4) % 2)
    {
      //Use random move from GetAllPossibleMoves
      const std::vector<Move> v = p->GetAllPossibleMoves();
      const int i = std::rand() % v.size();
      //TRACE(v[i]);
      p->Do(v[i]);
    }
    else
    {
      //Use random coordinats and random responses
      const Coordinat c = GetRandomCoordinat();
      if (p->CanTransfer(c))
      {
        std::vector<pylos::Coordinat> v = pylos::GetAllCoordinats();
        std::random_shuffle(std::begin(v),std::end(v));
        const std::size_t sz = v.size();
        for (std::size_t i = 0; i!=sz; ++i)
        {
          if (p->CanTransfer(c,v[i]))
          {
            p->Transfer(c,v[i]);
            break;
          }
        }
      }
      else if (p->CanSet(c))
      {
         p->Set(c);
      }
      while (p->GetMustRemove() != MustRemoveState::no)
      {
        std::vector<Coordinat> v;
        v.push_back(GetRandomCoordinat());
        if ((std::rand() >> 4) % 2) v.push_back(GetRandomCoordinat());
        if (p->CanRemove(v)) p->Remove(v);
      }
    }
  }
}

void ribi::pylos::Game::Remove(const Coordinat& c)
{
  assert(GetMustRemove() != MustRemoveState::no);
  assert(m_board->Get(c)==m_current_player);
  assert(m_board->CanRemove(std::vector<Coordinat>(1,c),m_current_player));
  m_board->Remove(std::vector<Coordinat>(1,c),m_current_player);
  ///\warning: do not allow these lines: this member function
  ///is an internal function used by
  ///void ribi::pylos::Game::Remove(const std::vector<Coordinat>& v).
  //m_must_remove = false;
  //TogglePlayer();

}

void ribi::pylos::Game::Remove(const std::vector<Coordinat>& v)
{
  //Cannot call RemoveMarbles(c), because this
  //also toggles the player
  assert(GetMustRemove() != MustRemoveState::no);
  assert(CanRemove(v));
  assert(v.size() == 1 || v.size() == 2);
  assert(m_board->Get(v[0]) == m_current_player);
  assert(v.size() == 1 || m_board->Get(v[1]) == m_current_player);
  assert(v.size() == 1 || v[0] != v[1]);
  //Proper ordering: v[0] must be marble above
  if (v.size() == 2 && v[0].GetLayer() < v[1].GetLayer())
  {
    std::vector<Coordinat> w(v);
    std::swap(w[0],w[1]);
    Remove(w); //Call the same member function with different order
    return;
  }
  //Assert proper ordering
  assert(v.size() == 1 || v[0].GetLayer() >= v[1].GetLayer());
  std::for_each(v.begin(),v.end(),
    [this](const Coordinat& c)
    {
      assert(m_board->Get(c)==m_current_player);
      this->Remove(c);
    }
  );

  ///Three lines below must be executed exactly once per move
  m_current_move->SetRemove(v);
  m_current_move->SetMustRemove(MustRemoveState::no);
  TogglePlayer();
}

void ribi::pylos::Game::Set(const Coordinat& c)
{
  #ifndef NDEBUG
  if (!CanSet(c)) TRACE(c);
  #endif
  //Set is always done as the first part of a Move
  assert(CanSet(c));

  MustRemoveState must_remove = GetMustRemove();
  assert(!must_remove && "Player must not remove a marble when placing a new one");
  m_board->Set(c,m_current_player,must_remove);
  if (must_remove != MustRemoveState::no) m_current_move->SetMustRemove(
    ToMustRemoveState(m_current_player));

  m_current_move->SetMoveSet(c);

  if (must_remove != MustRemoveState::no)
  {
    const Player player = ToPlayer(m_board->Get(c));
    m_current_move->SetMustRemove(ToMustRemoveState(player));
  }
  else
  {
    //If player must not remove another marble,
    //his/her turn is over
    TogglePlayer();
  }
}

#ifndef NDEBUG
void ribi::pylos::Game::Test() noexcept
{
  {
    static bool tested = false;
    if (tested) return;
    tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  const int testing_depth = 1;

  if (verbose) { TRACE("Test ribi::pylos::Game::operator== for different game types"); }
  {
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateBasicGame();
    assert(*a != *b);
    a->Set(Coordinat("(0,0,0)"));
    assert(*a != *b);
    b->Do("(0,0,0)");
    assert(*a != *b);
  }
  if (verbose) { TRACE("Test ribi::pylos::Game::operator== for same game types"); }
  {
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateAdvancedGame();
    assert(*a == *b);
    a->Do("(0,0,0)");
    assert(*a != *b);
    b->Do("(0,0,0)");
    assert(*a == *b);
  }
  if (verbose) { TRACE("Test basic Game dynamics using Set and Remove"); }
  {
    // 1
    // 1
    // 1 22
    // 1 22
    // Only advanced game must detect player 1's line
    // Both games must detect player 2's square
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateBasicGame();
    assert(*a != *b);
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    a->Set(Coordinat("(0,0,0)"));
    b->Set(Coordinat("(0,0,0)"));
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Set(Coordinat("(0,3,0)"));
    b->Set(Coordinat("(0,3,0)"));
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    a->Set(Coordinat("(0,0,1)"));
    b->Set(Coordinat("(0,0,1)"));
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Set(Coordinat("(0,2,0)"));
    b->Set(Coordinat("(0,2,0)"));
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    a->Set(Coordinat("(0,0,2)"));
    b->Set(Coordinat("(0,0,2)"));
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Set(Coordinat("(0,2,1)"));
    b->Set(Coordinat("(0,2,1)"));
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    //BoardAdvanced responds to the creation of a line
    a->Set(Coordinat("(0,0,3)")); //Line
    b->Set(Coordinat("(0,0,3)")); //Line
    assert(a->GetCurrentTurn() == Player::player1); //No toggle
    assert(b->GetCurrentTurn() == Player::player2); //Toggle
    assert( a->GetCurrentMove()->GetMustRemove() != MustRemoveState::no);
    assert(!b->GetCurrentMove()->GetMustRemove());
    assert( a->CanRemove(std::vector<Coordinat>(1,Coordinat("(0,0,0)"))));
    assert(!b->CanRemove(std::vector<Coordinat>(1,Coordinat("(0,0,0)"))));
    a->Remove(std::vector<Coordinat>(1,Coordinat("(0,0,0)")));
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    assert(a->GetCurrentTurn() == Player::player2); //Toggle
    assert(b->GetCurrentTurn() == Player::player2);

    //BoardAdvanced and BoardBasic respond to the creation of a square
    a->Set(Coordinat("(0,3,1)")); //Square
    b->Set(Coordinat("(0,3,1)")); //Square
    assert(a->GetCurrentTurn() == Player::player2); //No toggle
    assert(b->GetCurrentTurn() == Player::player2); //No toggle
    assert(a->GetCurrentMove()->GetMustRemove() != MustRemoveState::no);
    assert(b->GetCurrentMove()->GetMustRemove() != MustRemoveState::no);
    assert(a->CanRemove(std::vector<Coordinat>(1,Coordinat("(0,3,0)"))));
    assert(b->CanRemove(std::vector<Coordinat>(1,Coordinat("(0,3,0)"))));
    a->Remove(std::vector<Coordinat>(1,Coordinat("(0,3,0)")));
    b->Remove(std::vector<Coordinat>(1,Coordinat("(0,3,0)")));
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
  }
  if (verbose) { TRACE("Test ribi::pylos::Game::Clone of GameBasic"); }
  {
    boost::shared_ptr<Game> a = CreateBasicGame();
    boost::shared_ptr<Game> b(new Game(*a));
    boost::shared_ptr<Game> c = CreateAdvancedGame();
    assert(*a == *b);
    assert(*a != *c);
    a->Set(Coordinat("(0,0,0)"));
    assert(*a != *b);
    assert(*a != *c);
    b->Set(Coordinat("(0,0,0)"));
    assert(*a == *b);
    assert(*a != *c);
    c->Set(Coordinat("(0,0,0)"));
    assert(*a != *c);
    assert(*b != *c);
  }
  if (verbose) { TRACE("Test ribi::pylos::Game::Clone of GameAdvanced"); }
  {
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b(new Game(*a));
    boost::shared_ptr<Game> c = CreateBasicGame();
    assert(*a == *b);
    assert(*a != *c);
    a->Set(Coordinat("(0,0,0)"));
    assert(*a != *b);
    assert(*a != *c);
    b->Set(Coordinat("(0,0,0)"));
    assert(*a == *b);
    assert(*a != *c);
  }
  if (verbose) { TRACE("Test Clone of played GameBasic"); }
  {
    boost::shared_ptr<Game> a = CreateBasicGame();
    a->Set(Coordinat("(0,0,0)"));
    const boost::shared_ptr<Game> b(new Game(*a));
    assert(*a == *b);
    b->Set(Coordinat("(0,1,0)"));
    assert(*a != *b);
  }
  if (verbose) { TRACE("Test Clone of played BoardAdvanced"); }
  {
    const boost::shared_ptr<Game> a = CreateAdvancedGame();
    a->Set(Coordinat("(0,0,0)"));
    const boost::shared_ptr<Game> b(new Game(*a));
    assert(*a == *b);
    b->Set(Coordinat("(0,1,0)"));
    assert(*a != *b);
  }
  if (verbose) { TRACE("Test basic Game dynamics using full moves"); }
  {
    // 1
    // 1
    // 1 22
    // 1 22
    // Only advanced game must detect player 1's line
    // Both games must detect player 2's square
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateBasicGame();
    assert(*a != *b);
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Do(Move("(0,0,0)"));
    b->Do(Move("(0,0,0)"));
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Do(Move("(0,3,0)"));
    b->Do(Move("(0,3,0)"));
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    a->Do(Move("(0,0,1)"));
    b->Do(Move("(0,0,1)"));
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Do(Move("(0,2,0)"));
    b->Do(Move("(0,2,0)"));
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    a->Do(Move("(0,0,2)"));
    b->Do(Move("(0,0,2)"));
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    a->Do(Move("(0,2,1)"));
    b->Do(Move("(0,2,1)"));
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());

    //BoardAdvanced responds to the creation of a line
    assert(!a->CanDo("(0,0,3)"));
    assert(a->CanDo("(0,0,3) !(0,0,0)"));
    assert(a->CanDo("(0,0,3) !(0,0,1)"));
    assert(a->CanDo("(0,0,3) !(0,0,2)"));
    assert(a->CanDo("(0,0,3) !(0,0,3)"));
    assert(a->CanDo("(0,0,3) !(0,0,0) !(0,0,1)"));
    assert(a->CanDo("(0,0,3) !(0,0,0) !(0,0,2)"));
    assert(a->CanDo("(0,0,3) !(0,0,0) !(0,0,3)"));
    assert(a->CanDo("(0,0,3) !(0,0,1) !(0,0,0)"));
    assert(a->CanDo("(0,0,3) !(0,0,1) !(0,0,2)"));
    assert(a->CanDo("(0,0,3) !(0,0,1) !(0,0,3)"));
    assert(a->CanDo("(0,0,3) !(0,0,2) !(0,0,0)"));
    assert(a->CanDo("(0,0,3) !(0,0,2) !(0,0,1)"));
    assert(a->CanDo("(0,0,3) !(0,0,2) !(0,0,3)"));
    assert(a->CanDo("(0,0,3) !(0,0,3) !(0,0,0)"));
    assert(a->CanDo("(0,0,3) !(0,0,3) !(0,0,1)"));
    assert(a->CanDo("(0,0,3) !(0,0,3) !(0,0,2)"));
    assert(!a->CanDo("(0,0,3) !(0,0,0) !(0,0,0)"));
    assert(!a->CanDo("(0,0,3) !(0,0,1) !(0,0,1)"));
    assert(!a->CanDo("(0,0,3) !(0,0,2) !(0,0,2)"));
    assert(!a->CanDo("(0,0,3) !(0,0,3) !(0,0,3)"));
    assert(b->CanDo("(0,0,3)"));
    assert(!b->CanDo("(0,0,3) !(0,0,0)"));
    assert(!b->CanDo("(0,0,3) !(0,0,1)"));
    assert(!b->CanDo("(0,0,3) !(0,0,2)"));
    assert(!b->CanDo("(0,0,3) !(0,0,3)"));
    assert(!b->CanDo("(0,0,3) !(0,0,0) !(0,0,1)"));
    assert(!b->CanDo("(0,0,3) !(0,0,0) !(0,0,2)"));
    assert(!b->CanDo("(0,0,3) !(0,0,0) !(0,0,3)"));
    assert(!b->CanDo("(0,0,3) !(0,0,1) !(0,0,0)"));
    assert(!b->CanDo("(0,0,3) !(0,0,1) !(0,0,2)"));
    assert(!b->CanDo("(0,0,3) !(0,0,1) !(0,0,3)"));
    assert(!b->CanDo("(0,0,3) !(0,0,2) !(0,0,0)"));
    assert(!b->CanDo("(0,0,3) !(0,0,2) !(0,0,1)"));
    assert(!b->CanDo("(0,0,3) !(0,0,2) !(0,0,3)"));
    assert(!b->CanDo("(0,0,3) !(0,0,3) !(0,0,0)"));
    assert(!b->CanDo("(0,0,3) !(0,0,3) !(0,0,1)"));
    assert(!b->CanDo("(0,0,3) !(0,0,3) !(0,0,2)"));
    assert(!b->CanDo("(0,0,3) !(0,0,0) !(0,0,0)"));
    assert(!b->CanDo("(0,0,3) !(0,0,1) !(0,0,1)"));
    assert(!b->CanDo("(0,0,3) !(0,0,2) !(0,0,2)"));
    assert(!b->CanDo("(0,0,3) !(0,0,3) !(0,0,3)"));
    a->Do(Move("(0,0,3) !(0,0,0)")); //Line
    b->Do(Move("(0,0,3)")); //Line
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    assert(a->GetCurrentTurn() == Player::player2);
    assert(b->GetCurrentTurn() == Player::player2);

    //BoardAdvanced and BoardBasic respond to the creation of a square
    a->Set(Coordinat("(0,3,1)")); //Square
    b->Set(Coordinat("(0,3,1)")); //Square
    assert(a->GetCurrentTurn() == Player::player2); //No toggle
    assert(b->GetCurrentTurn() == Player::player2); //No toggle
    assert(a->GetCurrentMove()->GetMustRemove() != MustRemoveState::no);
    assert(b->GetCurrentMove()->GetMustRemove() != MustRemoveState::no);
    assert(a->CanRemove(std::vector<Coordinat>(1,Coordinat("(0,3,0)"))));
    assert(b->CanRemove(std::vector<Coordinat>(1,Coordinat("(0,3,0)"))));
    a->Remove(std::vector<Coordinat>(1,Coordinat("(0,3,0)")));
    b->Remove(std::vector<Coordinat>(1,Coordinat("(0,3,0)")));
    assert(!a->GetCurrentMove()->GetMustRemove());
    assert(!b->GetCurrentMove()->GetMustRemove());
    assert(a->GetCurrentTurn() == Player::player1);
    assert(b->GetCurrentTurn() == Player::player1);
  }


  if (verbose) { TRACE("Test Game history"); }
  {
    // 12..
    // 34..
    // 56..
    // 7...
    //Test that in basic play, no marbles must be removed. Test that in advanced play, marbles must be removed.
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateBasicGame();
    assert(a->GetMoveHistory().empty());
    assert(b->GetMoveHistory().empty());
    const std::vector<Move> v =
      {
        Move("(0,0,0)"),
        Move("(0,1,0)"),
        Move("(0,0,1)"),
        Move("(0,1,1)"),
        Move("(1,0,0)")
      };
    const std::size_t j = v.size();
    for (std::size_t i = 0; i!=j; ++i)
    {
      assert(a->GetMoveHistory().size() == i);
      assert(b->GetMoveHistory().size() == i);
      a->Do(v[i]);
      b->Do(v[i]);
      assert(a->GetMoveHistory()[i] == v[i]);
      assert(b->GetMoveHistory()[i] == v[i]);
    }
  }


  if (verbose) { TRACE("Game test #1"); }
  {
    // 12..
    // 34..
    // 56..
    // 7...
    //Test that in basic play, no marbles must be removed. Test that in advanced play, marbles must be removed.
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateBasicGame();
    a->Do(Move("(0,0,0)")); //1
    b->Do(Move("(0,0,0)"));
    a->Do(Move("(0,1,0)")); //2
    b->Do(Move("(0,1,0)"));
    a->Do(Move("(0,0,1)")); //3
    b->Do(Move("(0,0,1)"));
    a->Do(Move("(0,1,1)")); //4
    b->Do(Move("(0,1,1)"));
    a->Do(Move("(0,0,2)")); //5
    b->Do(Move("(0,0,2)"));
    a->Do(Move("(0,1,2)")); //6
    b->Do(Move("(0,1,2)"));
    assert(!a->CanDo(Move("(0,0,3)"))); //7
    assert( a->CanDo(Move("(0,0,3) !(0,0,3)"))); //7
    assert( b->CanDo(Move("(0,0,3)")));
  }
  if (verbose) { TRACE("Game test #2"); }
  {
    // ....
    // ....
    // ....
    // ....
    //Test that in basic play, no marbles must be removed. Test that in advanced play, marbles must be removed.
    boost::shared_ptr<Game> a = CreateAdvancedGame();
    boost::shared_ptr<Game> b = CreateBasicGame();
    const std::vector<Move> v =
      {
        Move("(0,0,0)"), Move("(0,0,1)"), Move("(0,1,1)"), Move("(0,1,0)"),
        Move("(1,0,0)"), Move("(0,0,2)"), Move("(0,1,2)"), Move("(0,2,0)"),
        Move("(0,2,2)"), Move("(0,3,0)")
      };
    std::for_each(v.begin(),v.end(),
      [a,b](const Move& m)
      {
        #ifndef NDEBUG
        if (!a->CanDo(m)) TRACE(m);
        if (!b->CanDo(m)) TRACE(m);
        #endif
        assert(a->CanDo(m));
        assert(b->CanDo(m));
        a->Do(m);
        b->Do(m);
      }
    );
    assert(!a->CanDo(Move("(0,2,1)")));
    assert(!a->CanDo(Move("(0,2,1) !(0,0,0)")));
    assert( a->CanDo(Move("(0,2,1) !(1,0,0)")));
    assert( a->CanDo(Move("(0,2,1) !(0,0,0) !(1,0,0)")));
    assert( a->CanDo(Move("(0,2,1) !(1,0,0) !(0,0,0)")));
    assert(!a->CanDo(Move("(0,2,1) !(0,1,1)")));
    assert( a->CanDo(Move("(0,2,1) !(0,1,1) !(1,0,0)")));
    assert( a->CanDo(Move("(0,2,1) !(1,0,0) !(0,1,1)")));

    assert(!b->CanDo(Move("(0,2,1)")));
    assert(!b->CanDo(Move("(0,2,1) !(0,0,0)")));
    assert( b->CanDo(Move("(0,2,1) !(1,0,0)")));
    assert( b->CanDo(Move("(0,2,1) !(0,0,0) !(1,0,0)")));
    assert( b->CanDo(Move("(0,2,1) !(1,0,0) !(0,0,0)")));
    assert(!b->CanDo(Move("(0,2,1) !(0,1,1)")));
    assert( b->CanDo(Move("(0,2,1) !(0,1,1) !(1,0,0)")));
    assert( b->CanDo(Move("(0,2,1) !(1,0,0) !(0,1,1)")));
  }
  if (testing_depth < 2) return;

  if (verbose) { TRACE("Playing 5 random basic Pylos games"); }
  for (int i=0; i!=5; ++i)
  {
    ribi::pylos::Game::PlayRandomGame(pylos::Board::CreateBasicBoard());
  }
  if (verbose) { TRACE("Playing 5 random advanced Pylos games"); }
  for (int i=0; i!=5; ++i)
  {
    ribi::pylos::Game::PlayRandomGame(pylos::Board::CreateAdvancedBoard());
  }
  if (verbose) { TRACE("Playing 5 random Pylos games"); }
  for (int i=0; i!=5; ++i)
  {
    ribi::pylos::Game::PlayRandomGame();
  }
}
#endif

void ribi::pylos::Game::TogglePlayer()
{
  assert(!m_current_move->GetMustRemove()
    && "First the current player must remove one or two marbles");
  m_move_history.push_back(m_current_move->GetMove());
  m_current_move.reset(new CurrentMoveState);
  m_current_player = (m_current_player == Player::player1 ? Player::player2 : Player::player1);
}

void ribi::pylos::Game::Transfer(
    const Coordinat& from,
    const Coordinat& to)
{
  assert(CanTransfer(from,to));

  m_board->Remove(std::vector<Coordinat>(1,from),m_current_player);
  MustRemoveState must_remove = MustRemoveState::no;
  m_board->Set(to,m_current_player,must_remove);

  m_current_move->SetMoveTransfer(from,to);

  if (must_remove != MustRemoveState::no)
  {
    const Player player = ToPlayer(m_board->Get(to));
    m_current_move->SetMustRemove(ToMustRemoveState(player));
  }
  else
  {
    //If player must not remove another marble,
    //his/her turn is over
    TogglePlayer();
  }
}

bool ribi::pylos::operator==(const Game& lhs, const Game& rhs)
{
  return *lhs.m_board          == *rhs.m_board
    &&   *lhs.m_current_move   == *rhs.m_current_move
    &&    lhs.m_current_player ==  rhs.m_current_player
    &&    lhs.m_move_history   ==  rhs.m_move_history;
}

bool ribi::pylos::operator!=(const Game& lhs, const Game& rhs)
{
  return !(lhs == rhs);
}

 

 

 

 

 

./CppPylos/pylosmove.h

 

//---------------------------------------------------------------------------
/*
pylos::Move, class for a Pylos/Phyraos move
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSMOVE_H
#define PYLOSMOVE_H

#include <iosfwd>
#include <string>
#include <vector>

#include "pyloscoordinat.h"

namespace ribi {

namespace pylos {

///PylosMove encapsulates a Pylos move.
///A Pylos move starts with either
/// - placing (Set) a new marble or
/// - moving (Transfer) an existing marble to a higher location
///Both a Set and a Transfer can results in that the player must
///remove either one or two marbles.
struct Move
{
  Move();

  ///Move is constructed from all the members it will hold
  Move(
    const std::vector<Coordinat>& moving,
    const std::vector<Coordinat>& removing);

  ///Move is constructed from Pylos notation
  Move(const std::string& s);

  ///Obtain this class its version
  static std::string GetVersion() noexcept;

  ///Obtain this class its version history
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Test if this Move is valid
  bool IsValid() const noexcept;

  std::string ToStr() const noexcept;

  ///What m_move is, depends on its size:
  ///m_move.size() == 1: placement at m_move[0]
  ///m_move.size() == 2: move from m_move[0] to m_move[1]
  std::vector<Coordinat> m_move;
  std::vector<Coordinat> m_remove;

  private:
  #ifndef NDEBUG
  ///Test this class
  static void Test() noexcept;
  #endif

};

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

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSMOVE_H

 

 

 

 

 

./CppPylos/pylosmove.cpp

 

//---------------------------------------------------------------------------
/*
pylos::Move, class for a Pylos/Phyraos move
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/CppPylos.htm
//---------------------------------------------------------------------------


#include "pylosmove.h"

#include <cassert>
#include <iostream>

#include "pyloscoordinat.h"
#include "testtimer.h"
#include "trace.h"


ribi::pylos::Move::Move()
  : m_move{},
    m_remove{}
{
  #ifndef NDEBUG
  Test();
  #endif
}

ribi::pylos::Move::Move(
  const std::vector<Coordinat>& moving,
  const std::vector<Coordinat>& removing)
  : m_move{moving},
    m_remove{removing}
{
  #ifndef NDEBUG
  Test();
  #endif

  assert(!m_move.empty()
    && "In every move a marble must be placed or transferred");
  assert(m_move.size() <= 2);
  assert(m_remove.size() <= 2
    && "At most two marbles are removed in a move");
}

ribi::pylos::Move::Move(const std::string& s)
  : m_move{},
    m_remove{}
{
  #ifndef NDEBUG
  Test();
  #endif

  #ifndef NDEBUG
  if (!(
       s.size() == 7
    || s.size() == 16
    || s.size() == 25
    || s.size() == 34)) TRACE(s);
  #endif

  //Read first coordinat
  assert(
       s.size() == 7
    || s.size() == 16
    || s.size() == 25
    || s.size() == 34);
  {
    const Coordinat c(s.substr(0,7));
    m_move.push_back(c);
    if (s.size() == 7) return;
  }
  //Test for transfer with/without removal '(Z,X,Y)->(Z,X,Y)'
  if (s[7] == '-' && s[8] == '>')
  {
    const Coordinat c(s.substr(9,7));
    m_move.push_back(c);
    if (s.size() == 16) return;
  }
  //Test for place and single removal '(Z,X,Y) !(Z,X,Y)'
  if (s[7] == ' ' && s[8] == '!')
  {
    const Coordinat c(s.substr(9,7));
    m_remove.push_back(c);
    if (s.size() == 16) return;
  }
  //Test for transfer with single removal '(Z,X,Y)->(Z,X,Y) !(Z,X,Y)'
  assert(s[16] == ' ' && s[17] == '!');
  {
    const Coordinat c(s.substr(18,7));
    m_remove.push_back(c);
    if (s.size() == 25) return;
  }
  //Test for transfer with double removal '(Z,X,Y)->(Z,X,Y) !(Z,X,Y) !(Z,X,Y)'
  assert(s[25] == ' ' && s[26] == '!');
  {
    const Coordinat c(s.substr(27,7));
    m_remove.push_back(c);
  }
  assert(s.size() == 34);
}

std::string ribi::pylos::Move::GetVersion() noexcept
{
  return "2.0";
}

std::vector<std::string> ribi::pylos::Move::GetVersionHistory() noexcept
{
  return {
    "2012-05-05: version 2.0: initial release version"
  };
}

bool ribi::pylos::Move::IsValid() const noexcept
{
  return
       m_move.size() >= 1
    && m_move.size() <= 2
    && m_remove.size() <= 2;
}

#ifndef NDEBUG
void ribi::pylos::Move::Test() noexcept
{
  {
    static bool tested = false;
    if (tested) return;
    tested = true;
  }
  {
    Coordinat(0,0,0);
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  {
    pylos::Move m;
    assert(!m.IsValid() && "An empty move is invalid");
    pylos::Move n;
    assert(m == n);
    m.m_move.push_back(Coordinat(0,0,0));
    n.m_move.push_back(Coordinat(0,0,0));
    assert(m == n);
  }
  {
    //Valid Moves
    const std::vector<std::string> v
    =
      {
        "(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,0)->(0,0,0) !(0,0,0) !(0,0,0)",
      };
    std::for_each(v.begin(),v.end(),
      [](const std::string& s)
      {
        try
        {
          const Move m(s);
          //OK
        }
        catch (std::exception& e)
        {
          TRACE(s);
          assert(!"Should not get here");
        }
      }
    );
  }
}
#endif

std::string ribi::pylos::Move::ToStr() const noexcept
{
  #ifndef NDEBUG
  if (!(m_move.size() == 1 || m_move.size() == 2)) TRACE(m_move.size());

  #endif
  assert(m_move.size() == 1 || m_move.size() == 2);

  std::string s = m_move[0].ToStr();
  if (m_move.size() == 2)
  {
    s+="->";
    s+=m_move[1].ToStr();
  }
  if (m_remove.empty()) return s;
  assert(m_remove.size() == 1 || m_remove.size() == 2);
  s+=" !";
  s+=m_remove[0].ToStr();
  if (m_remove.size() == 2)
  {
    s+=" !";
    s+=m_remove[0].ToStr();
  }
  return s;
}

bool ribi::pylos::operator==(const Move& lhs, const Move& rhs) noexcept
{
  return lhs.m_move == rhs.m_move
    && lhs.m_remove == rhs.m_remove;
}

std::ostream& ribi::pylos::operator<<(std::ostream& os, const Move& m) noexcept
{
  os << m.ToStr();
  return os;
}

 

 

 

 

 

./CppPylos/pylosmustremovestate.h

 

//---------------------------------------------------------------------------
/*
pylos::MustRemoveState, Pylos enum class for the state of a removal
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSMUSTREMOVESTATE_H
#define PYLOSMUSTREMOVESTATE_H

#include <string>

#include "pylosfwd.h"

namespace ribi {

namespace pylos {

///After placing (Set) or moving (Transfer) a marble, the MustRemoveState
///will be removed. These are:
///- no: no marbles need to be removed before next player's move
///- player1: player1 has to remove one or two marbles. This is determined
///  by the player that places or moves his/her marbles.
///- player2: player2 has to remove one or two marbles. This is determined
///  by the player that places or moves his/her marbles.
enum class MustRemoveState { player1, player2, no };
MustRemoveState ToMustRemoveState(const Player& player);
std::string ToStr(const MustRemoveState state);

//bool operator==(const MustRemoveState& state, const MustRemoveState& player);
//bool operator!=(const MustRemoveState& state, const MustRemoveState& player);
bool operator!(const MustRemoveState& state);

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSMUSTREMOVESTATE_H

 

 

 

 

 

./CppPylos/pylosmustremovestate.cpp

 

//---------------------------------------------------------------------------
/*
pylos::MustRemoveState, Pylos enum class for the state of a removal
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/CppPylos.htm
//---------------------------------------------------------------------------


#include "pylosmustremovestate.h"

#include <cassert>
#include <stdexcept>

#include "pylosplayer.h"



ribi::pylos::MustRemoveState ribi::pylos::ToMustRemoveState(const Player& player)
{
  switch (player)
  {
    case Player::player1: return MustRemoveState::player1;
    case Player::player2: return MustRemoveState::player2;
  }
  assert(!"Unknown value for Player");
  throw std::logic_error("Unknown value for Player");
}

std::string ribi::pylos::ToStr(const MustRemoveState state)
{
  switch (state)
  {
    case MustRemoveState::player1: return "player1";
    case MustRemoveState::player2: return "player2";
    case MustRemoveState::no: return "no";
  }
  assert(!"Unknown value for MustRemoveState");
  throw std::logic_error("Unknown value for MustRemoveState");
}

bool ribi::pylos::operator!(const MustRemoveState& state)
{
  return state == MustRemoveState::no;
}

 

 

 

 

 

./CppPylos/pylosplayer.h

 

//---------------------------------------------------------------------------
/*
pylos::Player, Pylos enum class for the players
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSPLAYER_H
#define PYLOSPLAYER_H

#include "pylosfwd.h"
#include "pylospositionstate.h"

namespace ribi {

namespace pylos {

enum class Player { player1, player2 };
///Toggles between the two players
void Toggle(Player& player);
///Convert a PositionState to Player
Player ToPlayer(const PositionState& state);

bool operator==(const Player& player, const PositionState& state);
bool operator!=(const Player& player, const PositionState& state);

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSPLAYER_H

 

 

 

 

 

./CppPylos/pylosplayer.cpp

 

//---------------------------------------------------------------------------
/*
pylos::Player, Pylos enum class for the players
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/CppPylos.htm
//---------------------------------------------------------------------------


#include "pylosplayer.h"

#include <cassert>
#include <stdexcept>

#include "pylospositionstate.h"



void ribi::pylos::Toggle(Player& player)
{
  player = (player == Player::player1 ? Player::player2 : Player::player1);
}

ribi::pylos::Player ribi::pylos::ToPlayer(const PositionState& state)
{
  switch (state)
  {
    case PositionState::player1: return Player::player1;
    case PositionState::player2: return Player::player2;
    default: break;
  }
  assert(!"Should not get here");
  throw std::logic_error("Cannot convert this PositionState to a Player");
}

bool ribi::pylos::operator==(const Player& player, const PositionState& state)
{
  switch (player)
  {
    case Player::player1: return state == PositionState::player1;
    case Player::player2: return state == PositionState::player2;
  }
  return false;
}

bool ribi::pylos::operator!=(const Player& player, const PositionState& state)
{
  return !(player==state);
}

 

 

 

 

 

./CppPylos/pylospositionstate.h

 

//---------------------------------------------------------------------------
/*
pylos::PositionState, Pylos enum class for the state of a position
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSPOSITIONSTATE_H
#define PYLOSPOSITIONSTATE_H

#include "pylosfwd.h"
//#include "pylosplayer.h"

namespace ribi {

namespace pylos {

enum class PositionState { player1, player2, empty };
PositionState ToPositionState(const Player& player);
char ToChar(const PositionState state);

bool operator==(const PositionState state, const Player& player);
bool operator!=(const PositionState state, const Player& player);
//bool operator!(const PositionState& state);

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSPOSITIONSTATE_H

 

 

 

 

 

./CppPylos/pylospositionstate.cpp

 

//---------------------------------------------------------------------------
/*
pylos::PositionState, Pylos enum class for the state of a position
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/CppPylos.htm
//---------------------------------------------------------------------------


#include "pylospositionstate.h"

#include <cassert>
#include <stdexcept>

#include "pylosplayer.h"


char ribi::pylos::ToChar(const PositionState state)
{
  switch (state)
  {
    case PositionState::empty: return '.';
    case PositionState::player1: return 'X';
    case PositionState::player2: return 'O';
  }
  assert(!"Should not get here");
  throw std::logic_error("Unknown PositionState in ToChar");
}

ribi::pylos::PositionState ribi::pylos::ToPositionState(const Player& player)
{
  switch (player)
  {
    case Player::player1: return PositionState::player1;
    case Player::player2: return PositionState::player2;
    //default: break;
  }
  assert(!"Should not get here");
  throw std::logic_error("Cannot convert this Player to a PositionState");
}

bool ribi::pylos::operator==(const PositionState state, const Player& player)
{
  return player == state;
}

bool ribi::pylos::operator!=(const PositionState state, const Player& player)
{
  return player != state;
}

 

 

 

 

 

./CppPylos/pyloswinner.h

 

//---------------------------------------------------------------------------
/*
pylos::Winner, Pylos enum class for the winner
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/CppPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSWINNER_H
#define PYLOSWINNER_H

#include <string>

namespace ribi {

namespace pylos {

enum class Winner { player1, player2, none };
std::string ToStr(const Winner winner);

} //~namespace Pylos

} //~namespace ribi

#endif // PYLOSWINNER_H

 

 

 

 

 

./CppPylos/pyloswinner.cpp

 

//---------------------------------------------------------------------------
/*
pylos::Winner, Pylos enum class for the winner
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/CppPylos.htm
//---------------------------------------------------------------------------


#include "pyloswinner.h"

#include <cassert>
#include <stdexcept>

std::string ribi::pylos::ToStr(const Winner winner)
{
  switch (winner)
  {
    case Winner::player1: return "player1";
    case Winner::player2: return "player2";
    case Winner::none: return "none";
  }
  assert(!"Cannot convert this Winner to a std::string");
  throw std::logic_error("Cannot convert this Winner to a std::string");
}

 

 

 

 

 

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