Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) ConnectThree

 

STLQt CreatorLubuntu

 

ConnectThree is a class for a ConnectThree game.

Technical facts

 

 

 

 

 

 

./CppConnectThree/CppConnectThree.pri

 

INCLUDEPATH += \
    ../../Classes/CppConnectThree

SOURCES += \
    ../../Classes/CppConnectThree/connectthree.cpp \
    ../../Classes/CppConnectThree/connectthreeplayer.cpp \
    ../../Classes/CppConnectThree/connectthreesquare.cpp \
    ../../Classes/CppConnectThree/connectthreewinner.cpp \
    ../../Classes/CppConnectThree/connectthreemove.cpp \
    ../../Classes/CppConnectThree/connectthreemovefactory.cpp

HEADERS  += \
    ../../Classes/CppConnectThree/connectthree.h \
    ../../Classes/CppConnectThree/connectthreeplayer.h \
    ../../Classes/CppConnectThree/connectthreesquare.h \
    ../../Classes/CppConnectThree/connectthreewinner.h \
    ../../Classes/CppConnectThree/connectthreemove.h \
    ../../Classes/CppConnectThree/connectthreefwd.h \
    ../../Classes/CppConnectThree/connectthreemovefactory.h

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

 

 

 

 

 

./CppConnectThree/connectthree.h

 

