Added CrissCross game

This commit is contained in:
Valentino Orlandi 2022-10-16 16:17:08 +02:00
parent 68c2e6037b
commit 6bf52806e8
Signed by: elB4RTO
GPG key ID: 1719E976DB2D4E71
3 changed files with 962 additions and 0 deletions

View file

@ -0,0 +1,346 @@
#include "crisscross.h"
#include "ui_crisscross.h"
#include <QMessageBox>
CrissCross::CrissCross( const QPalette& style, QWidget* parent ) :
QWidget(parent),
ui(new Ui::CrissCross)
{
this->ui->setupUi(this);
this->setPalette( style );
// verify that one player is human and the other is not
if ( !(p1_human^p2_human) ) {
throw("Players identities error: "+std::to_string(p1_human)+" - "+std::to_string(p2_human));
}
this->victory_sequence.reserve( 3 );
this->board_buttons[0] = this->ui->button_NW;
this->board_buttons[1] = this->ui->button_N;
this->board_buttons[2] = this->ui->button_NE;
this->board_buttons[3] = this->ui->button_W;
this->board_buttons[4] = this->ui->button_C;
this->board_buttons[5] = this->ui->button_E;
this->board_buttons[6] = this->ui->button_SW;
this->board_buttons[7] = this->ui->button_S;
this->board_buttons[8] = this->ui->button_SE;
if ( ! p1_human ) {
// AI starts
this->AI_playTurn();
}
}
CrissCross::~CrissCross()
{
delete ui;
}
/////////////////////
//// BOARD TILES ////
void CrissCross::on_button_NW_clicked()
{
if ( ! this->ui->button_NW->isFlat() ) {
this->board[0] = this->p_turn;
this->ui->button_NW->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_NW->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_N_clicked()
{
if ( ! this->ui->button_N->isFlat() ) {
this->board[1] = this->p_turn;
this->ui->button_N->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_N->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_NE_clicked()
{
if ( ! this->ui->button_NE->isFlat() ) {
this->board[2] = this->p_turn;
this->ui->button_NE->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_NE->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_W_clicked()
{
if ( ! this->ui->button_W->isFlat() ) {
this->board[3] = this->p_turn;
this->ui->button_W->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_W->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_C_clicked()
{
if ( ! this->ui->button_C->isFlat() ) {
this->board[4] = this->p_turn;
this->ui->button_C->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_C->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_E_clicked()
{
if ( ! this->ui->button_E->isFlat() ) {
this->board[5] = this->p_turn;
this->ui->button_E->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_E->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_SW_clicked()
{
if ( ! this->ui->button_SW->isFlat() ) {
this->board[6] = this->p_turn;
this->ui->button_SW->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_SW->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_S_clicked()
{
if ( ! this->ui->button_S->isFlat() ) {
this->board[7] = this->p_turn;
this->ui->button_S->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_S->setFlat( true );
this->endTurn();
}
}
void CrissCross::on_button_SE_clicked()
{
if ( ! this->ui->button_SE->isFlat() ) {
this->board[8] = this->p_turn;
this->ui->button_SE->setIcon( this->icons[ this->p_turn-1 ] );
this->ui->button_SE->setFlat( true );
this->endTurn();
}
}
//////////////////////
//// TURN RELATED ////
void CrissCross::endTurn()
{
if ( this->checkVictory() ) {
// a player won!
this->victory();
} else {
// nobody won yet
if ( this->gameDraw() ) {
// game is draw
this->draw();
} else {
// change turn and keep playing
this->nextTurn();
}
}
}
void CrissCross::nextTurn()
{
switch ( this->p_turn ) {
case 1:
this->p_turn += 1;
if ( ! this->p2_human ) {
this->AI_playTurn();
}
break;
case 2:
this->p_turn -= 1;
if ( ! this->p1_human ) {
this->AI_playTurn();
}
break;
default:
// wrong
throw("Wrong turn: "+std::to_string(this->p_turn));
break;
}
}
const bool CrissCross::checkVictory()
{
bool victory = false;
unsigned int streak;
for ( const auto& sequence : this->sequences ) {
streak = 0;
for ( const auto& index : sequence ) {
if ( this->board[ index ] == this->p_turn ) {
streak ++;
this->victory_sequence.push_back( index );
} else {
break;
}
}
if ( streak == 3 ) {
// victory
victory = true;
break;
} else {
this->victory_sequence.clear();
}
}
return victory;
}
void CrissCross::victory()
{
// disable all buttons except the victory sequence ones
bool disable;
for ( unsigned int i=0; i<9; i++ ) {
disable = true;
for ( const auto& j : this->victory_sequence ) {
if ( i == j ) {
disable = false;
break;
} else if ( i < j ) {
break;
}
}
this->board_buttons[ i ]->setFlat( true );
if ( disable ) {
this->board_buttons[ i ]->setEnabled( false );
}
}
// display a dialog
QString message;
if ( (this->p_turn == 1 && this->p1_human)
|| (this->p_turn == 2 && this->p2_human) ) {
// user won
message = CrissCross::tr("You beated me!");
} else {
// AI won
message = CrissCross::tr("This time you lost!");
}
QMessageBox::about(
this,
CrissCross::tr("Victory"),
message );
}
const bool CrissCross::gameDraw()
{
bool draw = false;
unsigned int empty_tiles = 9;
for ( const auto& tile : this->board ) {
if ( tile > 0 ) {
empty_tiles --;
}
}
if ( empty_tiles == 0 ) {
// no movement left
draw = true;
}
return draw;
}
void CrissCross::draw()
{
// disable all buttons
for ( const auto& button : this->board_buttons ) {
button->setEnabled( false );
}
// display a dialog
QMessageBox::about(
this,
CrissCross::tr("Draw"),
CrissCross::tr("Nice match") );
}
////////////
//// AI ////
void CrissCross::AI_playTurn()
{
this->AI_updateWeights();
emit this->board_buttons[ this->AI_makeChoice() ]->clicked();
}
void CrissCross::AI_updateWeights()
{
// reset the weights
for ( int i=0; i<9; i++ ) {
this->board_weights[ i ] = 0;
}
// calculate the new weights
unsigned int win_streak, lose_streak;
std::vector<unsigned int> empty_tiles;
for ( const auto& sequence : this->sequences ) {
// reset data
win_streak = lose_streak = 0;
empty_tiles.clear();
// check the tiles in the sequence
for ( const auto& index : sequence ) {
if ( this->board[ index ] == this->p_turn ) {
win_streak ++;
} else if ( this->board[ index ] > 0 ) {
lose_streak ++;
} else {
empty_tiles.push_back( index );
}
}
// set the new weight for the empty tiles
const unsigned int new_weight = (win_streak>lose_streak) ? win_streak+1 : lose_streak+1;
for ( const auto& index : empty_tiles ) {
if ( new_weight > this->board_weights[ index ] ) {
this->board_weights[ index ] = new_weight;
}
}
}
}
const unsigned int CrissCross::AI_makeChoice()
{
// get a list of the heaviest tiles
std::vector<unsigned int> moves;
unsigned int max_weight = 0;
unsigned int index = 0;
for ( const auto& weight : this->board_weights ) {
if ( weight > max_weight ) {
// heavier weight found
max_weight = weight;
moves.clear();
moves.push_back( index );
} else if ( weight == max_weight ) {
// same weight
moves.push_back( index );
}/* else {
// lighter weight
;
}*/
index ++;
}
// decide the movement (or better, randomly pick one)
unsigned int next_move;
if ( max_weight == 0 ) {
// first turn
next_move = rand() % 9;
} else {
next_move = moves[ rand() % moves.size() ];
}
return next_move;
}

View file

@ -0,0 +1,96 @@
#ifndef CRISSCROSS_H
#define CRISSCROSS_H
#include <QIcon>
#include <QWidget>
#include <QPushButton>
namespace Ui {
class CrissCross;
}
class CrissCross : public QWidget
{
Q_OBJECT
public:
explicit CrissCross( const QPalette& style, QWidget* parent=nullptr );
~CrissCross();
private slots:
void on_button_NW_clicked();
void on_button_N_clicked();
void on_button_NE_clicked();
void on_button_W_clicked();
void on_button_C_clicked();
void on_button_E_clicked();
void on_button_SW_clicked();
void on_button_S_clicked();
void on_button_SE_clicked();
private:
Ui::CrissCross *ui;
// player turn
unsigned int p_turn = 1;
// players identity
const bool p1_human = rand() %2;
const bool p2_human = (p1_human) ? false : true;
// victory related
std::vector<unsigned int> victory_sequence;
// game data
unsigned int board[9] = {
0,0,0,
0,0,0,
0,0,0
};
QPushButton* board_buttons[9];
const QIcon icons[2] = {
QIcon(":/games/games/o.png"),
QIcon(":/games/games/x.png")
};
const unsigned int sequences[8][3] = {
{0,1,2},{3,4,5},{6,7,8}, // horizontal
{0,3,6},{1,4,7},{2,5,8}, // vertical
{0,4,8},{2,4,6} // diagonal
};
// AI data
unsigned int board_weights[9] = {
1,1,1,
1,1,1,
1,1,1
};
// game methods
void endTurn();
void nextTurn();
const bool isPlayerTurn();
const bool checkVictory();
const bool gameDraw();
void victory();
void draw();
void AI_playTurn();
void AI_updateWeights();
const unsigned int AI_makeChoice();
};
#endif // CRISSCROSS_H

View file

@ -0,0 +1,520 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CrissCross</class>
<widget class="QWidget" name="CrissCross">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>512</width>
<height>512</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>512</width>
<height>512</height>
</size>
</property>
<property name="windowTitle">
<string>LogDoctor - CrissCross</string>
</property>
<property name="windowIcon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/logo/logo/logdoctor.svg</normaloff>:/logo/logo/logdoctor.svg</iconset>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>400</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>400</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="button_NW">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Line" name="line_3">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="button_N">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="Line" name="line_4">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="button_NE">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="Line" name="line_1">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="Line" name="line_6">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="Line" name="line_7">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="button_W">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Line" name="line_9">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="button_C">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="Line" name="line_10">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="button_E">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="Line" name="line_2">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="Line" name="line_5">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="Line" name="line_8">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="button_SW">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="Line" name="line_11">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="button_S">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="Line" name="line_12">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QPushButton" name="button_SE">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="iconSize">
<size>
<width>104</width>
<height>104</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<spacer name="spacer_Board_Right">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>38</width>
<height>397</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="spacer_Board_Left">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<spacer name="spacer_Board_Top">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>16</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<spacer name="spacer_Board_Bottom">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>16</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>