(C++) ConnectThreeWidget
January 25, 2018 · View on GitHub
(C++) ConnectThreeWidget



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 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 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; }