//---------------------------------------------------------------------------
/*
ConnectThree. A connect-three 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/CppConnectThree.htm
//---------------------------------------------------------------------------
#ifndef CONNECTTHREE_H
#define CONNECTTHREE_H

#include <bitset>
#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/shared_ptr.hpp>
#include <boost/checked_delete.hpp>
#include <boost/tuple/tuple.hpp>
#include "connectthreefwd.h"
#include "connectthreeplayer.h"
#include "connectthreesquare.h"
#include "connectthreewinner.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace con3 {

///ConnectThree does not know which players are human.
///ConnectThree can suggest a move, but for this it needs to know if the
///others are human or computer
struct ConnectThree
{
  explicit ConnectThree(
    const int n_cols = 16,
    const int n_rows = 12
  );

  bool CanDoMove(const int x, const int y) const noexcept;
  int CanGetSquare(const int x, const int y) const noexcept;
  void DoMove(const int x, const int y);
  void DoMove(const boost::shared_ptr<Move> p) noexcept;
  Player GetActivePlayer() const noexcept { return m_player; }
  int GetCols() const noexcept;
  int GetRows() const noexcept;
  Square GetSquare(const int x, const int y) const noexcept;
  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///GetWinner returns the index of the winner.
  ///Returns draw if the board is full without a winner
  ///Returns no_player if there are still free squares
  Winner GetWinner() const noexcept;

  void Restart() noexcept;

  ///SuggestMove suggests a good move. If the game is a draw,
  ///it returns nullptr
  boost::shared_ptr<Move> SuggestMove(const std::bitset<3>& is_player_human) const noexcept;

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

  //X-Y-ordered 2D std::vector
  std::vector<std::vector<Square>> m_area;
  Player m_player;

  bool CanDoMove(const boost::shared_ptr<Move> p) const noexcept;
  boost::shared_ptr<Move> CheckOneOther(const std::bitset<3>& is_player_human) const noexcept;
  boost::shared_ptr<Move> CheckTwoDiagonally() const noexcept;
  boost::shared_ptr<Move> CheckTwoHorizontalOwn() const noexcept;
  boost::shared_ptr<Move> CheckTwoOther(const std::bitset<3>& is_player_human) const noexcept;
  boost::shared_ptr<Move> CheckTwoVerticalOwn() const noexcept;
  std::vector<boost::shared_ptr<Move>> GetAllPossibleMoves() const noexcept;
  Player GetNextPlayer() const noexcept;
  std::vector<boost::shared_ptr<Move>> GetTwoHorizontalOtherMoves() const noexcept;
  std::vector<boost::shared_ptr<Move>> GetTwoVerticalOtherMoves() const noexcept;
  bool IsPlayerHuman(const Player player, const std::bitset<3>& is_player_human) const noexcept;
  boost::shared_ptr<Move> MakeRandomMove() const noexcept;
  Square PlayerToSquare(const Player player) const noexcept;
  Player SquareToPlayer(const Square square) const noexcept;
  Winner SquareToWinner(const Square square) const noexcept;
  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

std::ostream& operator<<(std::ostream& os, const ConnectThree& c);

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREE_H

 

 

 

 

 

./CppConnectThree/connectthree.cpp

 

//---------------------------------------------------------------------------
/*
ConnectThree. A connect-three 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/CppConnectThree.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "connectthree.h"

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

#include <boost/scoped_ptr.hpp>

#include "connectthreemove.h"
#include "connectthreemovefactory.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::con3::ConnectThree::ConnectThree(
  const int n_cols,
  const int n_rows
) : m_area(n_cols, std::vector<Square>(n_rows,Square::empty)),
    m_player(Player::player1)
{
  #ifndef NDEBUG
  Test();
  #endif

  Restart();

  assert(n_rows > 1); //Sure, a 2x2 board is useless, but should work
  assert(n_cols > 1); //Sure, a 2x2 board is useless, but should work
  assert(GetCols() == n_cols);
  assert(GetRows() == n_rows);
}

bool ribi::con3::ConnectThree::CanDoMove(const int x, const int y) const noexcept
{
  return CanGetSquare(x,y)
    && m_area[x][y] == Square::empty;
}

bool ribi::con3::ConnectThree::CanDoMove(const boost::shared_ptr<Move> p) const noexcept
{
  return p && CanDoMove(p->GetX(), p->GetY());
}

int ribi::con3::ConnectThree::CanGetSquare(const int x, const int y) const noexcept
{
  return
       x >= 0
    && x <  GetCols()
    && y >= 0
    && y <  GetRows()
  ;
}

void ribi::con3::ConnectThree::DoMove(const int x, const int y)
{
  assert(CanDoMove(x,y));
  m_area[x][y] = PlayerToSquare(m_player);
  m_player = GetNextPlayer();
}

void ribi::con3::ConnectThree::DoMove(const boost::shared_ptr<Move> p) noexcept
{
  assert(CanDoMove(p));
  DoMove(p->GetX(),p->GetY());
}

ribi::con3::Square ribi::con3::ConnectThree::GetSquare(const int x, const int y) const noexcept
{
  assert(CanGetSquare(x,y));
  assert(!m_area.empty());
  assert(x >= 0);
  assert(x < static_cast<int>(m_area.size()));
  assert(y >= 0);
  #ifndef NDEBUG
  if (y >= static_cast<int>(m_area[x].size()))
  {
    TRACE("ERROR");
    TRACE(y);
    TRACE(m_area.size());
    TRACE(m_area[x].size());
    TRACE("BREAK");
  }
  #endif
  assert(y < static_cast<int>(m_area[x].size()));
  return m_area[x][y];
}

std::string ribi::con3::ConnectThree::GetVersion() noexcept
{
  return "1.4";
}

std::vector<std::string> ribi::con3::ConnectThree::GetVersionHistory() noexcept
{
  return {
    "2010-12-28: version 0.1: initial seperation of game logic from GUI",
    "2011-01-09: version 0.2: converted square values to enum constant, fixed small constructor bug",
    "2011-01-11: version 1.0: added that the game can end in a draw. First tested and debugged version",
    "2011-04-19: version 1.1: added Restart member function, removed m_is_player_human",
    "2014-02-13: version 1.2: improved interface",
    "2014-06-30: version 1.3: fixed bug in ribi::con3::ConnectThree::DoMove",
    "2014-07-21: version 1.4: fixed another bug"
  };
}

ribi::con3::Winner ribi::con3::ConnectThree::GetWinner() const noexcept
{
  const int n_rows = GetRows();
  for (int y=0; y!=n_rows; ++y)
  {
    const int n_cols = GetCols();
    for (int x=0; x!=n_cols; ++x)
    {
      if (m_area[x][y] == Square::empty) continue;
      //Horizontal
      if (x + 2 < n_cols
        && m_area[x  ][y] == m_area[x+1][y]
        && m_area[x+1][y] == m_area[x+2][y])
      {
        return SquareToWinner(m_area[x][y]);
      }
      //Vertical
      if (y + 2 < n_rows
        && m_area[x][y  ] == m_area[x][y+1]
        && m_area[x][y+1] == m_area[x][y+2])
      {
        return SquareToWinner(m_area[x][y]);
      }
    }
  }
  //Check for draw
  {
    if (!MakeRandomMove())
    {
      return Winner::draw;
    }
  }
  return Winner::no_winner;
}

/*
bool ribi::con3::ConnectThree::IsInvalidMove(const Move& p) noexcept
{
  const Move q = CreateInvalidMove();
  return
       p.get<0>() == q.get<0>()
    && p.get<1>() == q.get<1>()
    && p.get<2>() == q.get<2>();
}
*/

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::SuggestMove(const std::bitset<3>& is_player_human) const noexcept
{
  //const std::bitset<3>& is_player_human
  if (CanDoMove(CheckTwoHorizontalOwn())) return CheckTwoHorizontalOwn();
  if (CanDoMove(CheckTwoVerticalOwn())) return CheckTwoVerticalOwn();
  if (CanDoMove(CheckTwoOther(is_player_human))) return CheckTwoOther(is_player_human);
  if (CanDoMove(CheckTwoDiagonally())) return CheckTwoDiagonally();
  if (CanDoMove(CheckOneOther(is_player_human))) return CheckOneOther(is_player_human);
  return MakeRandomMove();
}

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::CheckTwoHorizontalOwn() const noexcept
{
  const int n_rows = GetRows();
  for (int y=0; y!=n_rows; ++y)
  {
    const int n_cols = GetCols();
    for (int x=0; x!=n_cols-1; ++x) //-1 to prevent out of range
    {
      //Two consequtive selfs
      if (m_area[x][y] == PlayerToSquare(m_player) && m_area[x+1][y] == PlayerToSquare(m_player))
      {
        if (x >= 1)
        {
          if (m_area[x-1][y] == Square::empty)
          {
            const boost::shared_ptr<Move> p { MoveFactory().Create(x-1,y,m_player) };
            assert(CanDoMove(p));
            return p;
          }
        }
        if (x < n_cols-2 && m_area[x+2][y] == Square::empty)
        {
          const boost::shared_ptr<Move> p { MoveFactory().Create(x+2,y,m_player) };
          assert(CanDoMove(p));
          return p;
        }
      }
      //Perhaps a gap?
      if (x < n_cols-2)
      {
        if (m_area[x][y] == PlayerToSquare(m_player) && m_area[x+1][y] == Square::empty && m_area[x+2][y] == PlayerToSquare(m_player))
        {
          const boost::shared_ptr<Move> p { MoveFactory().Create(x+1,y,m_player) };
          assert(CanDoMove(p));
          return p;
        }
      }
    }
  }
  return nullptr;
}

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::CheckTwoVerticalOwn() const noexcept
{
  const int n_rows = GetRows();
  assert(n_rows > 1);
  for (int y=0; y!=n_rows-1; ++y) //-1 to prevent out of range
  {
    const int n_cols = GetCols();
    assert(n_cols > 1);
    for (int x=0; x!=n_cols; ++x)
    {
      //Two consequtive selfs?
      if (m_area[x][y] == PlayerToSquare(m_player) && m_area[x][y+1] == PlayerToSquare(m_player))
      {
        if (y >= 1)
        {
          if (m_area[x][y-1] == Square::empty)
          {
            const boost::shared_ptr<Move> p { MoveFactory().Create(x,y-1,m_player) };
            assert(CanDoMove(p));
            return p;
          }
        }
        if (y < n_rows-2)
        {
          if (m_area[x][y+2] == Square::empty)
          {
            const boost::shared_ptr<Move> p { MoveFactory().Create(x,y+2,m_player) };
            assert(CanDoMove(p));
            return p;
          }
        }
      }
      //Perhaps a gap?
      if (y < n_rows-2)
      {
        if (m_area[x][y] == PlayerToSquare(m_player) && m_area[x][y+1] == Square::empty && m_area[x][y+2] == PlayerToSquare(m_player))
        {
          const boost::shared_ptr<Move> p { MoveFactory().Create(x,y+1,m_player) };
          assert(CanDoMove(p));
          return p;
        }
      }
    }
  }
  return nullptr;
}

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::CheckTwoOther(const std::bitset<3>& is_player_human) const noexcept
{
  const auto moves(GetAllPossibleMoves());

  const int nMoves = moves.size();
  if (nMoves==0) return nullptr;

  {
    //Get anti-human moves
    std::vector<boost::shared_ptr<Move>> v;
    for(const auto m: moves)
    {
      assert(CanDoMove(m));
      //Player is human
      if (IsPlayerHuman(m->GetPlayer(),is_player_human))
      {
        v.push_back(m);
      }
    }
    //If there are anti-player moves, choose one at random
    if (!v.empty())
    {
      const auto move = v[std::rand() % v.size()];
      assert(CanDoMove(move));
      return move;
    }
  }
  {
    //Get moves anti-next-player
    const Player next_player_index = GetNextPlayer();
    std::vector<boost::shared_ptr<Move>> v;
    for(const auto& move: moves)
    {
      assert(CanDoMove(move));
      if (move->GetPlayer() == next_player_index)
        v.push_back(move);
    }
    //If there are anti-next-player moves, choose one at random
    if (!v.empty())
    {
      const auto move = v[std::rand() % v.size()];
      assert(CanDoMove(move));
      return move;
    }
  }
  //Choose a move at random
  {
    const auto move = moves[std::rand() % moves.size()];
    assert(CanDoMove(move));
    return move;
  }
}

