test: History ui tests

This commit is contained in:
Eberhard Graether
2018-09-07 12:50:04 +02:00
parent ac7b642d8a
commit 92a19869e7
19 changed files with 879 additions and 0 deletions
@@ -0,0 +1,67 @@
#include "artificial_player.h"
ArtificialPlayer::ArtificialPlayer( Field::Token token, const char* name )
: Player( token, name ) {
}
ArtificialPlayer::~ArtificialPlayer() {
}
Field::Move ArtificialPlayer::Turn( const Field& field ) const {
Field fakeField = field.Clone();
Node node = MinMax( fakeField, token_ );
return node.move;
}
ArtificialPlayer::Node ArtificialPlayer::MinMax( Field& field, Field::Token token ) const {
Node node;
node.value = -10000;
Field::Move move;
int sameMove;
for ( int i = 0; i < 3; i++ ) {
move.row = i;
for ( int j = 0; j < 3; j++ ) {
move.col = j;
if ( !field.IsEmpty( move ) ) {
continue;
}
field.MakeMove( move, token );
int turnValue = Evaluate( field, token );
if ( !turnValue && !field.IsFull() ) {
turnValue = -MinMax( field, Field::Opponent( token ) ).value;
}
field.ClearMove( move );
if ( turnValue > node.value ) {
node.move = move;
node.value = turnValue;
sameMove = 1;
} else if ( turnValue == node.value ) {
sameMove++;
if ( 1 % sameMove == 0 ) {
node.move = move;
}
}
}
}
return node;
}
int ArtificialPlayer::Evaluate( const Field& field, Field::Token token ) const {
if ( field.SameInRow( token, 3 ) ) {
return 2;
} else if ( field.SameInRow( Field::Opponent( token ), 2 ) ) {
return -1;
} else if ( field.SameInRow( token, 2 ) > 1 ) {
return 1;
}
return 0;
}
@@ -0,0 +1,24 @@
#ifndef _ARTIFICIAL_PLAYER_
#define _ARTIFICIAL_PLAYER_
#include "player.h"
class ArtificialPlayer : public Player {
public:
ArtificialPlayer( Field::Token token, const char* name );
virtual ~ArtificialPlayer();
virtual Field::Move Turn( const Field& field ) const;
private:
struct Node {
Field::Move move;
int value;
};
Node MinMax( Field& field, Field::Token token ) const;
int Evaluate( const Field& field, Field::Token token ) const;
};
#endif // _ARTIFICIAL_PLAYER_
+126
View File
@@ -0,0 +1,126 @@
#include "field.h"
#include "io.h"
Field::Token Field::Opponent( Token token ) {
if ( token == PlayerA ) {
return PlayerB;
} else if (token == PlayerB){
return PlayerA;
} else {
return None;
}
}
Field::Field()
: left_( 9 ) {
grid_ = new Token* [3];
for ( int i = 0; i < 3 ; i++ ) {
grid_[i] = new Token[3];
}
}
Field::~Field() {
for ( int i = 0; i < 3; i++ ) {
delete [] grid_[i];
}
delete [] grid_;
}
Field Field::Clone() const {
Field field;
for ( int i = 0; i < 3; i++ ) {
for ( int j = 0; j < 3; j++ ) {
field.grid_[i][j] = grid_[i][j];
}
}
field.left_ = left_;
return field;
}
void Field::Clear() {
for ( int i = 0; i < 3; i++ ) {
for ( int j = 0; j < 3; j++ ) {
grid_[i][j] = None;
}
}
left_ = 9;
}
void Field::Show() const {
io::stringOut(" 1 2 3\n");
for ( int row = 0; row < 3; row++ ) {
io::numberOut(row + 1);
io::stringOut(" ");
for ( int col = 0; col < 3; col++ ) {
if ( grid_[row][col] == PlayerA ) {
io::stringOut(" X ");
} else if ( grid_[row][col] == PlayerB ) {
io::stringOut(" O ");
} else {
io::stringOut(" ");
}
if ( col < 2 ) {
io::stringOut("|");
}
}
if ( row < 2 ) {
io::stringOut("\n -----------\n");
}
}
io::stringOut("\n\n");
}
int Field::SameInRow( Token token, int amount ) const {
int sum = amount * token;
int count = 0;
for ( int i = 0; i < 3; i++ ) {
if ( grid_[i][0] + grid_[i][1] + grid_[i][2] == sum ) {
count++;
} else if ( grid_[0][i] + grid_[1][i] + grid_[2][i] == sum ) {
count++;
}
}
if ( grid_[0][0] + grid_[1][1] + grid_[2][2] == sum ) {
count++;
} else if ( grid_[2][0] + grid_[1][1] + grid_[0][2] == sum ) {
count++;
}
return count;
}
bool Field::InRange( const Move& move ) const {
return move.row >= 0 && move.row < 3 && move.col >= 0 && move.col < 3;
}
bool Field::IsEmpty( const Move& move ) const {
return grid_[move.row][move.col] == None;
}
bool Field::IsFull() const {
return left_ == 0;
}
void Field::MakeMove( const Move& move, Token token ) {
if ( !InRange( move ) || !IsEmpty( move ) || token == None || !left_ ) {
return;
}
grid_[move.row][move.col] = token;
left_--;
}
void Field::ClearMove( const Move& move ) {
if ( !InRange( move ) || !IsEmpty( move ) || left_ == 9 ) {
return;
}
grid_[move.row][move.col] = None;
left_++;
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef _FIELD_
#define _FIELD_
#include "game_object.h"
class Field : public GameObject {
public:
enum Token {
None = 0,
PlayerA = 1,
PlayerB = 4
};
static Token Opponent( Token token );
struct Move {
int row;
int col;
};
Field();
~Field();
Field Clone() const;
void Clear();
void Show() const;
int SameInRow( Token token, int amount ) const;
bool InRange( const Move& move ) const;
bool IsEmpty( const Move& move ) const;
bool IsFull() const;
void MakeMove( const Move& move, Token token );
void ClearMove( const Move& move );
private:
Token** grid_;
int left_;
};
#endif // _FIELD_
+10
View File
@@ -0,0 +1,10 @@
#ifndef _GAME_OBJECT_
#define _GAME_OBJECT_
class GameObject {
public:
GameObject() {}
virtual ~GameObject() {}
};
#endif // _GAME_OBJECT_
@@ -0,0 +1,49 @@
#include "human_player.h"
#include "io.h"
HumanPlayer::HumanPlayer( Field::Token token, const char* name )
: Player( token, name ) {
}
HumanPlayer::~HumanPlayer() {
}
Field::Move HumanPlayer::Turn( const Field& field ) const {
Field::Move move;
io::stringOut(name_);
io::stringOut("\n");
do {
move = Input();
move.row -= 1;
move.col -= 1;
} while ( !Check( field, move ) );
return move;
}
Field::Move HumanPlayer::Input() const {
Field::Move move;
move.row = -1;
move.col = -1;
io::stringOut("Insert row: ");
move.row = io::numberIn();
io::stringOut("Insert column: ");
move.col = io::numberIn();
io::stringOut("\n");
return move;
}
bool HumanPlayer::Check( const Field& field, const Field::Move& move ) const {
if ( !field.InRange( move ) ) {
io::stringOut("Wrong input!\n");
return false;
} else if ( !field.IsEmpty( move ) ) {
io::stringOut("Is occupied!\n");
return false;
}
return true;
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef _HUMAN_PLAYER_
#define _HUMAN_PLAYER_
#include "player.h"
class HumanPlayer : public Player {
public:
HumanPlayer( Field::Token token, const char* name );
virtual ~HumanPlayer();
virtual Field::Move Turn( const Field& field ) const;
private:
Field::Move Input() const;
bool Check( const Field& field, const Field::Move& move ) const;
};
#endif // _HUMAN_PLAYER_
+23
View File
@@ -0,0 +1,23 @@
#include "io.h"
#include <iostream>
#include <sstream>
#include <string>
int io::numberIn() {
std::string input;
getline( std::cin, input );
std::stringstream stream( input );
int number;
stream >> number;
return number;
}
void io::numberOut(int num) {
std::cout << num;
}
void io::stringOut(const char* str) {
std::cout << str;
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef _IO_
#define _IO_
namespace io {
int numberIn();
void numberOut(int num);
void stringOut(const char* str);
}
#endif // _IO_
+11
View File
@@ -0,0 +1,11 @@
#include "tictactoe.h"
int main() {
TicTacToe tictactoe;
while ( tictactoe.Start() ) {
tictactoe.Run();
}
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
#include "player.h"
Player::Player( Field::Token token, const char* name )
: token_( token )
, name_( name ) {
}
Player::~Player() {
}
const Field::Token& Player::getToken() const
{
return token_;
}
const char* Player::getName() const
{
return name_;
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef _PLAYER_
#define _PLAYER_
#include "field.h"
#include "game_object.h"
class Player : public GameObject {
public:
Player( Field::Token token, const char* name );
virtual ~Player();
virtual Field::Move Turn( const Field& field ) const = 0;
const Field::Token& getToken() const;
const char* getName() const;
protected:
const Field::Token token_;
const char* name_;
};
#endif // _PLAYER_
+8
View File
@@ -0,0 +1,8 @@
#include "template.h"
void work()
{
int v = diff<int>(9, 5);
double w = diff<double>(0.5, 0.3);
}
+6
View File
@@ -0,0 +1,6 @@
template <typename T>
T diff(T a, T b)
{
return a - b;
}
+86
View File
@@ -0,0 +1,86 @@
#include "tictactoe.h"
#include "artificial_player.h"
#include "human_player.h"
#include "io.h"
TicTacToe::TicTacToe() {
players_[0] = 0;
players_[1] = 0;
}
TicTacToe::~TicTacToe() {
Reset();
}
bool TicTacToe::Start() {
Reset();
io::stringOut("Tic Tac Toe\n\n[1] Human\n[2] Computer\n[3] Quit\n\n");
players_[0] = SelectPlayer( Field::PlayerA, "Player A" );
if ( !players_[0] ) {
return false;
}
players_[1] = SelectPlayer( Field::PlayerB, "Player B" );
if ( !players_[1] ) {
return false;
}
io::stringOut("\n");
return true;
}
void TicTacToe::Run() {
field_.Show();
int playerIndex = 0;
for ( int i = 0; i < 9; i++ ) {
Player& player = *players_[playerIndex];
field_.MakeMove( player.Turn( field_ ), player.getToken() );
field_.Show();
if ( field_.SameInRow( player.getToken(), 3 ) ) {
io::stringOut(player.getName());
io::stringOut(" won!\n\n");
return;
}
playerIndex = ( playerIndex + 1 ) % 2;
}
io::stringOut("Game ends in draw!\n\n");
}
void TicTacToe::Reset() {
field_.Clear();
for ( int i = 0; i < 2; i++ ) {
if ( players_[i] ) {
delete players_[i];
players_[i] = 0;
}
}
}
Player* TicTacToe::SelectPlayer( Field::Token token, const char* name ) const {
int selection = 0;
while ( true ) {
io::stringOut("Choose ");
io::stringOut(name);
io::stringOut(": ");
selection = io::numberIn();
switch ( selection ) {
case 1 : return new HumanPlayer( token, name );
case 2 : return new ArtificialPlayer( token, name );
case 3 : return 0;
default : io::stringOut("Wrong input!\n");
}
}
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef _TIC_TAC_TOE_
#define _TIC_TAC_TOE_
#include "field.h"
#include "player.h"
class TicTacToe {
public:
TicTacToe();
~TicTacToe();
bool Start();
void Run();
private:
void Reset();
Player* SelectPlayer( Field::Token token, const char* name ) const;
Player* players_[2];
Field field_;
};
#endif // _TIC_TAC_TOE_
+119
View File
@@ -0,0 +1,119 @@
#define INVOCATION_TESTS
// TEST: build history
// START ----------------------------------------------------------------------
// ACTIONS: activate all these symbols top to bottom:
class One;
int two;
struct Three;
enum Four
{
FIVE
};
#define SIX
typedef bool Seven;
// RESULTS:
// - Seven is active
// - dropdown position at top: Seven
// - history menu: Seven at top
// END ------------------------------------------------------------------------
// TEST: dropdown open/close - click
// START ----------------------------------------------------------------------
// ACTION: Expand dropdown and click outside
// RESULT: dropdown disappears
// END ------------------------------------------------------------------------
// TEST: dropdown open/close - ESC
// START ----------------------------------------------------------------------
// ACTION: Expand dropdown and press ESC
// RESULT: dropdown disappears
// END ------------------------------------------------------------------------
// TEST: going back
// START ----------------------------------------------------------------------
// ACTION: Click back button
// ACTION: Use action History -> Back
// ACTION: Context menu: Back
// ACTION: Press Backspace
// RESULTS:
// - symbol name goes down
// - dropdown position goes down
// - history menu: current symbol always on top, end result:
// Three, Four, FIVE, SIX, Seven, two, One
// END ------------------------------------------------------------------------
// TEST: going forward
// START ----------------------------------------------------------------------
// ACTION: Click forward button
// ACTION: Use action History -> Forward
// ACTION: Context menu: Forward
// RESULTS:
// - symbol name goes up
// - dropdown position goes up
// - history menu: current symbl always on top, end result:
// SIX, FIVE, Four, Three, Seven, two, One
// END ------------------------------------------------------------------------
// TEST: dropdown selection
// START ----------------------------------------------------------------------
// ACTION: Expand dropdown and click on One
// RESULTS:
// - One is activated
// - dropdown position down at One
// END ------------------------------------------------------------------------
// TEST: history overwrite
// START ----------------------------------------------------------------------
namespace eight {} // <- ACTION: Activate namespace
// RESULTS:
// - dropdown list shrinked to: eight, One
// - dropdown position at top
// - history menu shows: eight, SIX, FIVE, Four, ...
// END ------------------------------------------------------------------------
// TEST: history menu selection
// START ----------------------------------------------------------------------
// ACTION: Select 'Three' from history menu
// RESULTS:
// - dropdown list: Three, eight, One
// - dropdown position at top
// - history menu shows: Three, eight, SIX, FIVE, ...
// END ------------------------------------------------------------------------
+176
View File
@@ -0,0 +1,176 @@
#define STATE_TESTS
// TEST: edge clicks
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'TicTacToe::Run()'
// ACTION 2: Click edge 'TicTacToe::Run()' -> 'Field::Show()'
// ACTION 3: Click edge 'TicTacToe::Run()' -> 'Player::Turn()'
// ACTION 4: Click 'main()'
// ACTION 5: go back
// RESULT 5: graph and code restored to edge 'TicTacToe::Run()' -> 'Player::Turn()'
// ACTION 6: go back again
// RESULT 6: graph and code restored to edge 'TicTacToe::Run()' -> 'Field::Show()'
// ACTION 6: go back again
// RESULT 6: graph and code restored to node 'TicTacToe::Run()'
// END ------------------------------------------------------------------------
// TEST: graph manipulations & scroll position
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'Field::Show()'
// ACTION 2: Click on 'Field'
// ACTION 3: Expand node 'Player'
// ACTION 4: Hide node 'GameObject'
// ACTION 5: Move node 'numberOut' far to the right
// ACTION 6: Scroll all the way to the right
// ACTION 7: Search for 'main'
// ACTION 8: go back
// RESULTS 8:
// - scroll position restored
// - graph restored
// - Player expanded
// - GameObject hidden
// - 'numberOut' far right
// ACTION 9: go back
// RESULTS 9:
// - 'GameObject' visible
// - 'numberOut' back to old position
// - Player still expanded
// ACTION 10: go back
// RESULT 10: 'Field::Show()' restored
// END ------------------------------------------------------------------------
// TEST: depth graph
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'io::stringOut'
// ACTION 2: Show callee graph
// ACTION 3: Change depth to 1
// ACTION 3: Hide node 'Field'
// ACTION 4: Search for 'main'
// ACTION 5: go back
// RESULT 5: graph depth 1 restored
// ACTION 6: go back
// RESULT 6: 'Field' restored
// ACTION 7: go back
// RESULT 7: graph depth 5 restored
// ACTION 8: go back
// RESULT 8: 'io::stringOut' graph restored
// END ------------------------------------------------------------------------
// TEST: graph multiple active
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'diff<..>'
// ACTION 2: Click on 'diff<int>'
// ACTION 3: Search for 'main'
// ACTION 4: go back
// RESULT 4: graph to 'diff<int>' restored
// ACTION 5: go back
// RESULT 5: graph with multiple active 'diff<..>' restored
// END ------------------------------------------------------------------------
// TEST: local symbol
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'Field::Show()'
// ACTION 2: Click local symbol 'row'
// ACTION 3: Activate other symbol
// ACTION 4: go back
// RESULTS 4:
// - graph and code restored to 'Field::Show()'
// - local symbol 'row' active
// END ------------------------------------------------------------------------
// TEST: code reference
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'io::numberIn()'
// ACTION 2: Navigate to 3rd reference
// ACTION 3: Activate other symbol
// ACTION 4: go back
// RESULTS 4: graph and code restored to 3rd reference of 'io::numberIn()'
// END ------------------------------------------------------------------------
// TEST: snippet state & scroll position
// START ----------------------------------------------------------------------
// ACTION 1: Switch to code snippet mode
// ACTION 2: Search for 'io::stringOut()'
// ACTION 3: Minimize 2nd and 3rd snippet
// ACTION 4: Expand last snippet
// ACTION 5: Scroll to recognizeable position
// ACTION 6: Activate other symbol
// ACTION 7: go back
// RESULTS 7:
// - Scroll position restored
// - Only first and last snippet expanded
// END ------------------------------------------------------------------------
// TEST: code view mode
// START ----------------------------------------------------------------------
// ACTION 1: Search for 'Field::Show()'
// ACTION 2: Click maximize on second snippet
// ACTION 3: go back
// RESULTS 3:
// - Snippet mode restored
// - Scroll position restored
// END ------------------------------------------------------------------------
// TEST: single mode scroll position
// START ----------------------------------------------------------------------
// ACTION 1: Switch to code single file mode
// ACTION 2: Search for 'Field::SameInRow()'
// ACTION 3: Scroll to recognizeable position
// ACTION 4; Activate other symbol
// ACTION 5: go back
// RESULTS 5:
// - Active symbol 'Field::SameInRow()' restored
// - Scroll position restored
// END ------------------------------------------------------------------------
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<config>
<description>Test Suites:\n\n [::\tmINVOCATION_TESTS\ts\tp]\n [::\tmSTATE_TESTS\ts\tp]\n</description>
<source_groups>
<source_group_475571d1-b082-4fff-81c5-d099c19f957d>
<cpp_standard>c++17</cpp_standard>
<cross_compilation>
<target>
<abi>unknown</abi>
<arch>x86_64</arch>
<sys>unknown</sys>
<vendor>unknown</vendor>
</target>
<target_options_enabled>0</target_options_enabled>
</cross_compilation>
<exclude_filters>
<exclude_filter>data/files/template.h</exclude_filter>
</exclude_filters>
<name>C++ Source Group</name>
<source_extensions>
<source_extension>.cpp</source_extension>
<source_extension>.cxx</source_extension>
<source_extension>.cc</source_extension>
</source_extensions>
<source_paths>
<source_path>data</source_path>
</source_paths>
<status>enabled</status>
<type>C++ Source Group</type>
</source_group_475571d1-b082-4fff-81c5-d099c19f957d>
</source_groups>
<version>7</version>
</config>