Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) ConnectThreeWidget

 

STLQt CreatorLubuntu

 

ConnectThreeWidget is a widget for a ConnectThree game.

Technical facts

 

 

 

 

 

 

./CppConnectThreeWidget/CppConnectThreeWidget.pri

 

INCLUDEPATH += \
    ../../Classes/CppConnectThreeWidget

SOURCES += \
    ../../Classes/CppConnectThreeWidget/connectthreewidget.cpp

HEADERS  += \
    ../../Classes/CppConnectThreeWidget/connectthreewidget.h

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

 

 

 

 

 

./CppConnectThreeWidget/connectthreewidget.h

 

//---------------------------------------------------------------------------
/*
ConnectThreeWidget. GUI independent ConnectThree widget.
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/CppConnectThreeWidget.htm
//---------------------------------------------------------------------------
#ifndef CONNECTTHREEWIDGET_H
#define CONNECTTHREEWIDGET_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/tuple/tuple.hpp>

#include "connectthree.h"
#include "connectthreefwd.h"
#pragma GCC diagnostic pop

namespace ribi {

struct TextCanvas;

namespace con3 {

struct ConnectThree;

///ConnectThreeWidget embodies the interaction with a user
///It keeps track which players are human and whose turn it is
struct ConnectThreeWidget
{
  enum class Key { up, right, down, left, select };

  explicit ConnectThreeWidget(
    const std::bitset<3>& is_player_human = CreateDefaultIsPlayerHuman(),
    const int n_cols = 16,
    const int n_rows = 12);


  bool CanDoMove() const noexcept { return CanDoMove(m_x,m_y); }
  bool CanSelect(const int x, const int y) const noexcept;

  static std::bitset<3> CreateDefaultIsPlayerHuman() noexcept;

  ///Let the computer do a move
  void DoComputerMove() noexcept;

  ///Press select at the current place
  void DoMove() noexcept;


  const boost::shared_ptr<const ConnectThree> GetGame() const noexcept { return m_game; }
  const std::bitset<3>& GetIsPlayerHuman() const noexcept { return m_is_player_human; }
  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;
  bool IsComputerTurn() const noexcept;
  bool IsHuman(const Player player) const noexcept;
  void OnKeyPress(const Key key) noexcept;
  void Restart() noexcept;
  void Select(const int x, const int y) noexcept;
  void SetIsPlayerHuman(const std::bitset<3>& is_player_human) noexcept;
  const boost::shared_ptr<Move> SuggestMove() const noexcept;
  const boost::shared_ptr<TextCanvas> ToTextCanvas() const noexcept;
  void Tick() noexcept;

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

  boost::shared_ptr<ConnectThree> m_game;
  std::bitset<3> m_is_player_human;

  //X coordinat of cursor
  int m_x;

  //Y coordinat of cursor
  int m_y;

  bool CanDoMove(const int x, const int y) const noexcept;
  void DoMove(const int x, const int y) noexcept;
  int PlayerToIndex(const Player player) const noexcept;

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

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

#endif // CONNECTTHREEWIDGET_H

 

 

 

 

 

./CppConnectThreeWidget/connectthreewidget.cpp

 

//---------------------------------------------------------------------------
/*
ConnectThreeWidget. GUI independent ConnectThree widget.
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/CppConnectThreeWidget.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "connectthreewidget.h"
#pragma GCC diagnostic pop

#include <cassert>
#include <stdexcept>

#include "connectthree.h"
#include "connectthreemove.h"
#include "testtimer.h"
#include "textcanvas.h"
#include "trace.h"

ribi::con3::ConnectThreeWidget::ConnectThreeWidget(
  const std::bitset<3>& is_player_human,
  const int n_cols,
  const int n_rows)
  : m_game(new ConnectThree(n_cols,n_rows)),
    m_is_player_human(is_player_human),
    m_x{n_cols / 2},
    m_y{n_rows / 2}
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(m_game);
  assert(m_x >= 0);
  assert(m_y >= 0);
  assert(n_cols > 0);
  assert(n_rows > 0);
  assert(m_x < n_cols);
  assert(m_y < n_rows);

}

bool ribi::con3::ConnectThreeWidget::CanDoMove(const int x,const int y) const noexcept
{
  return m_game->CanDoMove(x,y);
}

bool ribi::con3::ConnectThreeWidget::CanSelect(const int x, const int y) const noexcept
{
  return
       x >= 0 && x < GetGame()->GetCols()
    && y >= 0 && y < GetGame()->GetRows()
    && GetGame()->GetSquare(x,y) == Square::empty;
}

std::bitset<3> ribi::con3::ConnectThreeWidget::CreateDefaultIsPlayerHuman() noexcept
{
  std::bitset<3> b;
  b[0] = true;
  b[1] = true;
  b[2] = true;
  return b;
}

void ribi::con3::ConnectThreeWidget::DoComputerMove() noexcept
{
  const auto move = SuggestMove();
  assert(CanDoMove(move->GetX(),move->GetY()));
  DoMove(move->GetX(),move->GetY());
}

void ribi::con3::ConnectThreeWidget::DoMove() noexcept
{
  assert(CanDoMove());
  assert(CanDoMove(m_x,m_y));
  m_game->DoMove(m_x,m_y);
}

void ribi::con3::ConnectThreeWidget::DoMove(const int x,const int y) noexcept
{
  assert(CanDoMove(x,y));
  m_game->DoMove(x,y);
}

std::string ribi::con3::ConnectThreeWidget::GetVersion() noexcept
{
  return "1.0";
}

std::vector<std::string> ribi::con3::ConnectThreeWidget::GetVersionHistory() noexcept
{
  return {
    "2011-04-20: version 1.0: initial version"
  };
}

bool ribi::con3::ConnectThreeWidget::IsComputerTurn() const noexcept
{
  assert(m_game);
  return !IsHuman(m_game->GetActivePlayer());
}

bool ribi::con3::ConnectThreeWidget::IsHuman(const Player player) const noexcept
{
  const int player_index{PlayerToIndex(player)};
  assert(player_index >= 0);
  assert(player_index < static_cast<int>(m_is_player_human.size()));
  return m_is_player_human[player_index];
}

void ribi::con3::ConnectThreeWidget::OnKeyPress(const Key key) noexcept
{
  assert(m_x >= 0);
  assert(m_y >= 0);
  assert(m_x < m_game->GetCols());
  assert(m_y < m_game->GetRows());

  switch (key)
  {
    case Key::up   : if (m_y > 0) --m_y; break;
    case Key::right: if (m_x + 1 < m_game->GetCols()) ++m_x; break;
    case Key::down : if (m_y + 1 < m_game->GetRows()) ++m_y; break;
    case Key::left : if (m_x > 0) --m_x; break;
    case Key::select:
      if (m_game->CanDoMove(m_x,m_y)) { m_game->DoMove(m_x,m_y); }
      break;
  }

  assert(m_x >= 0);
  assert(m_y >= 0);
  assert(m_x < m_game->GetCols());
  assert(m_y < m_game->GetRows());
}

int ribi::con3::ConnectThreeWidget::PlayerToIndex(const Player player) const noexcept
{
  switch(player)
  {
    case Player::player1: return 0;
    case Player::player2: return 1;
    case Player::player3: return 2;
    default:
      assert(!"Should not get here");
      throw std::logic_error("Unknown player");
  }
}

void ribi::con3::ConnectThreeWidget::Restart() noexcept
{
  assert(m_game);
  m_game->Restart();
}

void ribi::con3::ConnectThreeWidget::Select(const int x, const int y) noexcept
{
  assert(CanSelect(x,y));
  m_x = x;
  m_y = y;
}

void ribi::con3::ConnectThreeWidget::SetIsPlayerHuman(const std::bitset<3>& is_player_human) noexcept
{
  if (m_is_player_human != is_player_human)
  {
    //Only restart game if something changed
    m_is_player_human = is_player_human;
    Restart();
  }
}

const boost::shared_ptr<ribi::con3::Move> ribi::con3::ConnectThreeWidget::SuggestMove() const noexcept
{
  return m_game->SuggestMove(m_is_player_human);
}

#ifndef NDEBUG
void ribi::con3::ConnectThreeWidget::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const boost::shared_ptr<ConnectThreeWidget> widget {
    new ConnectThreeWidget
  };
  assert(widget->IsHuman(Player::player1));
  assert(widget->IsHuman(Player::player2));
  assert(widget->IsHuman(Player::player3));
  assert(widget->GetGame()->GetCols() == 16);
  assert(widget->GetGame()->GetRows() == 12);
  while (widget->GetGame()->GetWinner() == Winner::no_winner)
  {
    switch ((std::rand() >> 4) % 5)
    {
      case 0: widget->OnKeyPress(Key::up); break;
      case 1: widget->OnKeyPress(Key::right); break;
      case 2: widget->OnKeyPress(Key::down); break;
      case 3: widget->OnKeyPress(Key::left); break;
      case 4: widget->OnKeyPress(Key::select); break;
    }
  }
}
#endif

///Tick does either wait for a human to make his/her move
///or lets a computer do its move. Tick must be called by
///external timers like Wt::WTimer or QTimer.
void ribi::con3::ConnectThreeWidget::Tick() noexcept
{
  if (IsComputerTurn())
  {
    const auto m = m_game->SuggestMove(m_is_player_human);
    m_game->DoMove(m);
  }
}


const boost::shared_ptr<ribi::TextCanvas> ribi::con3::ConnectThreeWidget::ToTextCanvas() const noexcept
{
  assert(m_game);
  const int n_cols { m_game->GetCols() };
  const int n_rows { m_game->GetRows() };

  const boost::shared_ptr<TextCanvas> canvas {
    new TextCanvas(n_cols,n_rows)
  };
  for (int y=0; y!=n_rows; ++y)
  {
    for (int x=0; x!=n_cols; ++x)
    {
      char c = ' ';
      assert(m_game->CanGetSquare(x,y));
      switch (m_game->GetSquare(x,y))
      {
        case Square::empty  : c = '.'; break;
        case Square::player1: c = 'O'; break;
        case Square::player2: c = 'X'; break;
        case Square::player3: c = 'A'; break;
        default:
          assert(!"Should not get here");
      }
      canvas->PutChar(x,y,c);
    }
  }

  const char c = canvas->GetChar(m_x,m_y);
  char d = ' ';
  switch (c)
  {
    case ' ': d = '.'; break;
    case '.': d = ' '; break;
    case 'O': d = 'o'; break;
    case 'X': d = 'x'; break;
    case 'A': d = 'a'; break;
    case 'o': d = 'O'; break;
    case 'x': d = 'X'; break;
    case 'a': d = 'A'; break;
  }
  canvas->PutChar(m_x,m_y,d);
  return canvas;
}

 

 

 

 

 

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