//const boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::CreateInvalidMove() noexcept
//{
//  return nullptr;
//}

///GetAllPossibleMoves returns all possible moves.
//boost::get<0>: x coordinat
//boost::get<1>: y coordinat
//boost::get<2>: player that would dislike this move
std::vector<boost::shared_ptr<ribi::con3::Move>>
  ribi::con3::ConnectThree::GetAllPossibleMoves() const noexcept
{
  std::vector<boost::shared_ptr<ribi::con3::Move>> v(GetTwoHorizontalOtherMoves());
  const std::vector<boost::shared_ptr<ribi::con3::Move>> w(GetTwoVerticalOtherMoves());
  std::copy(w.begin(),w.end(),std::back_inserter(v));
  return v;
}

std::vector<boost::shared_ptr<ribi::con3::Move>> ribi::con3::ConnectThree::GetTwoHorizontalOtherMoves() const noexcept
{
  const int n_rows = GetRows();
  std::vector<boost::shared_ptr<ribi::con3::Move>> moves;
  for (int y=0; y!=n_rows; ++y)
  {
    const int n_cols = GetCols();
    for (int x=0; x!=n_cols-1; ++x) //-1 to prevent out of range
    {
      //Check consequtive
      if (m_area[x][y]!=Square::empty && m_area[x][y] == m_area[x+1][y])
      {
        //Check A X B
        if (x > 0 && m_area[x-1][y] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x-1,y,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          moves.push_back(m);
        }
        //Check X B C
        if (x < n_cols-2 && m_area[x+2][y] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x+2,y,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          moves.push_back(m);
        }
      }
      //Check gap, also X B C
      if (m_area[x][y] != Square::empty && x + 2 < n_cols && m_area[x+1][y] == Square::empty && m_area[x][y] == m_area[x+2][y])
      {
        const boost::shared_ptr<Move> m { MoveFactory().Create(
          x+1,y,SquareToPlayer(m_area[x][y]))
        };
        assert(CanDoMove(m));
        moves.push_back(m);
      }

    }
  }
  return moves;
}

//A X B C (x is focus of for loop)
std::vector<boost::shared_ptr<ribi::con3::Move>>
  ribi::con3::ConnectThree::GetTwoVerticalOtherMoves() const noexcept
{
  const int n_rows = GetRows();
  std::vector<boost::shared_ptr<Move>> v;

  for (int y=0; y!=n_rows-1; ++y) //-1 to prevent out of range
  {
    const int n_cols = GetCols();
    for (int x=0; x!=n_cols; ++x)
    {
      //Check consequtive
      if (m_area[x][y] != Square::empty && m_area[x][y] == m_area[x][y+1])
      {
        //Check A X B
        if (y > 0 && m_area[x][y-1] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x,y-1,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          v.push_back(m);
        }
        //Check X B C
        if (y < n_rows-2 && m_area[x][y+2] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x,y+2,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          v.push_back(m);
        }
      }
      //Check gap, also X B C
      if (m_area[x][y] != Square::empty && y < n_rows && m_area[x][y+1] == Square::empty && m_area[x][y] == m_area[x][y+2])
      {
        const boost::shared_ptr<Move> m { MoveFactory().Create(
          x,y+1,SquareToPlayer(m_area[x][y]))
        };
        assert(CanDoMove(m));
        v.push_back(m);
      }
    }
  }
  return v;
}

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::CheckTwoDiagonally() const noexcept
{
  std::vector<boost::shared_ptr<Move>> v;

  const int n_rows = GetRows();
  for (int y=0; y!=n_rows-1; ++y) //-1 To prevent out of range
  {
    const int n_cols = GetCols();
    for (int x=0; x!=n_cols-1; ++x) //-1 to prevent out of range
    {
      if (m_area[x][y] == PlayerToSquare(m_player) && m_area[x+1][y+1] == PlayerToSquare(m_player))
      {
        if (m_area[x+1][y] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x+1,y,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          v.push_back(m);
        }
        if (m_area[x][y+1] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x,y+1,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          v.push_back(m);
        }
      }
    }
  }
  if (v.empty()) return nullptr;
  const auto m = v[std::rand() % v.size()];
  assert(CanDoMove(m));
  return m;
}

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::CheckOneOther(const std::bitset<3>& is_player_human) const noexcept
{
  std::vector<boost::shared_ptr<Move>> v;

  const int n_rows = GetRows();

  for (int y=0; y!=n_rows; ++y)
  {
    const int n_cols = GetCols();
    for (int x=0; x!=n_cols; ++x)
    {
      if (m_area[x][y] != Square::empty)
      {
        if (y >= 1 && m_area[x][y-1] == Square::empty)
        {
          const boost::shared_ptr<Move> m { MoveFactory().Create(
            x,y-1,SquareToPlayer(m_area[x][y]))
          };
          assert(CanDoMove(m));
          v.push_back(m);
        }
        if (y < n_rows-1)
        {
          if (m_area[x][y+1] == Square::empty)
          {
            const boost::shared_ptr<Move> m { MoveFactory().Create(
              x,y+1,SquareToPlayer(m_area[x][y]))
            };
            assert(CanDoMove(m));
            v.push_back(m);
          }
        }
        if (x >= 1)
        {
          if (m_area[x-1][y] == Square::empty)
          {
            const boost::shared_ptr<Move> m { MoveFactory().Create(
              x-1,y,SquareToPlayer(m_area[x][y]))
            };
            assert(CanDoMove(m));
            v.push_back(m);
          }
        }
        if (x < n_cols-1)
        {
          if (m_area[x+1][y] == Square::empty)
          {
            const boost::shared_ptr<Move> m { MoveFactory().Create(
              x+1,y,SquareToPlayer(m_area[x][y]))
            };
            assert(CanDoMove(m));
            v.push_back(m);
          }
        }
      }
    }
  }
  if (v.empty()) return nullptr;

  {
    //Get anti-human moves
    std::vector<boost::shared_ptr<Move>> w;
    for(const auto m: v)
    {
      assert(CanDoMove(m));
      if (IsPlayerHuman(m->GetPlayer(),is_player_human))
        w.push_back(m);
    }
    //If there are anti-player moves, choose one at random
    if (!w.empty())
    {
      const auto m = w[std::rand() % w.size()]; //ex-bug ('w.size()' was 'v.size()')
      assert(CanDoMove(m));
      return m;
    }
  }

  {
    //Get moves anti-next-player
    const Player next_player_index = GetNextPlayer();
    std::vector<boost::shared_ptr<Move>> w;
    for(const auto m: v)
    {
      assert(CanDoMove(m));
      if (m->GetPlayer() == next_player_index)
        w.push_back(m);
    }
    //If there are anti-next-player moves, choose one at random
    if (!w.empty())
    {
      const auto m = w[std::rand() % w.size()];
      assert(CanDoMove(m));
      return m;
    }
  }
  //Choose a move at random
  {
    const auto m = v[std::rand() % v.size()];
    assert(CanDoMove(m));
    return m;
  }
}

boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThree::MakeRandomMove() const noexcept
{
  std::vector<boost::shared_ptr<Move>> v;
  const int n_cols = GetCols();
  const int n_rows = GetRows();

  for (int y=0; y!=n_rows; ++y)
  {
    for (int x=0; x!=n_cols; ++x)
    {
      if (this->GetSquare(x,y) == Square::empty)
      {
        const boost::shared_ptr<Move> m { MoveFactory().Create(
          x,y,m_player)
        };
        assert(CanDoMove(m));
        v.push_back(m);
      }
    }
  }
  if (v.empty())
  {
    return nullptr;
  }
  const int index = std::rand() % v.size();
  return v[index];
}

int ribi::con3::ConnectThree::GetCols() const noexcept
{
  assert(!m_area.empty());
  return m_area.size();
}

ribi::con3::Player ribi::con3::ConnectThree::GetNextPlayer() const noexcept
{
  return ribi::con3::GetNextPlayer(m_player);
}

int ribi::con3::ConnectThree::GetRows() const noexcept
{
  assert(!m_area.empty());
  return m_area[0].size();
}

bool ribi::con3::ConnectThree::IsPlayerHuman(const Player player, const std::bitset<3>& is_player_human) const noexcept
{
  switch (player)
  {
    case Player::player1: return is_player_human[0];
    case Player::player2: return is_player_human[1];
    case Player::player3: return is_player_human[2];
    default:
      assert(!"Should not get here");
      throw std::logic_error("Unknown value of player");
  }
}

ribi::con3::Square ribi::con3::ConnectThree::PlayerToSquare(const Player player) const noexcept
{
  switch (player)
  {
    case Player::player1: return Square::player1;
    case Player::player2: return Square::player2;
    case Player::player3: return Square::player3;
    default:
      assert(!"Should not get here");
      throw std::logic_error("Unknown player");
  }
}


void ribi::con3::ConnectThree::Restart() noexcept
{
  m_area = std::vector<std::vector<Square> >(GetCols(),
    std::vector<Square>(GetRows(),Square::empty));
  m_player = Player::player1;
}

ribi::con3::Player ribi::con3::ConnectThree::SquareToPlayer(const Square square) const noexcept
{
  switch (square)
  {
    case Square::player1: return Player::player1;
    case Square::player2: return Player::player2;
    case Square::player3: return Player::player3;
    default:
      assert(!"Should not get here");
      throw std::logic_error("Unknown Square");
  }
}

ribi::con3::Winner ribi::con3::ConnectThree::SquareToWinner(const Square square) const noexcept
{
  switch (square)
  {
    case Square::player1: return Winner::player1;
    case Square::player2: return Winner::player2;
    case Square::player3: return Winner::player3;
    default:
      assert(!"Should not get here");
      throw std::logic_error("Unknown Square");
  }
}

#ifndef NDEBUG
void ribi::con3::ConnectThree::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  //Can a 2x2 game be played without exceptions thrown
  const int n_rows = 2;
  const int n_cols = 2;
  std::bitset<3> is_player_human;
  is_player_human[0] = true;
  is_player_human[1] = true;
  is_player_human[2] = true;
  ConnectThree c(n_cols,n_rows);
  assert(c.GetCols() == n_cols);
  assert(c.GetRows() == n_rows);
  assert(c.GetActivePlayer() == Player::player1);
  assert( c.CanDoMove(0,0));
  assert( c.CanDoMove(0,1));
  assert( c.CanDoMove(1,0));
  assert( c.CanDoMove(1,1));
  assert(!c.CanDoMove(0,n_rows));
  assert(!c.CanDoMove(n_rows,0));
  assert(c.SuggestMove(is_player_human));
  assert(c.GetWinner() == Winner::no_winner); //No winner yet
  c.DoMove(0,0);
  assert(c.GetActivePlayer() == Player::player2);
  assert(!c.CanDoMove(0,0));
  assert( c.CanDoMove(0,1));
  assert( c.CanDoMove(1,0));
  assert( c.CanDoMove(1,1));
  assert(c.SuggestMove(is_player_human));
  assert(c.GetWinner() == Winner::no_winner); //No winner yet
  c.DoMove(0,1);
  assert(c.GetActivePlayer() == Player::player3);
  assert(!c.CanDoMove(0,0));
  assert(!c.CanDoMove(0,1));
  assert( c.CanDoMove(1,0));
  assert( c.CanDoMove(1,1));
  assert(c.SuggestMove(is_player_human));
  assert(c.GetWinner() == Winner::no_winner); //No winner yet
  c.DoMove(1,0);
  assert(c.GetActivePlayer() == Player::player1);
  assert(!c.CanDoMove(0,0));
  assert(!c.CanDoMove(0,1));
  assert(!c.CanDoMove(1,0));
  assert( c.CanDoMove(1,1));
  assert(c.SuggestMove(is_player_human));
  assert(c.GetWinner() == Winner::no_winner); //No winner yet
  c.DoMove(1,1);
  assert(c.GetActivePlayer() == Player::player2);
  assert(!c.CanDoMove(0,0));
  assert(!c.CanDoMove(0,1));
  assert(!c.CanDoMove(1,0));
  assert(!c.CanDoMove(1,1));
  assert(!c.SuggestMove(is_player_human));
  assert(c.GetWinner() == Winner::draw);
}
#endif

std::ostream& ribi::con3::operator<<(std::ostream& os, const ribi::con3::ConnectThree& c)
{
  const int n_cols = c.GetCols();
  const int n_rows = c.GetRows();
  for (int y=0; y!=n_rows; ++y)
  {
    for (int x=0; x!=n_cols; ++x)
    {
      char d = ' ';
      switch (c.GetSquare(x,y))
      {
        case Square::empty  : d = '.'; break;
        case Square::player1: d = '1'; break;
        case Square::player2: d = '2'; break;
        case Square::player3: d = '3'; break;
        default: assert(!"Should not get here");
      }
      os << d;
    }
    os << '\n';
  }
  return os;
}

 

 

 

 

 

./CppConnectThree/connectthreefwd.h

 

#ifndef CONNECTTHREEFWD_H
#define CONNECTTHREEFWD_H

namespace ribi {
namespace con3 {

struct ConnectThreeResources;
struct ConnectThreeWidget;
struct Move;
struct WtConnectThreeGameDialog;
struct WtConnectThreeWidget;
struct WtSelectPlayerWidget;

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREEFWD_H

 

 

 

 

 

./CppConnectThree/connectthreemove.h

 

#ifndef CONNECTTHREEMOVE_H
#define CONNECTTHREEMOVE_H

#include "connectthreeplayer.h"

namespace ribi {
namespace con3 {

struct Move
{
  Move(const int x, const int y, const Player player)
    : m_player{player}, m_x{x}, m_y{y} {}

  Player GetPlayer() const noexcept { return m_player; }
  int GetX() const noexcept { return m_x; }
  int GetY() const noexcept { return m_y; }

  private:
  const Player m_player;
  const int m_x;
  const int m_y;

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

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREEMOVE_H

 

 

 

 

 

./CppConnectThree/connectthreemove.cpp

 

#include "connectthreemove.h"

#include <cassert>

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

#ifndef NDEBUG
void ribi::con3::Move::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

 

 

 

 

 

./CppConnectThree/connectthreemovefactory.h

 

#ifndef CONNECTTHREEMOVEFACTORY_H
#define CONNECTTHREEMOVEFACTORY_H

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

#include "connectthreefwd.h"
#include "connectthreeplayer.h"

namespace ribi {
namespace con3 {

struct MoveFactory
{
  const boost::shared_ptr<Move> Create(const int x, const int y, const Player player) const noexcept;

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

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREEMOVEFACTORY_H

 

 

 

 

 

./CppConnectThree/connectthreemovefactory.cpp

 

#include "connectthreemovefactory.h"

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

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

const boost::shared_ptr<ribi::con3::Move> ribi::con3::MoveFactory::Create(
  const int x, const int y, const Player player) const noexcept
{
  const boost::shared_ptr<Move> move {
    new Move(x,y,player)
  };
  assert(move);
  return move;
}

#ifndef NDEBUG
void ribi::con3::MoveFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

 

 

 

 

 

./CppConnectThree/connectthreeplayer.h

 

#ifndef CONNECTTHREEPLAYER_H
#define CONNECTTHREEPLAYER_H

namespace ribi {
namespace con3 {

enum class Player { player1, player2, player3 };

Player GetNextPlayer(const Player player) noexcept;

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREEPLAYER_H

 

 

 

 

 

./CppConnectThree/connectthreeplayer.cpp

 

#include "connectthreeplayer.h"

#include <cassert>
#include <stdexcept>

ribi::con3::Player ribi::con3::GetNextPlayer(const ribi::con3::Player player) noexcept
{
  switch (player)
  {
    case Player::player1: return Player::player2;
    case Player::player2: return Player::player3;
    case Player::player3: return Player::player1;
  }
  assert(!"Should not get here");
  throw std::logic_error("Unknown value of Player");
}

 

 

 

 

 

./CppConnectThree/connectthreesquare.h

 

#ifndef CONNECTTHREESQUARE_H
#define CONNECTTHREESQUARE_H

namespace ribi {
namespace con3 {

enum class Square { empty, player1, player2, player3 };

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREESQUARE_H

 

 

 

 

 

./CppConnectThree/connectthreesquare.cpp

 

#include "connectthreesquare.h"

 

 

 

 

 

./CppConnectThree/connectthreewinner.h

 

#ifndef CONNECTTHREEWINNER_H
#define CONNECTTHREEWINNER_H

namespace ribi {
namespace con3 {

enum class Winner { no_winner = 0, player1 = 1, player2 = 2, player3 = 3, draw = 4 };

} //~namespace con3
} //~namespace ribi

#endif // CONNECTTHREEWINNER_H

 

 

 

 

 

./CppConnectThree/connectthreewinner.cpp

 

#include "connectthreewinner.h"

 

 

 

 

 

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