From bf2f79dc8dba651be0961eb5d701aba45209f42e Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:48 +0000 Subject: [PATCH 001/190] Initial commit with ttrts base main.cpp --- ttrts/CMakeLists.txt | 14 ++++++++++++++ ttrts/main.cpp | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 ttrts/CMakeLists.txt create mode 100644 ttrts/main.cpp diff --git a/ttrts/CMakeLists.txt b/ttrts/CMakeLists.txt new file mode 100644 index 0000000..e1dd18b --- /dev/null +++ b/ttrts/CMakeLists.txt @@ -0,0 +1,14 @@ +# ====================== ttrts ======================= +# Project name +project( ttrts ) + +# Add the sources +set( SOURCES + main.cpp +) + +# Set to use c++11, because we're cool like that +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) + +# Add the executable +add_executable( ttrts ${SOURCES} ) \ No newline at end of file diff --git a/ttrts/main.cpp b/ttrts/main.cpp new file mode 100644 index 0000000..02fcc4c --- /dev/null +++ b/ttrts/main.cpp @@ -0,0 +1,5 @@ +// Main program entry point +int main() +{ + return 0; +}; \ No newline at end of file From 8ddb8ae9164d52c2e390563ff3e0b0cb5513457a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:50 +0000 Subject: [PATCH 002/190] Some initial game board code, really simple --- game/CMakeLists.txt | 14 ++++++++++ game/game.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++ game/game.h | 49 ++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 game/CMakeLists.txt create mode 100644 game/game.cpp create mode 100644 game/game.h diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt new file mode 100644 index 0000000..6ef148f --- /dev/null +++ b/game/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8.7) + +# game project +project( game ) + +# Set to use c++11, because we're cool like that +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) + +# Add the sources +set( SOURCES + game.cpp +) + +add_library( game ${SOURCES} ) \ No newline at end of file diff --git a/game/game.cpp b/game/game.cpp new file mode 100644 index 0000000..624985e --- /dev/null +++ b/game/game.cpp @@ -0,0 +1,65 @@ +#include "game.h" +// ---------------------------------------------- + +// Default constructor for the board +CBoardData::CBoardData( unsigned int c, unsigned int r ) +: cols ( c ) +, rows ( r ) +, total ( rows * cols ) +{ + board = new square_t[total]; + fill(square_invalid); +} + +CBoardData::~CBoardData() +{ + delete[] board; +} + +// Clear the board +void CBoardData::clear() +{ + fill(square_empty); +} + +// Fill the board +void CBoardData::fill(square_t v) +{ + std::fill(board,board+total,v); +} + +// print get a slot on the board +CBoardData::square_t CBoardData::get( unsigned int c, unsigned int r ) const +{ + if ( (r >= rows) || (c >= cols) ) + return square_invalid; + + return board[r*c]; +} + +// print a board +void CBoardData::print() const +{ + for ( unsigned int r = 0; r < rows; r++ ) + { + for ( unsigned int c = 0; c < cols; c++ ) + { + std::cout< // std::cout +#include // std::numeric_limits + +// Class to store simple data about a board +class CBoardData +{ +public: + // Type for the board square + typedef char square_t +; + // Invalid value for the board square + static const square_t square_invalid = std::numeric_limits::max(); + static const square_t square_empty = 32; // 32 is ascii empty + + // Default constructor + CBoardData( unsigned int c, unsigned int r ); + ~CBoardData(); + + // Print the board + void print() const; + + // clear the board + void clear(); + + // fill the board + void fill(square_t v); + + // Get a square on the board + square_t get( unsigned int c, unsigned int r ) const; + +private: + + const unsigned int cols; // Number of columns + const unsigned int rows; // Number of rows + const unsigned int total; // Total number of pieces + + square_t* board; // Board data storage +}; + +// Namespace for testing functions +namespace tests +{ + void test_CBoardData(); +}; + +#endif //_GAME_H_ \ No newline at end of file From 3c61d1a72f91861d98875dfb27c199995f0bf708 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:51 +0000 Subject: [PATCH 003/190] Higher level cmakelist to control whole project --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cdfab09 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required( VERSION 2.8.7 ) + +# Subprojects +add_subdirectory( ttrts ) +add_subdirectory( game ) From 3c74a2a6093eb6619d56b84fecf0cd3bc9d65d9d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:51 +0000 Subject: [PATCH 004/190] Add testing excecutable --- CMakeLists.txt | 1 + test/CMakeLists.txt | 20 ++++++++++++++++++++ test/test.cpp | 7 +++++++ 3 files changed, 28 insertions(+) create mode 100644 test/CMakeLists.txt create mode 100644 test/test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cdfab09..9eb2f49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,3 +3,4 @@ cmake_minimum_required( VERSION 2.8.7 ) # Subprojects add_subdirectory( ttrts ) add_subdirectory( game ) +add_subdirectory( test ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..85597f3 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,20 @@ + +# ====================== tests ======================= +# Project name +project( ttrts-test ) + +include_directories( + ../game +) + +set( SOURCES + test.cpp +) + +# Set to use c++11, because we're cool like that +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) + +# Add the executable +add_executable( ttrts-test ${SOURCES} ) + +target_link_libraries( ttrts-test game ) \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 0000000..671a826 --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,7 @@ +#include "game.h" + +// Main program entry point +int main() +{ + tests::test_CBoardData(); +}; \ No newline at end of file From 9a97fddd2f48949cb7553757d174685a9633150a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:51 +0000 Subject: [PATCH 005/190] Add gitignore with build forlder ignored --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file From 2320b87b8df8f282ae07e9a52de177e7c307233d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:52 +0000 Subject: [PATCH 006/190] Add readme and directories for other libraries --- README.md | 33 +++++++++++++++++++++++++++++++++ net/CMakeLists.txt | 0 player/CMakeLists.txt | 0 ui/CMakeLists.txt | 0 4 files changed, 33 insertions(+) create mode 100644 README.md create mode 100644 net/CMakeLists.txt create mode 100644 player/CMakeLists.txt create mode 100644 ui/CMakeLists.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..58619d8 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +ttrts +===== + +*the Tiny Terminal RTS where the players write their AIs* + +Introduction +------------ +We aim to create a simple terminal based rts where the user programs their armies AI. + +Targets +------- +### ttrts +Main ttrts executable , runs from the command line and can act as host or client + +### ttrts-test +Test executable, to be compiled and run to test various functionality + +Libraries +--------- +### game +Implementation of the RTS rules and simulation + +### net +Net code for hosting the server and communicating with clients + +### ui +Wrapper for user interface for the terminal, this only really needs three stages +* Initialise the game with settings and connect the clients +* Run the game simulation to it's conclusion +* Display the game result + +### player +Custom player AI code, this should contain examples and test code to help newcomers begin their journey \ No newline at end of file diff --git a/net/CMakeLists.txt b/net/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/player/CMakeLists.txt b/player/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt new file mode 100644 index 0000000..e69de29 From e8429ae4a29ae04946cdc3ba31a338bad06a266c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:52 +0000 Subject: [PATCH 007/190] Initial README for the game library with initial game design. --- game/README.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 game/README.md diff --git a/game/README.md b/game/README.md new file mode 100644 index 0000000..6f95afb --- /dev/null +++ b/game/README.md @@ -0,0 +1,53 @@ +ttrts Game Design +================= + +The game takes place in a series of simultanious turns on an arbitrarilly sized 2D grid. + +Each player is in control of a set number of starting units, each turn recieves data on the status of the board. + +Each player must then issue a single command to each unit in thier control. + +The engine then takes all commands, evaluates all movement first simultaniously, then all other commands. + +All attempted movement to the same square by two or more units will fail. + +-------------------------------------------------------- +Units +----- + +Currently only one unit, this will be expanded in the future. + +Units have a set of properties, and commands than can be issued. +Commands take the form of a single char literal. +All units have 1 health. + + +## X +X is your basic slow melee unit. It has very basic data and controls, basically acting like a turtle. + +##### properties +| property | type | description | +|:---------|:--------|:----------------------------------| +| pos | char[2] | x,y position on the grid | +| dir | char | compass direction unit is facing | + +##### commands +| command | description | +|:---------|:--------------------------------------------| +| L/R | turn left/right | +| F | move forward | +| B | move backward | +| A | deal 1 damage to unit directly in front | + +-------------------------------------------------------- +The Grid +-------- + +As an example, let's start with a basic starting `[10,5]` grid +```` +0000000000 +0000000000 +0000000000 +0000000000 +0000000000 +```` From 743c9a28592d6da4ef466e827be25acadc322aae Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:53 +0000 Subject: [PATCH 008/190] Rename CBoardData, refactor a few things and add a maths library folder --- README.md | 3 ++ game/CMakeLists.txt | 5 ++++ game/README.md | 10 +++---- game/board.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++ game/board.h | 47 +++++++++++++++++++++++++++++++ game/game.cpp | 64 ------------------------------------------ game/game.h | 45 ------------------------------ maths/basetypes.h | 7 +++++ maths/vector2.h | 12 ++++++++ test/CMakeLists.txt | 1 + test/test.cpp | 4 +-- 11 files changed, 150 insertions(+), 116 deletions(-) create mode 100644 game/board.cpp create mode 100644 game/board.h create mode 100644 maths/basetypes.h create mode 100644 maths/vector2.h diff --git a/README.md b/README.md index 58619d8..67fdcaa 100644 --- a/README.md +++ b/README.md @@ -29,5 +29,8 @@ Wrapper for user interface for the terminal, this only really needs three stages * Run the game simulation to it's conclusion * Display the game result +### maths +simple maths library for 2D calculations and types + ### player Custom player AI code, this should contain examples and test code to help newcomers begin their journey \ No newline at end of file diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 6ef148f..e780c69 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -3,12 +3,17 @@ cmake_minimum_required(VERSION 2.8.7) # game project project( game ) +include_directories( + ../maths +) + # Set to use c++11, because we're cool like that set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) # Add the sources set( SOURCES game.cpp + board.cpp ) add_library( game ${SOURCES} ) \ No newline at end of file diff --git a/game/README.md b/game/README.md index 6f95afb..7c4e70f 100644 --- a/game/README.md +++ b/game/README.md @@ -1,7 +1,7 @@ ttrts Game Design ================= -The game takes place in a series of simultanious turns on an arbitrarilly sized 2D grid. +The game takes place in a series of simultanious turns on an arbitrarilly sized 2D board. Each player is in control of a set number of starting units, each turn recieves data on the status of the board. @@ -28,8 +28,8 @@ X is your basic slow melee unit. It has very basic data and controls, basically ##### properties | property | type | description | |:---------|:--------|:----------------------------------| -| pos | char[2] | x,y position on the grid | -| dir | char | compass direction unit is facing | +| pos | char[2] | x,y position on the board | +| dir | char[2] | compass direction unit is facing | ##### commands | command | description | @@ -40,10 +40,10 @@ X is your basic slow melee unit. It has very basic data and controls, basically | A | deal 1 damage to unit directly in front | -------------------------------------------------------- -The Grid +The Board -------- -As an example, let's start with a basic starting `[10,5]` grid +As an example, let's start with a basic starting `[10,5]` board ```` 0000000000 0000000000 diff --git a/game/board.cpp b/game/board.cpp new file mode 100644 index 0000000..54b4a95 --- /dev/null +++ b/game/board.cpp @@ -0,0 +1,68 @@ +#include "board.h" + +#include // std::cout + +// ---------------------------------------------- + +// Default constructor for the board +CBoard::CBoard( unsigned int c, unsigned int r ) +: cols ( c ) +, rows ( r ) +, total ( rows * cols ) +{ + board = new square_t[total]; + fill(square_invalid); +} + +CBoard::~CBoard() +{ + delete[] board; +} + +// Clear the board +void CBoard::clear() +{ + fill(square_empty); +} + +// Fill the board +void CBoard::fill(square_t v) +{ + std::fill(board,board+total,v); +} + +// print get a slot on the board +square_t CBoard::get( unsigned int c, unsigned int r ) const +{ + if ( (r >= rows) || (c >= cols) ) + return square_invalid; + + return board[r*c]; +} + +// print a board +void CBoard::debug_print() const +{ + for ( unsigned int r = 0; r < rows; r++ ) + { + for ( unsigned int c = 0; c < cols; c++ ) + { + std::cout<<(char)board[r*c]; + } + std::cout< // std::numeric_limits + +// Class to store simple data about a board +class CBoard +{ +public: + + // Invalid value for the board square + static const square_t square_invalid = std::numeric_limits::max(); + static const square_t square_empty = 32; // 32 is ascii empty + + // Default constructor + CBoard( unsigned int c, unsigned int r ); + ~CBoard(); + + // Print the board + void debug_print() const; + + // clear the board + void clear(); + + // fill the board + void fill(square_t v); + + // Get a square on the board + square_t get( unsigned int c, unsigned int r ) const; + +private: + + const unsigned int cols; // Number of columns + const unsigned int rows; // Number of rows + const unsigned int total; // Total number of pieces + + square_t* board; // Board data storage +}; + +// Namespace for testing functions +namespace tests +{ + void test_CBoard(); +}; + +#endif //_BOARD_H_ \ No newline at end of file diff --git a/game/game.cpp b/game/game.cpp index 624985e..7bb61ba 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1,65 +1 @@ #include "game.h" -// ---------------------------------------------- - -// Default constructor for the board -CBoardData::CBoardData( unsigned int c, unsigned int r ) -: cols ( c ) -, rows ( r ) -, total ( rows * cols ) -{ - board = new square_t[total]; - fill(square_invalid); -} - -CBoardData::~CBoardData() -{ - delete[] board; -} - -// Clear the board -void CBoardData::clear() -{ - fill(square_empty); -} - -// Fill the board -void CBoardData::fill(square_t v) -{ - std::fill(board,board+total,v); -} - -// print get a slot on the board -CBoardData::square_t CBoardData::get( unsigned int c, unsigned int r ) const -{ - if ( (r >= rows) || (c >= cols) ) - return square_invalid; - - return board[r*c]; -} - -// print a board -void CBoardData::print() const -{ - for ( unsigned int r = 0; r < rows; r++ ) - { - for ( unsigned int c = 0; c < cols; c++ ) - { - std::cout< // std::cout -#include // std::numeric_limits - -// Class to store simple data about a board -class CBoardData -{ -public: - // Type for the board square - typedef char square_t -; - // Invalid value for the board square - static const square_t square_invalid = std::numeric_limits::max(); - static const square_t square_empty = 32; // 32 is ascii empty - - // Default constructor - CBoardData( unsigned int c, unsigned int r ); - ~CBoardData(); - - // Print the board - void print() const; - - // clear the board - void clear(); - - // fill the board - void fill(square_t v); - - // Get a square on the board - square_t get( unsigned int c, unsigned int r ) const; - -private: - - const unsigned int cols; // Number of columns - const unsigned int rows; // Number of rows - const unsigned int total; // Total number of pieces - - square_t* board; // Board data storage -}; - -// Namespace for testing functions -namespace tests -{ - void test_CBoardData(); -}; - #endif //_GAME_H_ \ No newline at end of file diff --git a/maths/basetypes.h b/maths/basetypes.h new file mode 100644 index 0000000..bacfeb1 --- /dev/null +++ b/maths/basetypes.h @@ -0,0 +1,7 @@ +#ifndef _BASETYPES_H_ +#define _BASETYPES_H_ + +// Type for the board square +typedef short square_t; + +#endif //_BASETYPES_H_ \ No newline at end of file diff --git a/maths/vector2.h b/maths/vector2.h new file mode 100644 index 0000000..d375fee --- /dev/null +++ b/maths/vector2.h @@ -0,0 +1,12 @@ +#ifndef _VECTOR2_H_ +#define _VECTOR2_H_ + +#include "basetypes.h" + +struct vector2 +{ + square_t x; + square_t y; +}; + +#endif //_VECTOR2_H_ \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 85597f3..ceca484 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,6 +5,7 @@ project( ttrts-test ) include_directories( ../game + ../maths ) set( SOURCES diff --git a/test/test.cpp b/test/test.cpp index 671a826..e7b2a28 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,7 +1,7 @@ -#include "game.h" +#include "board.h" // Main program entry point int main() { - tests::test_CBoardData(); + tests::test_CBoard(); }; \ No newline at end of file From 5f7dbb34d971034a4890dd6f12e41eb6eb60d745 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:53 +0000 Subject: [PATCH 009/190] Update Readme with info from discussion --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 67fdcaa..0ea84fa 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,10 @@ Wrapper for user interface for the terminal, this only really needs three stages * Initialise the game with settings and connect the clients * Run the game simulation to it's conclusion * Display the game result +* Acsii Colour wrapper for separate teams ### maths simple maths library for 2D calculations and types ### player -Custom player AI code, this should contain examples and test code to help newcomers begin their journey \ No newline at end of file +Custom player AI code, this should contain examples and test code to help newcomers begin their journey From 51cdda821dcdf23f19dd872b4554008afe8962b7 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:53 +0000 Subject: [PATCH 010/190] Update the README.md --- README.md | 55 ++++++++++++++++++++++++++++++++------------------ game/README.md | 8 ++++---- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 0ea84fa..a7206fb 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,52 @@ -ttrts -===== +# TTRTS +*The Tiny Terminal RTS where the players write their AIs* -*the Tiny Terminal RTS where the players write their AIs* +------------------------------------------------------------------------------------ +## Introduction +We aim to create a simple terminal based rts where a user can program an AI to control their army -Introduction ------------- -We aim to create a simple terminal based rts where the user programs their armies AI. +------------------------------------------------------------------------------------ +## Gameplay +1. TTRTS clients are run from the command line +2. clients will connect and confirm initial board state + 1. clients output a text file with game data for this turn + 2. a player, or program, reads the game data file and outputs an instructions file + 3. clients read the instructions file, simulates the turn + 4. game state is verified between clients and output + 5. repeat until an end state is reached +3. once game is finished, host and clients disconnect and a winner is notified -Targets -------- -### ttrts -Main ttrts executable , runs from the command line and can act as host or client +*see [the game directory](game) for full game rules* -### ttrts-test +------------------------------------------------------------------------------------ +## Source + + +### Targets +##### ttrts +Main TTRTS executable , runs from the command line and can act as host or client + +##### ttrts-test Test executable, to be compiled and run to test various functionality -Libraries ---------- -### game -Implementation of the RTS rules and simulation +##### player +Custom player AI code, this should contain examples and test code to help newcomers begin their journey -### net + +### Libraries +##### game +Implementation of the RTS rules and simulation. [game](game) has full information on it's implementation. + +##### net Net code for hosting the server and communicating with clients -### ui +##### ui Wrapper for user interface for the terminal, this only really needs three stages * Initialise the game with settings and connect the clients * Run the game simulation to it's conclusion * Display the game result * Acsii Colour wrapper for separate teams -### maths +##### maths simple maths library for 2D calculations and types -### player -Custom player AI code, this should contain examples and test code to help newcomers begin their journey diff --git a/game/README.md b/game/README.md index 7c4e70f..53a2077 100644 --- a/game/README.md +++ b/game/README.md @@ -22,21 +22,21 @@ Commands take the form of a single char literal. All units have 1 health. -## X -X is your basic slow melee unit. It has very basic data and controls, basically acting like a turtle. +## V +V is your basic slow melee unit. It has very basic data and controls, basically acting like a turtle. +Can be represented based on direction by `<` `^` `>` `v` ##### properties | property | type | description | |:---------|:--------|:----------------------------------| | pos | char[2] | x,y position on the board | -| dir | char[2] | compass direction unit is facing | +| dir | char | compass direction unit is facing | ##### commands | command | description | |:---------|:--------------------------------------------| | L/R | turn left/right | | F | move forward | -| B | move backward | | A | deal 1 damage to unit directly in front | -------------------------------------------------------- From 64f9638352112c09d2baecc20c670c4ddd3bb251 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:54 +0000 Subject: [PATCH 011/190] Implement some more base types, including basic units. This should be much closer to final interface --- game/board.cpp | 57 ++++++++++++----------------------------- game/board.h | 65 ++++++++++++++++++++++++++--------------------- game/game.h | 13 ++++++++++ game/unit.h | 26 +++++++++++++++++++ game/unitv.cpp | 15 +++++++++++ game/unitv.h | 20 +++++++++++++++ maths/basetypes.h | 4 +-- maths/vector2.h | 10 ++++++-- test/test.cpp | 38 +++++++++++++++++++++++++++ 9 files changed, 174 insertions(+), 74 deletions(-) create mode 100644 game/unit.h create mode 100644 game/unitv.cpp create mode 100644 game/unitv.h diff --git a/game/board.cpp b/game/board.cpp index 54b4a95..35f62ad 100644 --- a/game/board.cpp +++ b/game/board.cpp @@ -1,7 +1,5 @@ #include "board.h" -#include // std::cout - // ---------------------------------------------- // Default constructor for the board @@ -10,29 +8,22 @@ CBoard::CBoard( unsigned int c, unsigned int r ) , rows ( r ) , total ( rows * cols ) { - board = new square_t[total]; - fill(square_invalid); + board.resize(total,square_empty); } -CBoard::~CBoard() -{ - delete[] board; -} -// Clear the board -void CBoard::clear() +// constructor +CBoard::CBoard( unsigned int c, unsigned int r, vunit_t&& b ) +: cols ( c ) +, rows ( r ) +, total ( rows * cols ) +, board ( std::move(b) ) { - fill(square_empty); -} - -// Fill the board -void CBoard::fill(square_t v) -{ - std::fill(board,board+total,v); + board.resize(total,square_empty); } // print get a slot on the board -square_t CBoard::get( unsigned int c, unsigned int r ) const +unit_t CBoard::get( const unsigned int c, const unsigned int r ) const { if ( (r >= rows) || (c >= cols) ) return square_invalid; @@ -40,29 +31,13 @@ square_t CBoard::get( unsigned int c, unsigned int r ) const return board[r*c]; } -// print a board -void CBoard::debug_print() const +// Get a square on the board +unit_t CBoard::set( const unsigned int c, const unsigned int r , const unit_t n ) { - for ( unsigned int r = 0; r < rows; r++ ) - { - for ( unsigned int c = 0; c < cols; c++ ) - { - std::cout<<(char)board[r*c]; - } - std::cout<= rows) || (c >= cols) ) + return square_invalid; -// Test the board data class -void tests::test_CBoard() -{ - CBoard board = CBoard(20,10); - - std::cout<<"Blank board"< // std::numeric_limits +#include // std::vector + +typedef std::vector< unit_t > vunit_t; + +// Invalid value for the board square +constexpr unit_t square_invalid = std::numeric_limits::max(); +constexpr unit_t square_empty = 32; // 32 is ascii empty // Class to store simple data about a board class CBoard { public: - // Invalid value for the board square - static const square_t square_invalid = std::numeric_limits::max(); - static const square_t square_empty = 32; // 32 is ascii empty - - // Default constructor - CBoard( unsigned int c, unsigned int r ); - ~CBoard(); - - // Print the board - void debug_print() const; - - // clear the board - void clear(); - - // fill the board - void fill(square_t v); - - // Get a square on the board - square_t get( unsigned int c, unsigned int r ) const; - -private: - const unsigned int cols; // Number of columns const unsigned int rows; // Number of rows const unsigned int total; // Total number of pieces - square_t* board; // Board data storage + // constructor + CBoard( unsigned int c, unsigned int r ); + + // constructor + CBoard( unsigned int c, unsigned int r, vunit_t&& b ); + + // Default destructor + ~CBoard() = default; + + // clear the board + inline void clear() { fill(square_empty); } + + // fill the board + inline void fill(unit_t v) { std::fill(board.begin(),board.end(),v); }; + + // Get a square on the board + unit_t get( const unsigned int c, const unsigned int r ) const; + + // Get the full board + inline const vunit_t& get() const { return board; }; + + // Get a square on the board + unit_t set( const unsigned int c, const unsigned int r , const unit_t n ); + +private: + + vunit_t board; // Board data storage }; -// Namespace for testing functions -namespace tests -{ - void test_CBoard(); -}; - #endif //_BOARD_H_ \ No newline at end of file diff --git a/game/game.h b/game/game.h index fd6dbf3..8a4e184 100644 --- a/game/game.h +++ b/game/game.h @@ -1,4 +1,17 @@ #ifndef _GAME_H_ #define _GAME_H_ +#include "board.h" + +class CTTRTSGame +{ +public: + + CTTRTSGame(); + ~CTTRTSGame() = default; + +private: + +}; + #endif //_GAME_H_ \ No newline at end of file diff --git a/game/unit.h b/game/unit.h new file mode 100644 index 0000000..dca188d --- /dev/null +++ b/game/unit.h @@ -0,0 +1,26 @@ +#ifndef _UNIT_H_ +#define _UNIT_H_ + +#include + +#include "vector2.h" + +// Type for the unit type-id +typedef char unit_t; + +// Base unit type +template < unit_t unit_type > +class CUnit +{ +public: + + CUnit() = default; + virtual ~CUnit() = default; + +private: + + // All units must have position + uvector2 pos; +}; + +#endif //_UNIT_H_ \ No newline at end of file diff --git a/game/unitv.cpp b/game/unitv.cpp new file mode 100644 index 0000000..0a84da0 --- /dev/null +++ b/game/unitv.cpp @@ -0,0 +1,15 @@ +#include "unitv.h" + +// V unit +class CUnitV +: public CUnit<'V'> +{ +public: + CUnitV() = default; + virtual ~CUnitV() = default; + +private: + + // V also has a direction + char dir; +}; \ No newline at end of file diff --git a/game/unitv.h b/game/unitv.h new file mode 100644 index 0000000..65e3607 --- /dev/null +++ b/game/unitv.h @@ -0,0 +1,20 @@ +#ifndef _UNITV_H_ +#define _UNITV_H_ + +// V unit +class CUnitV +: public CUnit<'V'> +{ +public: + CUnitV() = default; + virtual ~CUnitV() = default; + + virtual std::string&& getDescriptor(); + +private: + + // V also has a direction + char dir; +}; + +#endif \ No newline at end of file diff --git a/maths/basetypes.h b/maths/basetypes.h index bacfeb1..004400b 100644 --- a/maths/basetypes.h +++ b/maths/basetypes.h @@ -1,7 +1,7 @@ #ifndef _BASETYPES_H_ #define _BASETYPES_H_ -// Type for the board square -typedef short square_t; +typedef short coord_t; +typedef unsigned short ucoord_t; #endif //_BASETYPES_H_ \ No newline at end of file diff --git a/maths/vector2.h b/maths/vector2.h index d375fee..446a68d 100644 --- a/maths/vector2.h +++ b/maths/vector2.h @@ -5,8 +5,14 @@ struct vector2 { - square_t x; - square_t y; + coord_t x; + coord_t y; +}; + +struct uvector2 +{ + ucoord_t x; + ucoord_t y; }; #endif //_VECTOR2_H_ \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp index e7b2a28..85142e8 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,5 +1,43 @@ #include "board.h" +#include // std::cout + + +// Namespace for testing functions +namespace tests +{ + // print a board + void debug_print( CBoard& b ) + { + for ( unsigned int r = 0; r < b.rows; r++ ) + { + for ( unsigned int c = 0; c < b.cols; c++ ) + { + std::cout<<(char)(b.get(c,r)); + } + std::cout< Date: Tue, 16 Dec 2014 13:12:54 +0000 Subject: [PATCH 012/190] Rename unit_t to unit_c to reflect it being a char --- game/board.cpp | 8 ++++---- game/board.h | 18 +++++++++--------- game/unit.h | 4 ++-- game/unitv.h | 2 -- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/game/board.cpp b/game/board.cpp index 35f62ad..6560498 100644 --- a/game/board.cpp +++ b/game/board.cpp @@ -13,7 +13,7 @@ CBoard::CBoard( unsigned int c, unsigned int r ) // constructor -CBoard::CBoard( unsigned int c, unsigned int r, vunit_t&& b ) +CBoard::CBoard( unsigned int c, unsigned int r, vunit_c&& b ) : cols ( c ) , rows ( r ) , total ( rows * cols ) @@ -23,7 +23,7 @@ CBoard::CBoard( unsigned int c, unsigned int r, vunit_t&& b ) } // print get a slot on the board -unit_t CBoard::get( const unsigned int c, const unsigned int r ) const +unit_c CBoard::get( const unsigned int c, const unsigned int r ) const { if ( (r >= rows) || (c >= cols) ) return square_invalid; @@ -32,12 +32,12 @@ unit_t CBoard::get( const unsigned int c, const unsigned int r ) const } // Get a square on the board -unit_t CBoard::set( const unsigned int c, const unsigned int r , const unit_t n ) +unit_c CBoard::set( const unsigned int c, const unsigned int r , const unit_c n ) { if ( (r >= rows) || (c >= cols) ) return square_invalid; - unit_t old = board[r*c]; + unit_c old = board[r*c]; board[r*c] = n; return old; } \ No newline at end of file diff --git a/game/board.h b/game/board.h index ac03270..53d2e50 100644 --- a/game/board.h +++ b/game/board.h @@ -7,11 +7,11 @@ #include // std::numeric_limits #include // std::vector -typedef std::vector< unit_t > vunit_t; +typedef std::vector< unit_c > vunit_c; // Invalid value for the board square -constexpr unit_t square_invalid = std::numeric_limits::max(); -constexpr unit_t square_empty = 32; // 32 is ascii empty +constexpr unit_c square_invalid = std::numeric_limits::max(); +constexpr unit_c square_empty = 32; // 32 is ascii empty // Class to store simple data about a board class CBoard @@ -26,7 +26,7 @@ public: CBoard( unsigned int c, unsigned int r ); // constructor - CBoard( unsigned int c, unsigned int r, vunit_t&& b ); + CBoard( unsigned int c, unsigned int r, vunit_c&& b ); // Default destructor ~CBoard() = default; @@ -35,20 +35,20 @@ public: inline void clear() { fill(square_empty); } // fill the board - inline void fill(unit_t v) { std::fill(board.begin(),board.end(),v); }; + inline void fill(unit_c v) { std::fill(board.begin(),board.end(),v); }; // Get a square on the board - unit_t get( const unsigned int c, const unsigned int r ) const; + unit_c get( const unsigned int c, const unsigned int r ) const; // Get the full board - inline const vunit_t& get() const { return board; }; + inline const vunit_c& get() const { return board; }; // Get a square on the board - unit_t set( const unsigned int c, const unsigned int r , const unit_t n ); + unit_c set( const unsigned int c, const unsigned int r , const unit_c n ); private: - vunit_t board; // Board data storage + vunit_c board; // Board data storage }; #endif //_BOARD_H_ \ No newline at end of file diff --git a/game/unit.h b/game/unit.h index dca188d..26b097b 100644 --- a/game/unit.h +++ b/game/unit.h @@ -6,10 +6,10 @@ #include "vector2.h" // Type for the unit type-id -typedef char unit_t; +typedef char unit_c; // Base unit type -template < unit_t unit_type > +template < unit_c unit_cype > class CUnit { public: diff --git a/game/unitv.h b/game/unitv.h index 65e3607..261f9aa 100644 --- a/game/unitv.h +++ b/game/unitv.h @@ -9,8 +9,6 @@ public: CUnitV() = default; virtual ~CUnitV() = default; - virtual std::string&& getDescriptor(); - private: // V also has a direction From 9fc5f33de862ce28a45882948f3394765d7f5185 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:55 +0000 Subject: [PATCH 013/190] Add more code to units to help distinguish between types and visual values --- game/CMakeLists.txt | 1 + game/board.cpp | 8 ++++---- game/board.h | 18 +++++++++--------- game/unit.h | 9 +++++++-- game/unitv.cpp | 35 +++++++++++++++++++++++++---------- game/unitv.h | 10 +++++++--- maths/basetypes.h | 8 ++++++++ test/test.cpp | 7 ++++++- 8 files changed, 67 insertions(+), 29 deletions(-) diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index e780c69..866c16c 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -14,6 +14,7 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) set( SOURCES game.cpp board.cpp + unitv.cpp ) add_library( game ${SOURCES} ) \ No newline at end of file diff --git a/game/board.cpp b/game/board.cpp index 6560498..3b257fa 100644 --- a/game/board.cpp +++ b/game/board.cpp @@ -13,7 +13,7 @@ CBoard::CBoard( unsigned int c, unsigned int r ) // constructor -CBoard::CBoard( unsigned int c, unsigned int r, vunit_c&& b ) +CBoard::CBoard( unsigned int c, unsigned int r, vunitType_c&& b ) : cols ( c ) , rows ( r ) , total ( rows * cols ) @@ -23,7 +23,7 @@ CBoard::CBoard( unsigned int c, unsigned int r, vunit_c&& b ) } // print get a slot on the board -unit_c CBoard::get( const unsigned int c, const unsigned int r ) const +unitType_c CBoard::get( const unsigned int c, const unsigned int r ) const { if ( (r >= rows) || (c >= cols) ) return square_invalid; @@ -32,12 +32,12 @@ unit_c CBoard::get( const unsigned int c, const unsigned int r ) const } // Get a square on the board -unit_c CBoard::set( const unsigned int c, const unsigned int r , const unit_c n ) +unitType_c CBoard::set( const unsigned int c, const unsigned int r , const unitType_c n ) { if ( (r >= rows) || (c >= cols) ) return square_invalid; - unit_c old = board[r*c]; + unitType_c old = board[r*c]; board[r*c] = n; return old; } \ No newline at end of file diff --git a/game/board.h b/game/board.h index 53d2e50..109d62b 100644 --- a/game/board.h +++ b/game/board.h @@ -7,11 +7,11 @@ #include // std::numeric_limits #include // std::vector -typedef std::vector< unit_c > vunit_c; +typedef std::vector< unitType_c > vunitType_c; // Invalid value for the board square -constexpr unit_c square_invalid = std::numeric_limits::max(); -constexpr unit_c square_empty = 32; // 32 is ascii empty +constexpr unitType_c square_invalid = std::numeric_limits::max(); +constexpr unitType_c square_empty = 32; // 32 is ascii empty // Class to store simple data about a board class CBoard @@ -26,7 +26,7 @@ public: CBoard( unsigned int c, unsigned int r ); // constructor - CBoard( unsigned int c, unsigned int r, vunit_c&& b ); + CBoard( unsigned int c, unsigned int r, vunitType_c&& b ); // Default destructor ~CBoard() = default; @@ -35,20 +35,20 @@ public: inline void clear() { fill(square_empty); } // fill the board - inline void fill(unit_c v) { std::fill(board.begin(),board.end(),v); }; + inline void fill(unitType_c v) { std::fill(board.begin(),board.end(),v); }; // Get a square on the board - unit_c get( const unsigned int c, const unsigned int r ) const; + unitType_c get( const unsigned int c, const unsigned int r ) const; // Get the full board - inline const vunit_c& get() const { return board; }; + inline const vunitType_c& get() const { return board; }; // Get a square on the board - unit_c set( const unsigned int c, const unsigned int r , const unit_c n ); + unitType_c set( const unsigned int c, const unsigned int r , const unitType_c n ); private: - vunit_c board; // Board data storage + vunitType_c board; // Board data storage }; #endif //_BOARD_H_ \ No newline at end of file diff --git a/game/unit.h b/game/unit.h index 26b097b..398adc3 100644 --- a/game/unit.h +++ b/game/unit.h @@ -6,10 +6,13 @@ #include "vector2.h" // Type for the unit type-id -typedef char unit_c; +typedef char unitType_c; + +// Typedef for unit visual representations +typedef char unitVis_c; // Base unit type -template < unit_c unit_cype > +template < unitType_c unit_type > class CUnit { public: @@ -17,6 +20,8 @@ public: CUnit() = default; virtual ~CUnit() = default; + virtual unitVis_c getVisual() const = 0; + private: // All units must have position diff --git a/game/unitv.cpp b/game/unitv.cpp index 0a84da0..752a60f 100644 --- a/game/unitv.cpp +++ b/game/unitv.cpp @@ -1,15 +1,30 @@ #include "unitv.h" -// V unit -class CUnitV -: public CUnit<'V'> +#include // for std::map + +CUnitV::CUnitV() +: dir(dir_t::S) { -public: - CUnitV() = default; - virtual ~CUnitV() = default; -private: +} - // V also has a direction - char dir; -}; \ No newline at end of file +unitVis_c CUnitV::getVisual() const +{ + // Map of visual representation of unitv + static const std::map< dir_t, unitVis_c > sk_visMap = + { + {dir_t::N,'^'}, + {dir_t::E,'>'}, + {dir_t::S,'v'}, + {dir_t::W,'<'}, + }; + + std::map< dir_t, char >::const_iterator it = sk_visMap.find(dir); + + if( it == sk_visMap.end() ) + { + return 0; + } + + return it->second; +} \ No newline at end of file diff --git a/game/unitv.h b/game/unitv.h index 261f9aa..baa0911 100644 --- a/game/unitv.h +++ b/game/unitv.h @@ -1,18 +1,22 @@ #ifndef _UNITV_H_ #define _UNITV_H_ +#include "unit.h" + // V unit class CUnitV : public CUnit<'V'> { public: - CUnitV() = default; + CUnitV(); virtual ~CUnitV() = default; + virtual unitVis_c getVisual() const; + private: // V also has a direction - char dir; + dir_t dir; }; -#endif \ No newline at end of file +#endif //_UNITV_H_ \ No newline at end of file diff --git a/maths/basetypes.h b/maths/basetypes.h index 004400b..a0d88c2 100644 --- a/maths/basetypes.h +++ b/maths/basetypes.h @@ -4,4 +4,12 @@ typedef short coord_t; typedef unsigned short ucoord_t; +enum dir_t : char +{ + N = 'N', + S = 'S', + E = 'E', + W = 'W', +}; + #endif //_BASETYPES_H_ \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp index 85142e8..082eb4b 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -2,6 +2,7 @@ #include // std::cout +#include "unitv.h" // Namespace for testing functions namespace tests @@ -19,7 +20,6 @@ namespace tests } } - // Test the board data class void test_CBoard() { @@ -42,4 +42,9 @@ namespace tests int main() { tests::test_CBoard(); + + std::cout<<"Testing units"< Date: Tue, 16 Dec 2014 13:12:55 +0000 Subject: [PATCH 014/190] Use char literal instead of int for a space --- game/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/board.h b/game/board.h index 109d62b..05232cb 100644 --- a/game/board.h +++ b/game/board.h @@ -11,7 +11,7 @@ typedef std::vector< unitType_c > vunitType_c; // Invalid value for the board square constexpr unitType_c square_invalid = std::numeric_limits::max(); -constexpr unitType_c square_empty = 32; // 32 is ascii empty +constexpr unitType_c square_empty = ' '; // Class to store simple data about a board class CBoard From 5ca0fc3e23d96c4409dc0d1575a7e0e3b6919ea5 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:55 +0000 Subject: [PATCH 015/190] Board should be using unit vis values not unit type values --- game/board.cpp | 8 ++++---- game/board.h | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/game/board.cpp b/game/board.cpp index 3b257fa..39a4aa5 100644 --- a/game/board.cpp +++ b/game/board.cpp @@ -13,7 +13,7 @@ CBoard::CBoard( unsigned int c, unsigned int r ) // constructor -CBoard::CBoard( unsigned int c, unsigned int r, vunitType_c&& b ) +CBoard::CBoard( unsigned int c, unsigned int r, vunitVis_c&& b ) : cols ( c ) , rows ( r ) , total ( rows * cols ) @@ -23,7 +23,7 @@ CBoard::CBoard( unsigned int c, unsigned int r, vunitType_c&& b ) } // print get a slot on the board -unitType_c CBoard::get( const unsigned int c, const unsigned int r ) const +unitVis_c CBoard::get( const unsigned int c, const unsigned int r ) const { if ( (r >= rows) || (c >= cols) ) return square_invalid; @@ -32,12 +32,12 @@ unitType_c CBoard::get( const unsigned int c, const unsigned int r ) const } // Get a square on the board -unitType_c CBoard::set( const unsigned int c, const unsigned int r , const unitType_c n ) +unitVis_c CBoard::set( const unsigned int c, const unsigned int r , const unitVis_c n ) { if ( (r >= rows) || (c >= cols) ) return square_invalid; - unitType_c old = board[r*c]; + unitVis_c old = board[r*c]; board[r*c] = n; return old; } \ No newline at end of file diff --git a/game/board.h b/game/board.h index 05232cb..ce7236d 100644 --- a/game/board.h +++ b/game/board.h @@ -7,11 +7,11 @@ #include // std::numeric_limits #include // std::vector -typedef std::vector< unitType_c > vunitType_c; +typedef std::vector< unitVis_c > vunitVis_c; // Invalid value for the board square -constexpr unitType_c square_invalid = std::numeric_limits::max(); -constexpr unitType_c square_empty = ' '; +constexpr unitVis_c square_invalid = std::numeric_limits::max(); +constexpr unitVis_c square_empty = ' '; // Class to store simple data about a board class CBoard @@ -26,7 +26,7 @@ public: CBoard( unsigned int c, unsigned int r ); // constructor - CBoard( unsigned int c, unsigned int r, vunitType_c&& b ); + CBoard( unsigned int c, unsigned int r, vunitVis_c&& b ); // Default destructor ~CBoard() = default; @@ -35,20 +35,20 @@ public: inline void clear() { fill(square_empty); } // fill the board - inline void fill(unitType_c v) { std::fill(board.begin(),board.end(),v); }; + inline void fill(unitVis_c v) { std::fill(board.begin(),board.end(),v); }; // Get a square on the board - unitType_c get( const unsigned int c, const unsigned int r ) const; + unitVis_c get( const unsigned int c, const unsigned int r ) const; // Get the full board - inline const vunitType_c& get() const { return board; }; + inline const vunitVis_c& get() const { return board; }; // Get a square on the board - unitType_c set( const unsigned int c, const unsigned int r , const unitType_c n ); + unitVis_c set( const unsigned int c, const unsigned int r , const unitVis_c n ); private: - vunitType_c board; // Board data storage + vunitVis_c board; // Board data storage }; #endif //_BOARD_H_ \ No newline at end of file From 8ade68101b36b2a261ed5e743c9f57f4356864d1 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:56 +0000 Subject: [PATCH 016/190] No need templates, templates bad, unneeded templates make c++god angry --- game/unit.h | 1 - game/unitv.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/game/unit.h b/game/unit.h index 398adc3..128e87d 100644 --- a/game/unit.h +++ b/game/unit.h @@ -12,7 +12,6 @@ typedef char unitType_c; typedef char unitVis_c; // Base unit type -template < unitType_c unit_type > class CUnit { public: diff --git a/game/unitv.h b/game/unitv.h index baa0911..1da4710 100644 --- a/game/unitv.h +++ b/game/unitv.h @@ -5,7 +5,7 @@ // V unit class CUnitV -: public CUnit<'V'> +: public CUnit { public: CUnitV(); From 7f639a6bf4717e8cb81a2e8d6a1b4ddef222ea12 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:56 +0000 Subject: [PATCH 017/190] Add new creation method from a visual representation --- game/CMakeLists.txt | 1 + game/unit.cpp | 26 ++++++++++++++++++++++++++ game/unit.h | 11 ++++++++--- game/unitv.cpp | 36 +++++++++++++++++++++++++++--------- game/unitv.h | 3 ++- test/test.cpp | 11 ++++++++--- 6 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 game/unit.cpp diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 866c16c..f9b861e 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -15,6 +15,7 @@ set( SOURCES game.cpp board.cpp unitv.cpp + unit.cpp ) add_library( game ${SOURCES} ) \ No newline at end of file diff --git a/game/unit.cpp b/game/unit.cpp new file mode 100644 index 0000000..14d517e --- /dev/null +++ b/game/unit.cpp @@ -0,0 +1,26 @@ +#include "unit.h" + +#include "unitv.h" + +#include + +std::unique_ptr CUnit::getUnitFromVis( unitVis_c vis ) +{ + switch( vis ) + { + case '^': + case '>': + case 'v': + case '<': + { + std::unique_ptr p = std::unique_ptr(new CUnitV); + if( (bool)p && p->setFromVisual(vis) ) + { + return std::move(p); + } + break; + } + } + + return std::move(std::unique_ptr(nullptr)); +} \ No newline at end of file diff --git a/game/unit.h b/game/unit.h index 128e87d..e71a96f 100644 --- a/game/unit.h +++ b/game/unit.h @@ -2,6 +2,7 @@ #define _UNIT_H_ #include +#include #include "vector2.h" @@ -15,11 +16,15 @@ typedef char unitVis_c; class CUnit { public: - - CUnit() = default; virtual ~CUnit() = default; - virtual unitVis_c getVisual() const = 0; + virtual unitVis_c getVisual() const = 0; + virtual bool setFromVisual(unitVis_c& vis) = 0; + + static std::unique_ptr getUnitFromVis( unitVis_c vis ); + +protected: + CUnit() = default; private: diff --git a/game/unitv.cpp b/game/unitv.cpp index 752a60f..92818c3 100644 --- a/game/unitv.cpp +++ b/game/unitv.cpp @@ -8,17 +8,18 @@ CUnitV::CUnitV() } + +// Map of visual representation of unitv +static const std::map< dir_t, unitVis_c > sk_visMap = +{ + {dir_t::N,'^'}, + {dir_t::E,'>'}, + {dir_t::S,'v'}, + {dir_t::W,'<'}, +}; + unitVis_c CUnitV::getVisual() const { - // Map of visual representation of unitv - static const std::map< dir_t, unitVis_c > sk_visMap = - { - {dir_t::N,'^'}, - {dir_t::E,'>'}, - {dir_t::S,'v'}, - {dir_t::W,'<'}, - }; - std::map< dir_t, char >::const_iterator it = sk_visMap.find(dir); if( it == sk_visMap.end() ) @@ -27,4 +28,21 @@ unitVis_c CUnitV::getVisual() const } return it->second; +} + +bool CUnitV::setFromVisual( unitVis_c& vis ) +{ + std::map< dir_t, char >::const_iterator it; + + for( it = sk_visMap.begin(); it != sk_visMap.end(); it++ ) + { + if( it->second == vis ) + { + dir == it->first; + return true; + } + } + + // No matching direction to visual + return false; } \ No newline at end of file diff --git a/game/unitv.h b/game/unitv.h index 1da4710..2f04dc7 100644 --- a/game/unitv.h +++ b/game/unitv.h @@ -11,7 +11,8 @@ public: CUnitV(); virtual ~CUnitV() = default; - virtual unitVis_c getVisual() const; + virtual unitVis_c getVisual() const override; + virtual bool setFromVisual( unitVis_c& vis ) override; private: diff --git a/test/test.cpp b/test/test.cpp index 082eb4b..4e17826 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -43,8 +43,13 @@ int main() { tests::test_CBoard(); - std::cout<<"Testing units"< myV = CUnit::getUnitFromVis('v'); + std::cout<getVisual()< Date: Tue, 16 Dec 2014 13:12:56 +0000 Subject: [PATCH 018/190] Actually build with debug symbols in debug configuration --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9eb2f49..9d94aa3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,11 @@ cmake_minimum_required( VERSION 2.8.7 ) +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) + +if( CMAKE_BUILD_TYPE MATCHES "Debug" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g" ) +endif() + # Subprojects add_subdirectory( ttrts ) add_subdirectory( game ) From 00579863804401a242c032d945d042d9f1009974 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:57 +0000 Subject: [PATCH 019/190] header game types --- game/gametypes.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 game/gametypes.h diff --git a/game/gametypes.h b/game/gametypes.h new file mode 100644 index 0000000..fe2deb9 --- /dev/null +++ b/game/gametypes.h @@ -0,0 +1,13 @@ +#ifndef _GAME_TYPES_H_ +#define _GAME_TYPES_H_ + +// Type for a team IDs +typedef unsigned short team_id_t; + +// Type for player IDs +typedef unsigned short player_id_t; + +// Type for unit IDs +typedef unsigned short unit_id_t; + +#endif //_GAME_TYPES_H_ \ No newline at end of file From 0e6c85229c48a9a7071ff1a57b7ab720fe3aff6a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:57 +0000 Subject: [PATCH 020/190] Add orders files and COrder class to store orders --- game/CMakeLists.txt | 1 + game/orders.cpp | 34 ++++++++++++++++++++++++++++++++++ game/orders.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 game/orders.cpp create mode 100644 game/orders.h diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index f9b861e..13b4d89 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -16,6 +16,7 @@ set( SOURCES board.cpp unitv.cpp unit.cpp + orders.cpp ) add_library( game ${SOURCES} ) \ No newline at end of file diff --git a/game/orders.cpp b/game/orders.cpp new file mode 100644 index 0000000..4cae3d2 --- /dev/null +++ b/game/orders.cpp @@ -0,0 +1,34 @@ +#include "orders.h" + +// Convert an order to a string +std::string GetStringFromOrder( COrder& order ) +{ + std::string ret; + ret += std::to_string(order.unit); + ret += ORDER_DELIMITER; + ret += order.order; + + return ret; +} + +// Convert a string to an order +COrder GetOrderFromString( std::string order ) +{ + COrder ret; + + int pos = order.find(ORDER_DELIMITER); + if( pos != std::string::npos ) + { + const std::string order_unit = order.substr(0, pos); + + ret.unit = atoi( order_unit.c_str() ); + + // Erase everything up to and including the delimiter + order.erase(0, pos + 1); + + // Next single char is the order + ret.order = order[0]; + } + + return ret; +} \ No newline at end of file diff --git a/game/orders.h b/game/orders.h new file mode 100644 index 0000000..d737935 --- /dev/null +++ b/game/orders.h @@ -0,0 +1,42 @@ +#ifndef _ORDERS_H_ +#define _ORDERS_H_ + +#include +#include + +#include "gametypes.h" + +#define ORDER_DELIMITER ' ' + +// Type for all orders ( as a char ) +typedef char order_c; + +// Container for an order +struct COrder +{ + // Unit order is for + unit_id_t unit; + + // Order command issued + order_c order; + + inline bool operator==( const COrder& rhs ) const; + inline bool operator!=( const COrder& rhs ) const { return !(*this==rhs); } +}; + +inline bool COrder::operator== ( const COrder& rhs ) const +{ + return ( unit == rhs.unit ) && ( order == rhs.order ); +} + +// Typedef a vector of orders +typedef std::vector COrderVector; + +// Order strings stored as simply "[unit id] [order char]" + +// string <--> order conversion functions +std::string GetStringFromOrder( COrder& order ); +COrder GetOrderFromString( std::string order ); + + +#endif //_ORDERS_H_ \ No newline at end of file From 6a17e4e4c199b8b46e71ac141fb9625408a0635d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:57 +0000 Subject: [PATCH 021/190] Add tests for COrders --- test/test.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/test.cpp b/test/test.cpp index 4e17826..c6e9272 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,8 +1,9 @@ -#include "board.h" - #include // std::cout #include "unitv.h" +#include "board.h" +#include "orders.h" + // Namespace for testing functions namespace tests @@ -44,12 +45,28 @@ int main() tests::test_CBoard(); { + std::cout<<"Basic V unit construction"< myV = CUnit::getUnitFromVis('v'); - std::cout<getVisual()<getVisual() != 'v' ) + std::cout<<"ERROR, failed to properly create V unit"< Date: Tue, 16 Dec 2014 13:12:58 +0000 Subject: [PATCH 022/190] lots of initial design for the game, and unit data --- game/game.cpp | 7 +++++++ game/game.h | 33 +++++++++++++++++++++++++++++++++ game/orders.h | 4 ++-- game/unit.cpp | 6 ++++-- game/unit.h | 10 ++++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 7bb61ba..b014a7b 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1 +1,8 @@ #include "game.h" + + +// Initialise the game with default configuration +void CTTRTSGame::Initialise() +{ + +} \ No newline at end of file diff --git a/game/game.h b/game/game.h index 8a4e184..a14b74f 100644 --- a/game/game.h +++ b/game/game.h @@ -2,6 +2,12 @@ #define _GAME_H_ #include "board.h" +#include "gametypes.h" +#include "orders.h" + +#include // unique_ptr and shared_ptr + +typedef std::vector< std::shared_ptr > sharedUnitVector_t; class CTTRTSGame { @@ -10,8 +16,35 @@ public: CTTRTSGame(); ~CTTRTSGame() = default; + // Initialise the game with default configuration + void Initialise(); + + // Issue orders to the game + bool IssueOrders( std::string orders ); + bool IssueOrders( COrderVector orders ); + + // Simulate and progress to the next turn + bool NextTurn(); + + // Get the number of units + inline unsigned int GetNumUnits() const { return m_orders.size(); } + + // Get unit by index as above (not unit ID) + inline const CUnit& GetUnitByIndex( unsigned int i ) const { return *m_orders[i]; } + private: + // Simulate all movements + bool SimulateMovements(); + + // Simulate all actions + bool SimulateActions(); + + // Vector to store points to all units + sharedUnitVector_t m_allUnits; + + // Orders to execute this turn + COrderVector m_orders; }; #endif //_GAME_H_ \ No newline at end of file diff --git a/game/orders.h b/game/orders.h index d737935..e90d3c7 100644 --- a/game/orders.h +++ b/game/orders.h @@ -20,10 +20,12 @@ struct COrder // Order command issued order_c order; + // Basic operators inline bool operator==( const COrder& rhs ) const; inline bool operator!=( const COrder& rhs ) const { return !(*this==rhs); } }; +// Simple == operator inline bool COrder::operator== ( const COrder& rhs ) const { return ( unit == rhs.unit ) && ( order == rhs.order ); @@ -33,10 +35,8 @@ inline bool COrder::operator== ( const COrder& rhs ) const typedef std::vector COrderVector; // Order strings stored as simply "[unit id] [order char]" - // string <--> order conversion functions std::string GetStringFromOrder( COrder& order ); COrder GetOrderFromString( std::string order ); - #endif //_ORDERS_H_ \ No newline at end of file diff --git a/game/unit.cpp b/game/unit.cpp index 14d517e..517c239 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -1,18 +1,19 @@ #include "unit.h" +// Unit types #include "unitv.h" -#include - std::unique_ptr CUnit::getUnitFromVis( unitVis_c vis ) { switch( vis ) { + // Match with any image for a V case '^': case '>': case 'v': case '<': { + // Create a V std::unique_ptr p = std::unique_ptr(new CUnitV); if( (bool)p && p->setFromVisual(vis) ) { @@ -22,5 +23,6 @@ std::unique_ptr CUnit::getUnitFromVis( unitVis_c vis ) } } + // No unit found, return nullptr return std::move(std::unique_ptr(nullptr)); } \ No newline at end of file diff --git a/game/unit.h b/game/unit.h index e71a96f..585c6c7 100644 --- a/game/unit.h +++ b/game/unit.h @@ -4,6 +4,7 @@ #include #include +#include "gametypes.h" #include "vector2.h" // Type for the unit type-id @@ -28,6 +29,15 @@ protected: private: + // Unit ID + unit_id_t id; + + // Team ID + team_id_t team_id; + + // Owner ID + player_id_t owner_id; + // All units must have position uvector2 pos; }; From 2abd4ad83270d78052adec39bb048667f2b57443 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:58 +0000 Subject: [PATCH 023/190] Move the board code out to ui as it's only for board visualisation --- CMakeLists.txt | 1 + game/CMakeLists.txt | 1 - game/game.h | 14 ++++++++++---- game/gametypes.h | 6 ++++++ game/unit.h | 6 ------ test/CMakeLists.txt | 3 ++- ui/CMakeLists.txt | 19 +++++++++++++++++++ {game => ui}/board.cpp | 0 {game => ui}/board.h | 2 +- 9 files changed, 39 insertions(+), 13 deletions(-) rename {game => ui}/board.cpp (100%) rename {game => ui}/board.h (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d94aa3..014ed35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,3 +10,4 @@ endif() add_subdirectory( ttrts ) add_subdirectory( game ) add_subdirectory( test ) +add_subdirectory( ui ) diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 13b4d89..935bb02 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -13,7 +13,6 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) # Add the sources set( SOURCES game.cpp - board.cpp unitv.cpp unit.cpp orders.cpp diff --git a/game/game.h b/game/game.h index a14b74f..81b51fc 100644 --- a/game/game.h +++ b/game/game.h @@ -1,7 +1,7 @@ #ifndef _GAME_H_ #define _GAME_H_ -#include "board.h" +#include "unit.h" #include "gametypes.h" #include "orders.h" @@ -27,10 +27,16 @@ public: bool NextTurn(); // Get the number of units - inline unsigned int GetNumUnits() const { return m_orders.size(); } + inline unsigned int GetNumUnits() const { return m_allUnits.size(); } // Get unit by index as above (not unit ID) - inline const CUnit& GetUnitByIndex( unsigned int i ) const { return *m_orders[i]; } + inline const CUnit& GetUnitByIndex( unsigned int i ) const { return *m_allUnits[i]; } + + // Get the number of order + inline unsigned int GetNumOrders() const { return m_orders.size(); } + + // Get orders by index as above + inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_orders[i]; } private: @@ -39,7 +45,7 @@ private: // Simulate all actions bool SimulateActions(); - + // Vector to store points to all units sharedUnitVector_t m_allUnits; diff --git a/game/gametypes.h b/game/gametypes.h index fe2deb9..47dd9ab 100644 --- a/game/gametypes.h +++ b/game/gametypes.h @@ -10,4 +10,10 @@ typedef unsigned short player_id_t; // Type for unit IDs typedef unsigned short unit_id_t; +// Type for the unit type-id +typedef char unitType_c; + +// Typedef for unit visual representations +typedef char unitVis_c; + #endif //_GAME_TYPES_H_ \ No newline at end of file diff --git a/game/unit.h b/game/unit.h index 585c6c7..4fc71b7 100644 --- a/game/unit.h +++ b/game/unit.h @@ -7,12 +7,6 @@ #include "gametypes.h" #include "vector2.h" -// Type for the unit type-id -typedef char unitType_c; - -// Typedef for unit visual representations -typedef char unitVis_c; - // Base unit type class CUnit { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ceca484..6ef211e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,7 @@ project( ttrts-test ) include_directories( ../game ../maths + ../ui ) set( SOURCES @@ -18,4 +19,4 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) # Add the executable add_executable( ttrts-test ${SOURCES} ) -target_link_libraries( ttrts-test game ) \ No newline at end of file +target_link_libraries( ttrts-test game ui ) \ No newline at end of file diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index e69de29..8b508ff 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 2.8.7) + +# game project +project( ui ) + +include_directories( + ../maths + ../game +) + +# Set to use c++11, because we're cool like that +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) + +# Add the sources +set( SOURCES + board.cpp +) + +add_library( ui ${SOURCES} ) \ No newline at end of file diff --git a/game/board.cpp b/ui/board.cpp similarity index 100% rename from game/board.cpp rename to ui/board.cpp diff --git a/game/board.h b/ui/board.h similarity index 96% rename from game/board.h rename to ui/board.h index ce7236d..d36ab3a 100644 --- a/game/board.h +++ b/ui/board.h @@ -1,8 +1,8 @@ #ifndef _BOARD_H_ #define _BOARD_H_ +#include "gametypes.h" #include "basetypes.h" -#include "unit.h" #include // std::numeric_limits #include // std::vector From 108be3035fad323b4dd2106befaa28fd29553570 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:58 +0000 Subject: [PATCH 024/190] Final bit of design doe the game class, should be enough of a final interface to get started on implementation --- game/game.cpp | 7 ------- game/game.h | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index b014a7b..7bb61ba 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1,8 +1 @@ #include "game.h" - - -// Initialise the game with default configuration -void CTTRTSGame::Initialise() -{ - -} \ No newline at end of file diff --git a/game/game.h b/game/game.h index 81b51fc..c639b5c 100644 --- a/game/game.h +++ b/game/game.h @@ -13,18 +13,29 @@ class CTTRTSGame { public: - CTTRTSGame(); + CTTRTSGame( ucoord_t c, ucoord_t r ) + : cols (c) + , rows (r) + { + + } + + // Default dtor ~CTTRTSGame() = default; - // Initialise the game with default configuration - void Initialise(); - - // Issue orders to the game - bool IssueOrders( std::string orders ); - bool IssueOrders( COrderVector orders ); + // Issue orders to the game, returns non-zero if orders are incorrect + int IssueOrders( player_id_t player, std::string orders ); + int IssueOrders( player_id_t player, COrderVector orders ); // Simulate and progress to the next turn - bool NextTurn(); + // Returns non-zero if simulation failed + int SimulateToNextTurn(); + + // Add a unit, nonzero return value indicates error + int AddUnit( std::shared_ptr unit ); + + // Add a units, nonzero return value indicates error + int AddUnits( sharedUnitVector_t units ); // Get the number of units inline unsigned int GetNumUnits() const { return m_allUnits.size(); } @@ -46,11 +57,18 @@ private: // Simulate all actions bool SimulateActions(); + // Verify any order + bool VerifyOrder( player_id_t player, COrder& order ); + // Vector to store points to all units - sharedUnitVector_t m_allUnits; + sharedUnitVector_t m_allUnits; // Orders to execute this turn - COrderVector m_orders; + COrderVector m_orders; + + // Dimensions of the game + ucoord_t rows; + ucoord_t cols; }; #endif //_GAME_H_ \ No newline at end of file From e9308b839c997e3a47bd27dbd9985cf637afb453 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:59 +0000 Subject: [PATCH 025/190] Rename basetypes to mathtypes --- maths/{basetypes.h => mathtypes.h} | 2 ++ maths/vector2.h | 2 +- ui/board.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) rename maths/{basetypes.h => mathtypes.h} (70%) diff --git a/maths/basetypes.h b/maths/mathtypes.h similarity index 70% rename from maths/basetypes.h rename to maths/mathtypes.h index a0d88c2..8dfdd5f 100644 --- a/maths/basetypes.h +++ b/maths/mathtypes.h @@ -1,9 +1,11 @@ #ifndef _BASETYPES_H_ #define _BASETYPES_H_ +// Coordinate types typedef short coord_t; typedef unsigned short ucoord_t; +// Direction representation enum dir_t : char { N = 'N', diff --git a/maths/vector2.h b/maths/vector2.h index 446a68d..fe00dec 100644 --- a/maths/vector2.h +++ b/maths/vector2.h @@ -1,7 +1,7 @@ #ifndef _VECTOR2_H_ #define _VECTOR2_H_ -#include "basetypes.h" +#include "mathtypes.h" struct vector2 { diff --git a/ui/board.h b/ui/board.h index d36ab3a..38703a4 100644 --- a/ui/board.h +++ b/ui/board.h @@ -2,7 +2,7 @@ #define _BOARD_H_ #include "gametypes.h" -#include "basetypes.h" +#include "mathtypes.h" #include // std::numeric_limits #include // std::vector From 270036c92510c896e7082430015d5f325802b86c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:12:59 +0000 Subject: [PATCH 026/190] Add stubs for game --- game/game.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ game/game.h | 12 +++--- game/unit.h | 2 + 3 files changed, 117 insertions(+), 5 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 7bb61ba..fc72ef1 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1 +1,109 @@ #include "game.h" + + +// Interpret a string of orders +int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) +{ + COrderVector orderVector; + std::string orders = _orders; + + size_t pos; + while ( (pos = orders.find("\n")) != std::string::npos ) + { + const std::string sorder = orders.substr(0, pos); + orders.erase(0,pos+1); + + COrder order = GetOrderFromString( sorder ); + + orderVector.push_back(order); + } + + return IssueOrders(player,orderVector); +} + +// Issue orders by vector to the game +int CTTRTSGame::IssueOrders( player_id_t player, const COrderVector& orders ) +{ + // verify all the orders + for ( COrderVector::const_iterator it = orders.begin(); it != orders.end(); it++ ) + { + if ( IssueOrder(player,*it) ) + return 1; + } + + return 0; +} + +// Issue a single order +int CTTRTSGame::IssueOrder( player_id_t player, const COrder& order ) +{ + if ( VerifyOrder(player,order) ) + return 1; + + m_orders.push_back(order); + + return 0; +} + +// Simulate and progress to the next turn +// Returns non-zero if simulation failed +int CTTRTSGame::SimulateToNextTurn() +{ + int error; + + // Simulate all movements first + error = SimulateMovements(); + + // Simulate all the actions + error = SimulateActions(); + + // Clear all orders + m_orders.resize(0); + + return error; +} + +// Add a unit, nonzero return value indicates error +int CTTRTSGame::AddUnit( std::shared_ptr unit ) +{ + return 0; +} + +// Add a units, nonzero return value indicates error +int CTTRTSGame::AddUnits( sharedUnitVector_t units ) +{ + return 0; +} + +// Simulate all movements +int CTTRTSGame::SimulateMovements() +{ + return 0; +} + +// Simulate all actions +int CTTRTSGame::SimulateActions() +{ + return 0; +} + +// Verify any order +int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) +{ + // Grab the unit ID + const unit_id_t unitID = order.unit; + + // Attempt to find the unit + bool unitFound = false; + for ( sharedUnitVector_t::const_iterator it = m_allUnits.begin(); it != m_allUnits.end(); it++ ) + { + if ( (*it)->getID() == unitID ) + { + unitFound = true; + break; + } + } + + // for now, as long as the unit exists we can attempt the order + return unitFound; +} \ No newline at end of file diff --git a/game/game.h b/game/game.h index c639b5c..0c40383 100644 --- a/game/game.h +++ b/game/game.h @@ -24,8 +24,10 @@ public: ~CTTRTSGame() = default; // Issue orders to the game, returns non-zero if orders are incorrect - int IssueOrders( player_id_t player, std::string orders ); - int IssueOrders( player_id_t player, COrderVector orders ); + int IssueOrders( player_id_t player, const std::string& orders ); + int IssueOrders( player_id_t player, const COrderVector& orders ); + + int IssueOrder( player_id_t player, const COrder& order ); // Simulate and progress to the next turn // Returns non-zero if simulation failed @@ -52,13 +54,13 @@ public: private: // Simulate all movements - bool SimulateMovements(); + int SimulateMovements(); // Simulate all actions - bool SimulateActions(); + int SimulateActions(); // Verify any order - bool VerifyOrder( player_id_t player, COrder& order ); + int VerifyOrder( player_id_t player, const COrder& order ); // Vector to store points to all units sharedUnitVector_t m_allUnits; diff --git a/game/unit.h b/game/unit.h index 4fc71b7..98d3c04 100644 --- a/game/unit.h +++ b/game/unit.h @@ -18,6 +18,8 @@ public: static std::unique_ptr getUnitFromVis( unitVis_c vis ); + inline unit_id_t getID() const { return id; } + protected: CUnit() = default; From da1cb325832e0ecf9d5a0079c860eb0d16382294 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:00 +0000 Subject: [PATCH 027/190] Update README.md with info on ttrts-server --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a7206fb..da359ed 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,15 @@ We aim to create a simple terminal based rts where a user can program an AI to c ------------------------------------------------------------------------------------ ## Gameplay -1. TTRTS clients are run from the command line -2. clients will connect and confirm initial board state +1. ttrts clients are run from the command line +2. ttrts-server is launched from the command line +3. clients will connect to server and confirm initial board state 1. clients output a text file with game data for this turn 2. a player, or program, reads the game data file and outputs an instructions file 3. clients read the instructions file, simulates the turn - 4. game state is verified between clients and output + 4. game state is verified between clients and server 5. repeat until an end state is reached -3. once game is finished, host and clients disconnect and a winner is notified +4. once game is finished, host and clients disconnect and a winner is notified *see [the game directory](game) for full game rules* @@ -24,14 +25,16 @@ We aim to create a simple terminal based rts where a user can program an AI to c ### Targets ##### ttrts -Main TTRTS executable , runs from the command line and can act as host or client +Main TTRTS executable , runs from the command line and acts as client -##### ttrts-test -Test executable, to be compiled and run to test various functionality +##### ttrts-server +TTRTS server executable, runs from the command line acting as server ##### player Custom player AI code, this should contain examples and test code to help newcomers begin their journey +##### ttrts-test +Test executable, to be compiled and run to test various functionality ### Libraries ##### game From a6e1319fad0db97b3bde5c0cf2c9d4fc1712a397 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:00 +0000 Subject: [PATCH 028/190] Add invalid coord variables --- game/gametypes.h | 9 +++++++++ maths/mathtypes.h | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/game/gametypes.h b/game/gametypes.h index 47dd9ab..903ac9a 100644 --- a/game/gametypes.h +++ b/game/gametypes.h @@ -1,6 +1,8 @@ #ifndef _GAME_TYPES_H_ #define _GAME_TYPES_H_ +#include // std::numeric_limits + // Type for a team IDs typedef unsigned short team_id_t; @@ -16,4 +18,11 @@ typedef char unitType_c; // Typedef for unit visual representations typedef char unitVis_c; +// Invalid data for above types +static const team_id_t team_id_invalid = std::numeric_limits::max(); +static const player_id_t player_id_invalid = std::numeric_limits::max(); +static const unit_id_t unit_id_invalid = std::numeric_limits::max(); +static const unitType_c unitType_invalid = std::numeric_limits::max(); +static const unitVis_c unitVis_invalid = std::numeric_limits::max(); + #endif //_GAME_TYPES_H_ \ No newline at end of file diff --git a/maths/mathtypes.h b/maths/mathtypes.h index 8dfdd5f..ab1e0eb 100644 --- a/maths/mathtypes.h +++ b/maths/mathtypes.h @@ -1,10 +1,16 @@ #ifndef _BASETYPES_H_ #define _BASETYPES_H_ +#include // std::numeric_limits + // Coordinate types typedef short coord_t; typedef unsigned short ucoord_t; +// Invalid values +static const coord_t coord_invalid = std::numeric_limits::max(); +static const ucoord_t ucoord_invalid = std::numeric_limits::max(); + // Direction representation enum dir_t : char { From 008739dee6e0d9a86314d71f29b91f6c4ec079e2 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:00 +0000 Subject: [PATCH 029/190] Various work towards a basic game implementation Get rid of some virtual functions that weren't needed. Fix some functions to work during static init if need be. Units now have unique incremental IDs --- game/game.cpp | 49 +++++++++++++++++++++++++++++++++------------- game/game.h | 3 +++ game/unit.cpp | 19 ++++++++++++++++++ game/unit.h | 44 +++++++++++++++++++++++++++++++++++------ game/unitv.cpp | 53 ++++++++++++++++++++++++++++++++------------------ game/unitv.h | 7 +++++-- 6 files changed, 135 insertions(+), 40 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index fc72ef1..1ebb6d8 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -65,25 +65,36 @@ int CTTRTSGame::SimulateToNextTurn() // Add a unit, nonzero return value indicates error int CTTRTSGame::AddUnit( std::shared_ptr unit ) -{ +{ + // Verify the unit + const int val = unit->valid(); + if( val ) + return val; + + // Verify if the unit can be placed on the current board + const uvector2 pos = unit->getPos(); + if( (pos.x < cols) && (pos.y < rows) ) + return 1; + + m_allUnits.push_back(unit); + + return 0; } // Add a units, nonzero return value indicates error int CTTRTSGame::AddUnits( sharedUnitVector_t units ) -{ - return 0; -} +{ + sharedUnitVector_t::iterator it; -// Simulate all movements -int CTTRTSGame::SimulateMovements() -{ - return 0; -} + for ( it = units.begin(); it != units.end(); it++ ) + { + // Attempt the unit add + if ( AddUnit(*it) ) + return 1; + } -// Simulate all actions -int CTTRTSGame::SimulateActions() -{ + // All units added successfully return 0; } @@ -106,4 +117,16 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) // for now, as long as the unit exists we can attempt the order return unitFound; -} \ No newline at end of file +} + +// Simulate all movements +int CTTRTSGame::SimulateMovements() +{ + return 0; +} + +// Simulate all actions +int CTTRTSGame::SimulateActions() +{ + return 0; +} diff --git a/game/game.h b/game/game.h index 0c40383..b4d0857 100644 --- a/game/game.h +++ b/game/game.h @@ -62,6 +62,9 @@ private: // Verify any order int VerifyOrder( player_id_t player, const COrder& order ); + // Verify any order + int VerifyUnit( const CUnit& unit ); + // Vector to store points to all units sharedUnitVector_t m_allUnits; diff --git a/game/unit.cpp b/game/unit.cpp index 517c239..524941c 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -3,6 +3,25 @@ // Unit types #include "unitv.h" +namespace +{ + // Helper function for generating unique unit ids during static init + unit_id_t get_unique_unit_id() + { + static unit_id_t p = 0; + return p++; + } +} + +CUnit::CUnit() +: unit_id ( get_unique_unit_id() ) +, team_id ( team_id_invalid ) +, player_id ( player_id_invalid ) +, unit_vis ( unitVis_invalid ) +{ + +}; + std::unique_ptr CUnit::getUnitFromVis( unitVis_c vis ) { switch( vis ) diff --git a/game/unit.h b/game/unit.h index 98d3c04..51764af 100644 --- a/game/unit.h +++ b/game/unit.h @@ -13,29 +13,61 @@ class CUnit public: virtual ~CUnit() = default; - virtual unitVis_c getVisual() const = 0; + + inline const unit_id_t& getID() const { return unit_id; } + inline const team_id_t& getTeam() const { return team_id; } + inline const player_id_t& getPlayer() const { return player_id; } + inline const unitVis_c& getVisual() const { return unit_vis; } + + // Return non-zero values on error + inline int setTeam(const team_id_t& v) { return (v == team_id_invalid) ? -1 : (( team_id = v ), 0); } + inline int setPlayer(const player_id_t& v) { return (v == player_id_invalid) ? -1 : (( player_id = v ), 0); } + inline int setVisual(const unitVis_c& v) { return (v == unitVis_invalid) ? -1 : (( unit_vis = v ), 0); } + + inline const uvector2& getPos() const { return pos; } + inline void setPos(const uvector2& v) { pos = v; } + + // Check unit is valid + inline bool valid() const; + + // Set a unit based solely on it's visual + // Maybe make non-virtual at some point to avoid vtable lookups virtual bool setFromVisual(unitVis_c& vis) = 0; + // Factory function for creating units from a visual static std::unique_ptr getUnitFromVis( unitVis_c vis ); - inline unit_id_t getID() const { return id; } - protected: - CUnit() = default; + + // Protected constructor, cannot be constructed as base type + CUnit(); private: // Unit ID - unit_id_t id; + const unit_id_t unit_id; + + // Visual + unitVis_c unit_vis; // Team ID team_id_t team_id; // Owner ID - player_id_t owner_id; + player_id_t player_id; + // All units must have position uvector2 pos; }; +// Simple validation +inline bool CUnit::valid() const +{ + return (unit_id != unit_id_invalid ) + && (team_id != team_id_invalid ) + && (player_id != player_id_invalid) + && (unit_vis != unitVis_invalid); +} + #endif //_UNIT_H_ \ No newline at end of file diff --git a/game/unitv.cpp b/game/unitv.cpp index 92818c3..39f0d95 100644 --- a/game/unitv.cpp +++ b/game/unitv.cpp @@ -5,40 +5,55 @@ CUnitV::CUnitV() : dir(dir_t::S) { - + updateMyVisual(); } // Map of visual representation of unitv -static const std::map< dir_t, unitVis_c > sk_visMap = -{ - {dir_t::N,'^'}, - {dir_t::E,'>'}, - {dir_t::S,'v'}, - {dir_t::W,'<'}, -}; - -unitVis_c CUnitV::getVisual() const -{ - std::map< dir_t, char >::const_iterator it = sk_visMap.find(dir); - - if( it == sk_visMap.end() ) +namespace +{ + typedef std::map< dir_t, unitVis_c > dir_to_vis_map; + + // Helper function to get the vis map during static init + const dir_to_vis_map& get_vis_map() { - return 0; - } + static const dir_to_vis_map sk_visMap = + { + {dir_t::N,'^'}, + {dir_t::E,'>'}, + {dir_t::S,'v'}, + {dir_t::W,'<'}, + }; - return it->second; + return sk_visMap; + } +} + +// Update the visual representation of the unit +unitVis_c CUnitV::updateMyVisual() +{ + // Start at invalid + setVisual(unitVis_invalid); + + dir_to_vis_map::const_iterator it = get_vis_map().find(dir); + + // If found set to new vis + if( it != get_vis_map().end() ) + setVisual(it->second); + + return getVisual(); } bool CUnitV::setFromVisual( unitVis_c& vis ) { - std::map< dir_t, char >::const_iterator it; + dir_to_vis_map::const_iterator it; - for( it = sk_visMap.begin(); it != sk_visMap.end(); it++ ) + for( it = get_vis_map().begin(); it != get_vis_map().end(); it++ ) { if( it->second == vis ) { dir == it->first; + updateMyVisual(); return true; } } diff --git a/game/unitv.h b/game/unitv.h index 2f04dc7..a1cc463 100644 --- a/game/unitv.h +++ b/game/unitv.h @@ -11,11 +11,14 @@ public: CUnitV(); virtual ~CUnitV() = default; - virtual unitVis_c getVisual() const override; - virtual bool setFromVisual( unitVis_c& vis ) override; + // Set from a visual + virtual bool setFromVisual( unitVis_c& vis ) override; private: + // Update the visual of V + unitVis_c updateMyVisual(); + // V also has a direction dir_t dir; }; From 72b35dbc067d5d90225184849efb8dd3f17acb5e Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:00 +0000 Subject: [PATCH 030/190] Framework for movement and action orders, plus some better organisation of the test code --- game/game.cpp | 20 +++++++++++ game/orders.cpp | 29 ++++++++++++++-- game/orders.h | 21 ++++++++++-- maths/mathtypes.h | 5 +++ test/test.cpp | 84 +++++++++++++++++++++++++---------------------- 5 files changed, 115 insertions(+), 44 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 1ebb6d8..b52b671 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -122,11 +122,31 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) // Simulate all movements int CTTRTSGame::SimulateMovements() { + // Grab all movement orders + COrderVector movements; + for ( COrderVector::const_iterator it = m_orders.begin(); it != m_orders.end(); it++ ) + { + if( isMovementOrder(*it) ) + movements.push_back(*it); + } + + // Calculate movements + return 0; } // Simulate all actions int CTTRTSGame::SimulateActions() { + // Grab all action orders + COrderVector actions; + for ( COrderVector::const_iterator it = m_orders.begin(); it != m_orders.end(); it++ ) + { + if( isActionOrder(*it) ) + actions.push_back(*it); + } + + // Calculate actions + return 0; } diff --git a/game/orders.cpp b/game/orders.cpp index 4cae3d2..5928f1f 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -1,7 +1,9 @@ #include "orders.h" +#include "mathtypes.h" + // Convert an order to a string -std::string GetStringFromOrder( COrder& order ) +std::string GetStringFromOrder(const COrder& order ) { std::string ret; ret += std::to_string(order.unit); @@ -12,8 +14,9 @@ std::string GetStringFromOrder( COrder& order ) } // Convert a string to an order -COrder GetOrderFromString( std::string order ) +COrder GetOrderFromString( const std::string& _order ) { + std::string order = _order; COrder ret; int pos = order.find(ORDER_DELIMITER); @@ -31,4 +34,26 @@ COrder GetOrderFromString( std::string order ) } return ret; +} + +bool isMovementOrder( const COrder& order ) +{ + const order_c c = order.order; + for ( unsigned int i = 0; i < _countof(sk_movementOrders); i++ ) + { + if ( c == sk_movementOrders[i] ) + return true; + } + return false; +} + +bool isActionOrder( const COrder& order ) +{ + const order_c c = order.order; + for ( unsigned int i = 0; i < _countof(sk_actionOrders); i++ ) + { + if ( c == sk_actionOrders[i] ) + return true; + } + return false; } \ No newline at end of file diff --git a/game/orders.h b/game/orders.h index e90d3c7..a433235 100644 --- a/game/orders.h +++ b/game/orders.h @@ -11,6 +11,20 @@ // Type for all orders ( as a char ) typedef char order_c; +// Movement orders +static const order_c sk_movementOrders[] = +{ + 'F', // Forward +}; + +// Action orders +static const order_c sk_actionOrders[] = +{ + 'L', // Left + 'R', // Right + 'A', // Attack +}; + // Container for an order struct COrder { @@ -36,7 +50,10 @@ typedef std::vector COrderVector; // Order strings stored as simply "[unit id] [order char]" // string <--> order conversion functions -std::string GetStringFromOrder( COrder& order ); -COrder GetOrderFromString( std::string order ); +std::string GetStringFromOrder(const COrder& order ); +COrder GetOrderFromString( const std::string& order ); + +bool isMovementOrder( const COrder& order ); +bool isActionOrder( const COrder& order ); #endif //_ORDERS_H_ \ No newline at end of file diff --git a/maths/mathtypes.h b/maths/mathtypes.h index ab1e0eb..322f78f 100644 --- a/maths/mathtypes.h +++ b/maths/mathtypes.h @@ -3,6 +3,11 @@ #include // std::numeric_limits +#include "stdlib.h" // for size_t + +template +constexpr size_t _countof(T (&)[N]) { return N; } + // Coordinate types typedef short coord_t; typedef unsigned short ucoord_t; diff --git a/test/test.cpp b/test/test.cpp index c6e9272..29ccbbe 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -4,69 +4,73 @@ #include "board.h" #include "orders.h" - -// Namespace for testing functions -namespace tests +const char* tests() { - // print a board - void debug_print( CBoard& b ) - { - for ( unsigned int r = 0; r < b.rows; r++ ) - { - for ( unsigned int c = 0; c < b.cols; c++ ) - { - std::cout<<(char)(b.get(c,r)); - } - std::cout< myV = CUnit::getUnitFromVis('v'); if( myV->getVisual() != 'v' ) - std::cout<<"ERROR, failed to properly create V unit"< Date: Tue, 16 Dec 2014 13:13:00 +0000 Subject: [PATCH 031/190] Even more tests --- test/test.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/test.cpp b/test/test.cpp index 29ccbbe..670db3b 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -3,6 +3,7 @@ #include "unitv.h" #include "board.h" #include "orders.h" +#include "game.h" const char* tests() { @@ -18,6 +19,13 @@ const char* tests() return "failed to properly create V unit"; } + { + CUnitV myV; + CUnitV myV2; + if( myV.getID() == myV2.getID() ) + return "Unit IDs the same"; + } + { std::unique_ptr myV = CUnit::getUnitFromVis('v'); if( myV->getVisual() != 'v' ) @@ -55,6 +63,18 @@ const char* tests() return "Wrongly detected an movement order"; } + { + CTTRTSGame game( 15, 10 ); + if( game.SimulateToNextTurn() ) + return "Failed to simulate a blank game"; + + if( game.GetNumOrders() ) + return "Game started with non-zero order number"; + + if( game.GetNumUnits() ) + return "Game started with non-zero unit number"; + } + return nullptr; } From 0700a13129907bd36f1d8b115e2cef8cb0a040a1 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 032/190] use uvec unstead of two params, simplifies things --- game/game.cpp | 2 +- game/game.h | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index b52b671..20c6801 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -73,7 +73,7 @@ int CTTRTSGame::AddUnit( std::shared_ptr unit ) // Verify if the unit can be placed on the current board const uvector2 pos = unit->getPos(); - if( (pos.x < cols) && (pos.y < rows) ) + if( (pos.x < dimentions.x) && (pos.y < dimentions.y) ) return 1; m_allUnits.push_back(unit); diff --git a/game/game.h b/game/game.h index b4d0857..883b347 100644 --- a/game/game.h +++ b/game/game.h @@ -14,8 +14,7 @@ class CTTRTSGame public: CTTRTSGame( ucoord_t c, ucoord_t r ) - : cols (c) - , rows (r) + : dimentions( {c,r} ) { } @@ -50,6 +49,9 @@ public: // Get orders by index as above inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_orders[i]; } + + // Get dimentions + inline const uvector2& GetDimentions() const { return dimentions; }; private: @@ -72,8 +74,7 @@ private: COrderVector m_orders; // Dimensions of the game - ucoord_t rows; - ucoord_t cols; + uvector2 dimentions; }; #endif //_GAME_H_ \ No newline at end of file From 0c7721cb172dcccfcd80fff20be5f86bbd4df2b8 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 033/190] remove unit inheritence, twas a silly idea, and remove usage of shared ptr and unique ptr not needed anymore --- game/CMakeLists.txt | 1 - game/game.cpp | 19 +++++----- game/game.h | 10 ++--- game/unit.cpp | 89 ++++++++++++++++++++++++++++++++++----------- game/unit.h | 19 ++++++---- game/unitv.cpp | 63 -------------------------------- game/unitv.h | 26 ------------- test/test.cpp | 16 ++++---- 8 files changed, 101 insertions(+), 142 deletions(-) delete mode 100644 game/unitv.cpp delete mode 100644 game/unitv.h diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 935bb02..79b8f0a 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -13,7 +13,6 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) # Add the sources set( SOURCES game.cpp - unitv.cpp unit.cpp orders.cpp ) diff --git a/game/game.cpp b/game/game.cpp index 20c6801..c7354f7 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -64,33 +64,32 @@ int CTTRTSGame::SimulateToNextTurn() } // Add a unit, nonzero return value indicates error -int CTTRTSGame::AddUnit( std::shared_ptr unit ) +int CTTRTSGame::AddUnit( CUnit&& unit ) { // Verify the unit - const int val = unit->valid(); + const int val = unit.valid(); if( val ) return val; // Verify if the unit can be placed on the current board - const uvector2 pos = unit->getPos(); + const uvector2 pos = unit.getPos(); if( (pos.x < dimentions.x) && (pos.y < dimentions.y) ) return 1; - m_allUnits.push_back(unit); - + m_allUnits.push_back(std::move(unit)); return 0; } // Add a units, nonzero return value indicates error -int CTTRTSGame::AddUnits( sharedUnitVector_t units ) +int CTTRTSGame::AddUnits( CUnitVector&& units ) { - sharedUnitVector_t::iterator it; + CUnitVector::iterator it; for ( it = units.begin(); it != units.end(); it++ ) { // Attempt the unit add - if ( AddUnit(*it) ) + if ( AddUnit( std::move(*it) ) ) return 1; } @@ -106,9 +105,9 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) // Attempt to find the unit bool unitFound = false; - for ( sharedUnitVector_t::const_iterator it = m_allUnits.begin(); it != m_allUnits.end(); it++ ) + for ( CUnitVector::const_iterator it = m_allUnits.begin(); it != m_allUnits.end(); it++ ) { - if ( (*it)->getID() == unitID ) + if ( (*it).getID() == unitID ) { unitFound = true; break; diff --git a/game/game.h b/game/game.h index 883b347..7f511ea 100644 --- a/game/game.h +++ b/game/game.h @@ -7,7 +7,7 @@ #include // unique_ptr and shared_ptr -typedef std::vector< std::shared_ptr > sharedUnitVector_t; +typedef std::vector< CUnit > CUnitVector; class CTTRTSGame { @@ -33,16 +33,16 @@ public: int SimulateToNextTurn(); // Add a unit, nonzero return value indicates error - int AddUnit( std::shared_ptr unit ); + int AddUnit( CUnit&& unit ); // Add a units, nonzero return value indicates error - int AddUnits( sharedUnitVector_t units ); + int AddUnits( CUnitVector&& units ); // Get the number of units inline unsigned int GetNumUnits() const { return m_allUnits.size(); } // Get unit by index as above (not unit ID) - inline const CUnit& GetUnitByIndex( unsigned int i ) const { return *m_allUnits[i]; } + inline const CUnit& GetUnitByIndex( unsigned int i ) const { return m_allUnits[i]; } // Get the number of order inline unsigned int GetNumOrders() const { return m_orders.size(); } @@ -68,7 +68,7 @@ private: int VerifyUnit( const CUnit& unit ); // Vector to store points to all units - sharedUnitVector_t m_allUnits; + CUnitVector m_allUnits; // Orders to execute this turn COrderVector m_orders; diff --git a/game/unit.cpp b/game/unit.cpp index 524941c..03b16b7 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -1,7 +1,6 @@ #include "unit.h" -// Unit types -#include "unitv.h" +#include // for std::map namespace { @@ -11,37 +10,83 @@ namespace static unit_id_t p = 0; return p++; } + + // Map of visual representation of unit V + typedef std::map< dir_t, unitVis_c > dir_to_vis_map; + + // Helper function to get the vis map during static init + const dir_to_vis_map& get_vis_map_V() + { + static const dir_to_vis_map sk_visMap = + { + {dir_t::N,'^'}, + {dir_t::E,'>'}, + {dir_t::S,'v'}, + {dir_t::W,'<'}, + }; + + return sk_visMap; + } } +// Plain constructor CUnit::CUnit() -: unit_id ( get_unique_unit_id() ) -, team_id ( team_id_invalid ) +: unit_id ( get_unique_unit_id() ) +, team_id ( team_id_invalid ) , player_id ( player_id_invalid ) -, unit_vis ( unitVis_invalid ) +, unit_vis ( unitVis_invalid ) +, dir ( dir_t::S ) { - + updateMyVisual(); }; -std::unique_ptr CUnit::getUnitFromVis( unitVis_c vis ) +// Move constructor +CUnit::CUnit(CUnit&& unit) +: unit_id ( std::move(unit.unit_id) ) +, team_id ( std::move(unit.team_id) ) +, player_id ( std::move(unit.player_id) ) +, unit_vis ( std::move(unit.unit_vis) ) +, dir ( std::move(unit.dir) ) { - switch( vis ) + updateMyVisual(); +} + +CUnit&& CUnit::getUnitFromVis( unitVis_c vis ) +{ + CUnit unit; + unit.setFromVisual(vis); + return std::move(unit); +} + +// Update the visual representation of the unit +unitVis_c CUnit::updateMyVisual() +{ + // Start at invalid + setVisual(unitVis_invalid); + + dir_to_vis_map::const_iterator it = get_vis_map_V().find(dir); + + // If found set to new vis + if( it != get_vis_map_V().end() ) + setVisual(it->second); + + return getVisual(); +} + +bool CUnit::setFromVisual( const unitVis_c& vis ) +{ + dir_to_vis_map::const_iterator it; + + for( it = get_vis_map_V().begin(); it != get_vis_map_V().end(); it++ ) { - // Match with any image for a V - case '^': - case '>': - case 'v': - case '<': + if( it->second == vis ) { - // Create a V - std::unique_ptr p = std::unique_ptr(new CUnitV); - if( (bool)p && p->setFromVisual(vis) ) - { - return std::move(p); - } - break; + dir == it->first; + updateMyVisual(); + return true; } } - // No unit found, return nullptr - return std::move(std::unique_ptr(nullptr)); + // No matching direction to visual + return false; } \ No newline at end of file diff --git a/game/unit.h b/game/unit.h index 51764af..c1b51c9 100644 --- a/game/unit.h +++ b/game/unit.h @@ -11,8 +11,10 @@ class CUnit { public: - virtual ~CUnit() = default; + CUnit(); + CUnit(CUnit&& unit); + ~CUnit() = default; inline const unit_id_t& getID() const { return unit_id; } inline const team_id_t& getTeam() const { return team_id; } @@ -31,19 +33,20 @@ public: inline bool valid() const; // Set a unit based solely on it's visual - // Maybe make non-virtual at some point to avoid vtable lookups - virtual bool setFromVisual(unitVis_c& vis) = 0; + bool setFromVisual( const unitVis_c& vis); // Factory function for creating units from a visual - static std::unique_ptr getUnitFromVis( unitVis_c vis ); + static CUnit&& getUnitFromVis( unitVis_c vis ); + protected: - // Protected constructor, cannot be constructed as base type - CUnit(); private: + // Update the visual of V + unitVis_c updateMyVisual(); + // Unit ID const unit_id_t unit_id; @@ -56,8 +59,10 @@ private: // Owner ID player_id_t player_id; + // Direction + dir_t dir; - // All units must have position + // Position uvector2 pos; }; diff --git a/game/unitv.cpp b/game/unitv.cpp deleted file mode 100644 index 39f0d95..0000000 --- a/game/unitv.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "unitv.h" - -#include // for std::map - -CUnitV::CUnitV() -: dir(dir_t::S) -{ - updateMyVisual(); -} - - -// Map of visual representation of unitv -namespace -{ - typedef std::map< dir_t, unitVis_c > dir_to_vis_map; - - // Helper function to get the vis map during static init - const dir_to_vis_map& get_vis_map() - { - static const dir_to_vis_map sk_visMap = - { - {dir_t::N,'^'}, - {dir_t::E,'>'}, - {dir_t::S,'v'}, - {dir_t::W,'<'}, - }; - - return sk_visMap; - } -} - -// Update the visual representation of the unit -unitVis_c CUnitV::updateMyVisual() -{ - // Start at invalid - setVisual(unitVis_invalid); - - dir_to_vis_map::const_iterator it = get_vis_map().find(dir); - - // If found set to new vis - if( it != get_vis_map().end() ) - setVisual(it->second); - - return getVisual(); -} - -bool CUnitV::setFromVisual( unitVis_c& vis ) -{ - dir_to_vis_map::const_iterator it; - - for( it = get_vis_map().begin(); it != get_vis_map().end(); it++ ) - { - if( it->second == vis ) - { - dir == it->first; - updateMyVisual(); - return true; - } - } - - // No matching direction to visual - return false; -} \ No newline at end of file diff --git a/game/unitv.h b/game/unitv.h deleted file mode 100644 index a1cc463..0000000 --- a/game/unitv.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _UNITV_H_ -#define _UNITV_H_ - -#include "unit.h" - -// V unit -class CUnitV -: public CUnit -{ -public: - CUnitV(); - virtual ~CUnitV() = default; - - // Set from a visual - virtual bool setFromVisual( unitVis_c& vis ) override; - -private: - - // Update the visual of V - unitVis_c updateMyVisual(); - - // V also has a direction - dir_t dir; -}; - -#endif //_UNITV_H_ \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp index 670db3b..6b111f7 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,6 +1,5 @@ #include // std::cout -#include "unitv.h" #include "board.h" #include "orders.h" #include "game.h" @@ -14,21 +13,22 @@ const char* tests() } { - CUnitV myV; - if( myV.getVisual() != 'v' && myV.getVisual() != '<' && myV.getVisual() != '^' && myV.getVisual() != '>' ) + CUnit unit; + unit.setFromVisual('v'); + if( unit.getVisual() != 118 ) return "failed to properly create V unit"; } { - CUnitV myV; - CUnitV myV2; - if( myV.getID() == myV2.getID() ) + CUnit unit; + CUnit unit2; + if( unit.getID() == unit2.getID() ) return "Unit IDs the same"; } { - std::unique_ptr myV = CUnit::getUnitFromVis('v'); - if( myV->getVisual() != 'v' ) + CUnit unit = CUnit::getUnitFromVis('v'); + if( unit.getVisual() != 'v' ) return "failed to properly create V unit"; } From 2f17a17587aa0a4370ea888b18be521412363485 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 034/190] fix using move semantics for no reason --- game/unit.cpp | 6 +++--- game/unit.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/game/unit.cpp b/game/unit.cpp index 03b16b7..30bc711 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -51,11 +51,11 @@ CUnit::CUnit(CUnit&& unit) updateMyVisual(); } -CUnit&& CUnit::getUnitFromVis( unitVis_c vis ) +CUnit CUnit::getUnitFromVis( unitVis_c vis ) { CUnit unit; unit.setFromVisual(vis); - return std::move(unit); + return unit; } // Update the visual representation of the unit @@ -89,4 +89,4 @@ bool CUnit::setFromVisual( const unitVis_c& vis ) // No matching direction to visual return false; -} \ No newline at end of file +} diff --git a/game/unit.h b/game/unit.h index c1b51c9..cc211c8 100644 --- a/game/unit.h +++ b/game/unit.h @@ -36,7 +36,7 @@ public: bool setFromVisual( const unitVis_c& vis); // Factory function for creating units from a visual - static CUnit&& getUnitFromVis( unitVis_c vis ); + static CUnit getUnitFromVis( unitVis_c vis ); protected: @@ -75,4 +75,4 @@ inline bool CUnit::valid() const && (unit_vis != unitVis_invalid); } -#endif //_UNIT_H_ \ No newline at end of file +#endif //_UNIT_H_ From b43248e89fa46ed7d8a0bb3a168dd72d69325c82 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 035/190] Clarify test failure --- test/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.cpp b/test/test.cpp index 6b111f7..81fa864 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -29,7 +29,7 @@ const char* tests() { CUnit unit = CUnit::getUnitFromVis('v'); if( unit.getVisual() != 'v' ) - return "failed to properly create V unit"; + return "failed to properly create V unit with factory"; } { From e310acfaf817aaa50681c1d5f51e383e94725e9a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 036/190] More skeleton code for simulating a turn Use enum classes for safe enums --- CMakeLists.txt.user | 228 ++++++++++++++ game/game.cpp | 59 +++- game/game.h | 24 +- game/orders.cpp | 6 +- game/orders.h | 18 +- maths/mathtypes.h | 6 +- test/test.cpp | 8 +- ttrts.sublime-project | 8 + ttrts.sublime-workspace | 651 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 983 insertions(+), 25 deletions(-) create mode 100644 CMakeLists.txt.user create mode 100644 ttrts.sublime-project create mode 100644 ttrts.sublime-workspace diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 0000000..6ec3667 --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,228 @@ + + + + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + true + 1 + true + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {4857d833-10e4-4a1a-ab3c-1fb012a1b975} + 0 + 0 + 1 + + /home/mdiluzio/Projects/ttrts/build + false + + + + + all + + false + false + true + Make + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + clean + + true + false + true + Make + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + all + + CMakeProjectManager.CMakeBuildConfiguration + + 1 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy locally + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + true + + false + false + false + false + true + 0.01 + 10 + true + 25 + + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + ttrts + + false + + + ttrts + + CMakeProjectManager.CMakeRunConfiguration.ttrts + 3768 + true + false + false + true + + + true + + false + false + false + false + true + 0.01 + 10 + true + 25 + + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + ttrts-test + + false + + + ttrts-test + + CMakeProjectManager.CMakeRunConfiguration.ttrts-test + 3768 + true + false + false + true + + 2 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.EnvironmentId + {3beffcb8-4a04-420b-880b-3a95e17b2e51} + + + ProjectExplorer.Project.Updater.FileVersion + 12 + + diff --git a/game/game.cpp b/game/game.cpp index c7354f7..7d30032 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -98,7 +98,7 @@ int CTTRTSGame::AddUnits( CUnitVector&& units ) } // Verify any order -int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) +int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) const { // Grab the unit ID const unit_id_t unitID = order.unit; @@ -118,17 +118,68 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) return unitFound; } + +// Get unit by unit ID +const CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) const +{ + CUnitVector::const_iterator it; + + for ( it = m_allUnits.begin(); it != m_allUnits.end(); it++ ) + { + // Attempt the unit add + if ( (*it).getID() ) + return *it; + } + + // Return an invalid unit + static CUnit invalid_unit; + return invalid_unit; +} + +// Verify an order unit pair +int CTTRTSGame::VerifyOrderUnitPair( const OrderUnitPair& pair ) const +{ + switch ( pair.order.order ) + { + case order_c::F: + { + // Verify new unit position will be on the board + } + break; + case order_c::L: + case order_c::R: + case order_c::A: + // Nothing needed here, orders can always be carried out + break; + } + + return 0; +} + // Simulate all movements int CTTRTSGame::SimulateMovements() { - // Grab all movement orders - COrderVector movements; + OrderUnitPairVector movements; + + // Grab all movement orders for ( COrderVector::const_iterator it = m_orders.begin(); it != m_orders.end(); it++ ) { if( isMovementOrder(*it) ) - movements.push_back(*it); + { + const OrderUnitPair pair = { *it, GetUnitByID((*it).unit) }; + movements.push_back(pair); + } } + // Remove all orders with straight up impossible movements + for ( OrderUnitPairVector::const_iterator it = movements.begin(); it != movements.end(); it++ ) + { + if( VerifyOrderUnitPair(*it) ) + { + + } + } + // Calculate movements return 0; diff --git a/game/game.h b/game/game.h index 7f511ea..10913e0 100644 --- a/game/game.h +++ b/game/game.h @@ -9,6 +9,15 @@ typedef std::vector< CUnit > CUnitVector; +// Type for order and unit pairs +struct OrderUnitPair +{ + const COrder& order; + const CUnit& unit; +}; + +typedef std::vector< OrderUnitPair > OrderUnitPairVector; + class CTTRTSGame { public: @@ -44,6 +53,9 @@ public: // Get unit by index as above (not unit ID) inline const CUnit& GetUnitByIndex( unsigned int i ) const { return m_allUnits[i]; } + // Get unit by unit ID + const CUnit& GetUnitByID( unit_id_t id ) const; + // Get the number of order inline unsigned int GetNumOrders() const { return m_orders.size(); } @@ -51,7 +63,7 @@ public: inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_orders[i]; } // Get dimentions - inline const uvector2& GetDimentions() const { return dimentions; }; + inline const uvector2& GetDimentions() const { return dimentions; } private: @@ -61,11 +73,13 @@ private: // Simulate all actions int SimulateActions(); - // Verify any order - int VerifyOrder( player_id_t player, const COrder& order ); + int VerifyOrderUnitPair( const OrderUnitPair& pair ) const; // Verify any order - int VerifyUnit( const CUnit& unit ); + int VerifyOrder( player_id_t player, const COrder& order ) const; + + // Verify any order + int VerifyUnit( const CUnit& unit ) const; // Vector to store points to all units CUnitVector m_allUnits; @@ -77,4 +91,4 @@ private: uvector2 dimentions; }; -#endif //_GAME_H_ \ No newline at end of file +#endif //_GAME_H_ diff --git a/game/orders.cpp b/game/orders.cpp index 5928f1f..96606e9 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -8,7 +8,7 @@ std::string GetStringFromOrder(const COrder& order ) std::string ret; ret += std::to_string(order.unit); ret += ORDER_DELIMITER; - ret += order.order; + ret += (char)order.order; return ret; } @@ -30,7 +30,7 @@ COrder GetOrderFromString( const std::string& _order ) order.erase(0, pos + 1); // Next single char is the order - ret.order = order[0]; + ret.order = (order_c)order[0]; } return ret; @@ -56,4 +56,4 @@ bool isActionOrder( const COrder& order ) return true; } return false; -} \ No newline at end of file +} diff --git a/game/orders.h b/game/orders.h index a433235..400f2b3 100644 --- a/game/orders.h +++ b/game/orders.h @@ -9,20 +9,26 @@ #define ORDER_DELIMITER ' ' // Type for all orders ( as a char ) -typedef char order_c; +enum class order_c : char +{ + F = 'F', + L = 'L', + R = 'R', + A = 'A' +}; // Movement orders static const order_c sk_movementOrders[] = { - 'F', // Forward + order_c::F, // Forward }; // Action orders static const order_c sk_actionOrders[] = { - 'L', // Left - 'R', // Right - 'A', // Attack + order_c::L, // Left + order_c::R, // Right + order_c::A, // Attack }; // Container for an order @@ -56,4 +62,4 @@ COrder GetOrderFromString( const std::string& order ); bool isMovementOrder( const COrder& order ); bool isActionOrder( const COrder& order ); -#endif //_ORDERS_H_ \ No newline at end of file +#endif //_ORDERS_H_ diff --git a/maths/mathtypes.h b/maths/mathtypes.h index 322f78f..b7804af 100644 --- a/maths/mathtypes.h +++ b/maths/mathtypes.h @@ -17,12 +17,12 @@ static const coord_t coord_invalid = std::numeric_limits::max(); static const ucoord_t ucoord_invalid = std::numeric_limits::max(); // Direction representation -enum dir_t : char +enum class dir_t : char { N = 'N', S = 'S', E = 'E', - W = 'W', + W = 'W' }; -#endif //_BASETYPES_H_ \ No newline at end of file +#endif //_BASETYPES_H_ diff --git a/test/test.cpp b/test/test.cpp index 81fa864..c4b5864 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -34,7 +34,7 @@ const char* tests() { COrder order; - order.order = 'F'; + order.order = order_c::F; order.unit = 10; std::string order_string = GetStringFromOrder(order); COrder order2 = GetOrderFromString(order_string); @@ -45,7 +45,7 @@ const char* tests() { COrder order; - order.order = 'F'; + order.order = order_c::F; if (!isMovementOrder(order) ) return "Failed to detect a movement order"; @@ -55,7 +55,7 @@ const char* tests() { COrder order; - order.order = 'L'; + order.order = order_c::A; if (! isActionOrder(order) ) return "Failed to detect a action order"; @@ -93,4 +93,4 @@ int main() std::cout<<"Tests succeeded"< Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 037/190] Simplify some things, zomg c++11 for loops are beautiful --- game/game.cpp | 128 ++++++++++++++++++++------------------------------ game/game.h | 8 ---- 2 files changed, 52 insertions(+), 84 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 7d30032..8d6f899 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -25,9 +25,9 @@ int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) int CTTRTSGame::IssueOrders( player_id_t player, const COrderVector& orders ) { // verify all the orders - for ( COrderVector::const_iterator it = orders.begin(); it != orders.end(); it++ ) + for ( auto order : orders ) { - if ( IssueOrder(player,*it) ) + if ( IssueOrder(player,order) ) return 1; } @@ -51,11 +51,52 @@ int CTTRTSGame::SimulateToNextTurn() { int error; - // Simulate all movements first - error = SimulateMovements(); + OrderUnitPairVector orderPairs; - // Simulate all the actions - error = SimulateActions(); + // Grab all movement orders + for ( auto order : m_orders ) + { + const OrderUnitPair pair = { order, GetUnitByID(order.unit) }; + orderPairs.push_back(pair); + } + + // Attempt all movement orders + for ( auto pair : orderPairs ) + { + switch ( pair.order.order ) + { + case order_c::F: + { + // Verify new unit position will be on the board + } + break; + } + } + + // Vector of units to kill + std::vector< unit_id_t > toKill; + + // Attempt all actions + for ( auto pair : orderPairs ) + { + switch ( pair.order.order ) + { + case order_c::A: + { + // Verify that there's a unit in front to attack + } + break; + case order_c::L: + case order_c::R: + // Nothing needed here, these orders can always be carried out + break; + } + } + + for ( auto id : toKill ) + { + + } // Clear all orders m_orders.resize(0); @@ -105,9 +146,9 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) const // Attempt to find the unit bool unitFound = false; - for ( CUnitVector::const_iterator it = m_allUnits.begin(); it != m_allUnits.end(); it++ ) + for ( const CUnit& unit : m_allUnits ) { - if ( (*it).getID() == unitID ) + if ( unit.getID() == unitID ) { unitFound = true; break; @@ -124,79 +165,14 @@ const CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) const { CUnitVector::const_iterator it; - for ( it = m_allUnits.begin(); it != m_allUnits.end(); it++ ) + for ( const CUnit& unit : m_allUnits ) { // Attempt the unit add - if ( (*it).getID() ) - return *it; + if ( unit.getID() ) + return unit; } // Return an invalid unit static CUnit invalid_unit; return invalid_unit; } - -// Verify an order unit pair -int CTTRTSGame::VerifyOrderUnitPair( const OrderUnitPair& pair ) const -{ - switch ( pair.order.order ) - { - case order_c::F: - { - // Verify new unit position will be on the board - } - break; - case order_c::L: - case order_c::R: - case order_c::A: - // Nothing needed here, orders can always be carried out - break; - } - - return 0; -} - -// Simulate all movements -int CTTRTSGame::SimulateMovements() -{ - OrderUnitPairVector movements; - - // Grab all movement orders - for ( COrderVector::const_iterator it = m_orders.begin(); it != m_orders.end(); it++ ) - { - if( isMovementOrder(*it) ) - { - const OrderUnitPair pair = { *it, GetUnitByID((*it).unit) }; - movements.push_back(pair); - } - } - - // Remove all orders with straight up impossible movements - for ( OrderUnitPairVector::const_iterator it = movements.begin(); it != movements.end(); it++ ) - { - if( VerifyOrderUnitPair(*it) ) - { - - } - } - - // Calculate movements - - return 0; -} - -// Simulate all actions -int CTTRTSGame::SimulateActions() -{ - // Grab all action orders - COrderVector actions; - for ( COrderVector::const_iterator it = m_orders.begin(); it != m_orders.end(); it++ ) - { - if( isActionOrder(*it) ) - actions.push_back(*it); - } - - // Calculate actions - - return 0; -} diff --git a/game/game.h b/game/game.h index 10913e0..c13024e 100644 --- a/game/game.h +++ b/game/game.h @@ -67,14 +67,6 @@ public: private: - // Simulate all movements - int SimulateMovements(); - - // Simulate all actions - int SimulateActions(); - - int VerifyOrderUnitPair( const OrderUnitPair& pair ) const; - // Verify any order int VerifyOrder( player_id_t player, const COrder& order ) const; From 6fc41914f7a1f67c9e475c6fe87c98167dd834d8 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 038/190] ignore user files --- .gitignore | 4 +- CMakeLists.txt.user | 228 -------------- ttrts.sublime-workspace | 651 ---------------------------------------- 3 files changed, 3 insertions(+), 880 deletions(-) delete mode 100644 CMakeLists.txt.user delete mode 100644 ttrts.sublime-workspace diff --git a/.gitignore b/.gitignore index c795b05..7ccaa71 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -build \ No newline at end of file +build +.user +.sublime-workspace diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user deleted file mode 100644 index 6ec3667..0000000 --- a/CMakeLists.txt.user +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - true - 1 - true - 0 - true - 0 - 8 - true - 1 - true - true - true - false - - - - ProjectExplorer.Project.PluginSettings - - - - ProjectExplorer.Project.Target.0 - - Desktop - Desktop - {4857d833-10e4-4a1a-ab3c-1fb012a1b975} - 0 - 0 - 1 - - /home/mdiluzio/Projects/ttrts/build - false - - - - - all - - false - false - true - Make - - CMakeProjectManager.MakeStep - - 1 - Build - - ProjectExplorer.BuildSteps.Build - - - - clean - - true - false - true - Make - - CMakeProjectManager.MakeStep - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - all - - CMakeProjectManager.CMakeBuildConfiguration - - 1 - - - 0 - Deploy - - ProjectExplorer.BuildSteps.Deploy - - 1 - Deploy locally - - ProjectExplorer.DefaultDeployConfiguration - - 1 - - - true - - false - false - false - false - true - 0.01 - 10 - true - 25 - - true - valgrind - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - 2 - ttrts - - false - - - ttrts - - CMakeProjectManager.CMakeRunConfiguration.ttrts - 3768 - true - false - false - true - - - true - - false - false - false - false - true - 0.01 - 10 - true - 25 - - true - valgrind - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - 2 - ttrts-test - - false - - - ttrts-test - - CMakeProjectManager.CMakeRunConfiguration.ttrts-test - 3768 - true - false - false - true - - 2 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.EnvironmentId - {3beffcb8-4a04-420b-880b-3a95e17b2e51} - - - ProjectExplorer.Project.Updater.FileVersion - 12 - - diff --git a/ttrts.sublime-workspace b/ttrts.sublime-workspace deleted file mode 100644 index 42d1cf6..0000000 --- a/ttrts.sublime-workspace +++ /dev/null @@ -1,651 +0,0 @@ -{ - "auto_complete": - { - "selected_items": - [ - [ - "eErr", - "eError_noErr" - ], - [ - "g", - "gMainWindow" - ] - ] - }, - "buffers": - [ - { - "file": "game/unit.cpp", - "settings": - { - "buffer_size": 486, - "line_ending": "Unix" - } - }, - { - "file": "game/game.h", - "settings": - { - "buffer_size": 1722, - "line_ending": "Unix" - } - }, - { - "file": "game/board.h", - "settings": - { - "buffer_size": 0, - "line_ending": "Unix" - } - }, - { - "file": "game/CMakeLists.txt", - "settings": - { - "buffer_size": 320, - "line_ending": "Unix" - } - }, - { - "file": "ui/CMakeLists.txt", - "settings": - { - "buffer_size": 293, - "line_ending": "Unix" - } - }, - { - "file": "CMakeLists.txt", - "settings": - { - "buffer_size": 307, - "line_ending": "Unix" - } - }, - { - "file": "test/CMakeLists.txt", - "settings": - { - "buffer_size": 384, - "line_ending": "Unix" - } - }, - { - "file": "ui/board.h", - "settings": - { - "buffer_size": 1299, - "line_ending": "Unix" - } - }, - { - "file": "game/unit.h", - "settings": - { - "buffer_size": 557, - "line_ending": "Unix" - } - }, - { - "file": "game/gametypes.h", - "settings": - { - "buffer_size": 367, - "line_ending": "Unix" - } - }, - { - "file": "game/game.cpp", - "settings": - { - "buffer_size": 18, - "line_ending": "Unix" - } - } - ], - "build_system": "", - "command_palette": - { - "height": 54.0, - "selected_items": - [ - [ - "install", - "Package Control: Install Package" - ], - [ - "insta", - "Package Control: Install Package" - ] - ], - "width": 449.0 - }, - "console": - { - "height": 139.0 - }, - "distraction_free": - { - "menu_visible": true, - "show_minimap": false, - "show_open_files": false, - "show_tabs": false, - "side_bar_visible": false, - "status_bar_visible": false - }, - "file_history": - [ - "/home/mdiluzio/Projects/ttrts/game/game.h", - "/home/mdiluzio/Projects/ttrts/game/game.cpp", - "/home/mdiluzio/Projects/ttrts/ttrts/CMakeLists.txt", - "/home/mdiluzio/Projects/ttrts/ttrts/main.cpp", - "/home/mdiluzio/Projects/ttrts/CMakeLists.txt", - "/home/mdiluzio/Projects/ttrts/test/CMakeLists.txt", - "/home/mdiluzio/Projects/ttrts/game/README.md", - "/home/mdiluzio/Projects/ttrts/maths/vector2.h", - "/home/mdiluzio/Projects/ttrts/game/gametypes.h", - "/home/mdiluzio/Projects/ttrts/game/unit.h", - "/home/mdiluzio/Projects/ttrts/game/unitv.h", - "/home/mdiluzio/Projects/ttrts/maths/mathtypes.h", - "/home/mdiluzio/Projects/ttrts/game/orders.h", - "/home/mdiluzio/Projects/ttrts/game/orders.cpp", - "/home/mdiluzio/Projects/ttrts/game/CMakeLists.txt", - "/home/mdiluzio/Projects/ttrts/test/test.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/build/SDLGame.sublime-workspace", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/CMakeLists.txt", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/src/SDLThread.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/LEngine/src/LEngine.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/src/SDLWindow.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/src/SDLEventLoop.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Util/include/error.h", - "/home/mdiluzio/Projects/SDLGame/Docs/html/types_8h_source.html", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Util/include/debug.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Util/src/debug.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Util/CMakeLists.txt", - "/home/mdiluzio/Projects/SDLGame/Source/CMakeLists.txt", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/LEngine/CMakeLists.txt", - "/home/mdiluzio/Projects/SDLGame/Source/src/main.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Util/include/types.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/src/SDLMain.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/include/SDLWindow.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/src/SDLRenderer.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/LEngine/include/LEngine.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/LEngine/include/LGameBase.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/LEngine/include/LObject.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/include/SDLEventLoop.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/include/SDLMain.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/include/SDLRenderer.h", - "/home/mdiluzio/Projects/SDLGame/Source/include/GameOne.h", - "/home/mdiluzio/Projects/SDLGame/readme.md", - "/home/mdiluzio/Projects/SDLGame/Source/include/TestDoxy.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/SDLInterface/SDL2/BUGS.txt", - "/home/mdiluzio/Projects/SDLGame/Source/src/GameOne.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/LEngine/src/LGameBase.cpp", - "/home/mdiluzio/Projects/SDLGame/.gitignore", - "/home/mdiluzio/Projects/SDLGame/Source/build/CMakeFiles/SDLGame.dir/CXX.includecache", - "/home/mdiluzio/Projects/SDLGame/Source/build/CMakeFiles/SDLGame.dir/build.make", - "/home/mdiluzio/Projects/SDLGame/Source/build/CMakeFiles/SDLGame.dir/depend.internal", - "/home/mdiluzio/Projects/SDLGame/Source/build/CMakeFiles/SDLGame.dir/depend.make", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/CXX.includecache", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/DependInfo.cmake", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/build.make", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/cmake_clean.cmake", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/depend.internal", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/depend.make", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/sdlinterface/build/CMakeFiles/sdlinterface.dir/build.make", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/sdlinterface/build/util/build/CMakeFiles/util.dir/build.make", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/CMakeFiles/lengine.dir/link.txt", - "/home/mdiluzio/Projects/SDLGame/Source/build/Libraries/LEngine/Makefile", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Engine/include/GameBase.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Engine/src/GameBase.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Engine/include/Engine.h", - "/home/mdiluzio/Projects/SDLGame/Source/Libraries/Engine/src/Engine.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/include/GameOne.cpp", - "/home/mdiluzio/Projects/SDLGame/Source/include/Prefix.pch", - "/home/mdiluzio/Projects/SDLGame/Source/include/untitled", - "/home/mdiluzio/Projects/SDLGame/Source/build/game", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/Engine/src/Engine.h", - "/home/mdiluzio/Projects/TestGame/Source/src/main.cpp", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/Util/include/types.h", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/SDLInterface/src/SDLEventLoop.cpp", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/SDLInterface/src/SDLWindow.cpp", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/SDLInterface/include/SDLEventLoop.h", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/SDLInterface/src/SDLMain.cpp", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/SDLInterface/include/SDLWindow.h", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/Engine/CMakeLists.txt", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/SDLInterface/CMakeLists.txt", - "/home/mdiluzio/Projects/TestGame/Source/Libraries/Util/CMakeLists.txt", - "/home/mdiluzio/Projects/TestGame/makeAndRun.sh", - "/home/mdiluzio/.config/sublime-text-2/Packages/User/cmake.sublime-build", - "/home/mdiluzio/Projects/TestGame/include/types.h", - "/home/mdiluzio/Projects/TestGame/build.sh", - "/home/mdiluzio/Projects/TestGame/src/main.cpp" - ], - "find": - { - "height": 35.0 - }, - "find_in_files": - { - "height": 0.0, - "where_history": - [ - "/home/mdiluzio/Projects/ttrts", - "/home/mdiluzio/Projects/SDLGame", - "/home/mdiluzio/Projects/TestGame/src" - ] - }, - "find_state": - { - "case_sensitive": true, - "find_history": - [ - "dir_t", - "tBoardPiece", - "piece_t", - "TBoardPiece", - "eError_quitRequested", - "eError_noErr", - "eError_SDL_Error", - "_GAMEBASE_H_", - "_OBJECT_H_", - "Object", - "GameBase", - "_ENGINE_H_", - "Engine", - "err = eError_noErr", - "err == eError_noErr", - "build", - ")\n", - "success", - "[" - ], - "highlight": true, - "in_selection": false, - "preserve_case": false, - "regex": false, - "replace_history": - [ - "square", - "square_t", - "piece_t", - "eError_quitRequest", - "_LGAMEBASE_H_", - "_LOBJECT_H_", - "LObject", - "LGameBase", - "_LENGINE_H_", - "LEngine", - "eError_noErr == err", - "err" - ], - "reverse": false, - "show_context": true, - "use_buffer2": true, - "whole_word": false, - "wrap": true - }, - "groups": - [ - { - "selected": 1, - "sheets": - [ - { - "buffer": 0, - "file": "game/unit.cpp", - "settings": - { - "buffer_size": 486, - "regions": - { - }, - "selection": - [ - [ - 204, - 204 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage", - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 1, - "file": "game/game.h", - "settings": - { - "buffer_size": 1722, - "regions": - { - }, - "selection": - [ - [ - 301, - 301 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage", - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 2, - "file": "game/board.h", - "settings": - { - "buffer_size": 0, - "regions": - { - }, - "selection": - [ - [ - 0, - 0 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage", - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 3, - "file": "game/CMakeLists.txt", - "settings": - { - "buffer_size": 320, - "regions": - { - }, - "selection": - [ - [ - 253, - 253 - ] - ], - "settings": - { - "syntax": "Packages/CMake/CMake.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 4, - "file": "ui/CMakeLists.txt", - "settings": - { - "buffer_size": 293, - "regions": - { - }, - "selection": - [ - [ - 293, - 293 - ] - ], - "settings": - { - "syntax": "Packages/CMake/CMake.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 5, - "file": "CMakeLists.txt", - "settings": - { - "buffer_size": 307, - "regions": - { - }, - "selection": - [ - [ - 304, - 304 - ] - ], - "settings": - { - "syntax": "Packages/CMake/CMake.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 6, - "file": "test/CMakeLists.txt", - "settings": - { - "buffer_size": 384, - "regions": - { - }, - "selection": - [ - [ - 382, - 382 - ] - ], - "settings": - { - "syntax": "Packages/CMake/CMake.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 7, - "file": "ui/board.h", - "settings": - { - "buffer_size": 1299, - "regions": - { - }, - "selection": - [ - [ - 59, - 59 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage", - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 8, - "file": "game/unit.h", - "settings": - { - "buffer_size": 557, - "regions": - { - }, - "selection": - [ - [ - 115, - 115 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage", - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 9, - "file": "game/gametypes.h", - "settings": - { - "buffer_size": 367, - "regions": - { - }, - "selection": - [ - [ - 162, - 162 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 10, - "file": "game/game.cpp", - "settings": - { - "buffer_size": 18, - "regions": - { - }, - "selection": - [ - [ - 18, - 18 - ] - ], - "settings": - { - "syntax": "Packages/C++/C++.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - } - ] - } - ], - "incremental_find": - { - "height": 0.0 - }, - "input": - { - "height": 33.0 - }, - "layout": - { - "cells": - [ - [ - 0, - 0, - 1, - 1 - ] - ], - "cols": - [ - 0.0, - 1.0 - ], - "rows": - [ - 0.0, - 1.0 - ] - }, - "menu_visible": true, - "output.exec": - { - "height": 112.0 - }, - "replace": - { - "height": 0.0 - }, - "save_all_on_build": true, - "select_file": - { - "height": 0.0, - "selected_items": - [ - ], - "width": 0.0 - }, - "select_project": - { - "height": 0.0, - "selected_items": - [ - ], - "width": 0.0 - }, - "show_minimap": true, - "show_open_files": false, - "show_tabs": true, - "side_bar_visible": true, - "side_bar_width": 153.0, - "status_bar_visible": true -} From aee703b107152528c2aa920678923f34e17616ff Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 039/190] Finialise the full game step Had to rework a ton of code from bad design. NOTE: FRIENDLY FIRE IS ON >:D --- game/game.cpp | 171 +++++++++++++++++++++++++++++++++++++++--------- game/game.h | 55 +++++++++++----- game/orders.h | 10 ++- game/unit.cpp | 92 +++++++++++++++++++++++++- game/unit.h | 18 +++-- maths/vector2.h | 59 ++++++++++++++++- test/test.cpp | 31 +++++++-- 7 files changed, 379 insertions(+), 57 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 8d6f899..701d6ed 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1,5 +1,6 @@ #include "game.h" +#include // Interpret a string of orders int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) @@ -40,34 +41,81 @@ int CTTRTSGame::IssueOrder( player_id_t player, const COrder& order ) if ( VerifyOrder(player,order) ) return 1; - m_orders.push_back(order); + for ( OrderUnitPair& pair : m_OrderUnitPairs ) + { + if ( pair.unit.getID() == order.unit ) + { + pair.order = order; + return 0; + } + } - return 0; + return 1; +} + +// Verify a position +int CTTRTSGame::VerifyPos(uvector2 vec) const +{ + if ( ( vec.x >= dimentions.x ) + || ( vec.y >= dimentions.y ) ) + { + return 1; + } + + return 0; +} + + +// Get a units new position +uvector2 CTTRTSGame::GetNewPosition( const OrderUnitPair& pair ) const +{ + switch ( pair.order.order ) + { + case order_c::F: + return pair.unit.getInFront(); + } + + return { ucoord_invalid,ucoord_invalid }; } // Simulate and progress to the next turn // Returns non-zero if simulation failed int CTTRTSGame::SimulateToNextTurn() { - int error; - - OrderUnitPairVector orderPairs; - - // Grab all movement orders - for ( auto order : m_orders ) - { - const OrderUnitPair pair = { order, GetUnitByID(order.unit) }; - orderPairs.push_back(pair); - } + int error = 0; // Attempt all movement orders - for ( auto pair : orderPairs ) + for ( OrderUnitPair& pair : m_OrderUnitPairs ) { switch ( pair.order.order ) { case order_c::F: { // Verify new unit position will be on the board + uvector2 newpos = GetNewPosition(pair); + + // Verify the position is even available + bool possible = ( VerifyPos(newpos) == 0 ); + + if ( possible ) + { + // If any unit is in this spot, or moving unit moving to said spot, reject this + for ( const OrderUnitPair& pair2 : m_OrderUnitPairs ) + { + if( GetNewPosition(pair2) != newpos ) + { + possible = false; + break; + } + } + } + + // If the movement is still possible + if ( possible ) + { + pair.unit.setPos(newpos); + pair.order = COrder(); + } } break; } @@ -77,29 +125,74 @@ int CTTRTSGame::SimulateToNextTurn() std::vector< unit_id_t > toKill; // Attempt all actions - for ( auto pair : orderPairs ) + for ( OrderUnitPair& pair : m_OrderUnitPairs ) { switch ( pair.order.order ) { case order_c::A: { // Verify that there's a unit in front to attack + uvector2 infront = pair.unit.getInFront(); + + // Check if there's any unit in front + // FRIENDLY FIRE IS ENABLED + for ( const OrderUnitPair& pair2 : m_OrderUnitPairs ) + { + // if the unit is infront of our unit, then add it to the kill list + if( pair2.unit.getPos() == infront ) + { + toKill.push_back(pair.unit.getID()); + pair.order = COrder(); + break; + } + } } break; case order_c::L: + { + pair.unit.turnLeft(); + pair.order = COrder(); + } + break; case order_c::R: - // Nothing needed here, these orders can always be carried out + { + pair.unit.turnRight(); + pair.order = COrder(); + } break; } } + // Sort and erase all duplicates + std::sort( toKill.begin(), toKill.end() ); + toKill.erase( std::unique( toKill.begin(), toKill.end() ), toKill.end() ); + + // Iterate through all kill orders for ( auto id : toKill ) { + // Kill the units + for ( OrderUnitPairVector::iterator it = m_OrderUnitPairs.begin(); + it != m_OrderUnitPairs.end(); + it++ ) + { + if( (*it).unit.getID() == id ) + { + // Add the dead unit to our dead unit list + m_deadUnits.push_back(std::move((*it).unit)); + + // Remove the unit from our alive unit pairs + m_OrderUnitPairs.erase(it); + break; + } + } } // Clear all orders - m_orders.resize(0); + for ( OrderUnitPair& pair : m_OrderUnitPairs ) + { + pair.order = COrder(); + } return error; } @@ -114,10 +207,11 @@ int CTTRTSGame::AddUnit( CUnit&& unit ) // Verify if the unit can be placed on the current board const uvector2 pos = unit.getPos(); - if( (pos.x < dimentions.x) && (pos.y < dimentions.y) ) + if( (pos.x >= dimentions.x) || (pos.y >= dimentions.y) ) return 1; - m_allUnits.push_back(std::move(unit)); + // Add the unit with a blank order + m_OrderUnitPairs.push_back( OrderUnitPair(std::move(unit), COrder()) ); return 0; } @@ -141,35 +235,52 @@ int CTTRTSGame::AddUnits( CUnitVector&& units ) // Verify any order int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) const { + int ret = 1; + // Grab the unit ID const unit_id_t unitID = order.unit; - // Attempt to find the unit - bool unitFound = false; - for ( const CUnit& unit : m_allUnits ) + // Attempt to find the unit + for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { - if ( unit.getID() == unitID ) + if ( pair.unit.getID() == unitID ) { - unitFound = true; + ret = 0; break; } } // for now, as long as the unit exists we can attempt the order - return unitFound; + return ret; } - // Get unit by unit ID -const CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) const +const CUnit& CTTRTSGame::GetUnitByIDConst( unit_id_t id ) const { - CUnitVector::const_iterator it; + CUnitVector::iterator it; - for ( const CUnit& unit : m_allUnits ) + for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { // Attempt the unit add - if ( unit.getID() ) - return unit; + if ( pair.unit.getID() ) + return pair.unit; + } + + // Return an invalid unit + static CUnit invalid_unit; + return invalid_unit; +} + +// Get unit by unit ID +CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) +{ + CUnitVector::iterator it; + + for ( OrderUnitPair& pair : m_OrderUnitPairs ) + { + // Attempt the unit add + if ( pair.unit.getID() ) + return pair.unit; } // Return an invalid unit diff --git a/game/game.h b/game/game.h index c13024e..2ea176a 100644 --- a/game/game.h +++ b/game/game.h @@ -12,8 +12,27 @@ typedef std::vector< CUnit > CUnitVector; // Type for order and unit pairs struct OrderUnitPair { - const COrder& order; - const CUnit& unit; + // Straight up move constructor + OrderUnitPair( OrderUnitPair&& other ) + : unit ( std::move(other.unit) ) + , order ( other.order ) + { + + } + + // Multi parameter constructor + OrderUnitPair( CUnit&& u, COrder o ) + : unit ( std::move(u) ) + , order ( o ) + { + + } + + // Move asignment operator + inline OrderUnitPair& operator=( OrderUnitPair&& rhs ) { *this = std::move(rhs); return *this; } + + CUnit unit; + COrder order; }; typedef std::vector< OrderUnitPair > OrderUnitPairVector; @@ -48,36 +67,42 @@ public: int AddUnits( CUnitVector&& units ); // Get the number of units - inline unsigned int GetNumUnits() const { return m_allUnits.size(); } + inline unsigned int GetNumUnits() const { return m_OrderUnitPairs.size(); } // Get unit by index as above (not unit ID) - inline const CUnit& GetUnitByIndex( unsigned int i ) const { return m_allUnits[i]; } + inline const CUnit& GetUnitByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].unit; } // Get unit by unit ID - const CUnit& GetUnitByID( unit_id_t id ) const; - - // Get the number of order - inline unsigned int GetNumOrders() const { return m_orders.size(); } + const CUnit& GetUnitByIDConst( unit_id_t id ) const; // Get orders by index as above - inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_orders[i]; } + inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].order; } // Get dimentions inline const uvector2& GetDimentions() const { return dimentions; } private: - // Verify any order + // Verify any order - non-zero is error int VerifyOrder( player_id_t player, const COrder& order ) const; - // Verify any order + // Verify any order - non-zero is error int VerifyUnit( const CUnit& unit ) const; - // Vector to store points to all units - CUnitVector m_allUnits; + // Verify Position - non-zero is error + int VerifyPos( uvector2 vec ) const; - // Orders to execute this turn - COrderVector m_orders; + // Get a units new position after an order + uvector2 GetNewPosition( const OrderUnitPair& pair ) const; + + // Get unit by unit ID + CUnit& GetUnitByID( unit_id_t id ); + + // Vector to store points to all units + OrderUnitPairVector m_OrderUnitPairs; + + // List of dead units + CUnitVector m_deadUnits; // Dimensions of the game uvector2 dimentions; diff --git a/game/orders.h b/game/orders.h index 400f2b3..4961af7 100644 --- a/game/orders.h +++ b/game/orders.h @@ -14,7 +14,8 @@ enum class order_c : char F = 'F', L = 'L', R = 'R', - A = 'A' + A = 'A', + INVALID }; // Movement orders @@ -34,6 +35,13 @@ static const order_c sk_actionOrders[] = // Container for an order struct COrder { + COrder() + : unit ( unit_id_invalid ) + , order ( order_c::INVALID ) + { + + } + // Unit order is for unit_id_t unit; diff --git a/game/unit.cpp b/game/unit.cpp index 30bc711..d70d0a7 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -36,6 +36,7 @@ CUnit::CUnit() , player_id ( player_id_invalid ) , unit_vis ( unitVis_invalid ) , dir ( dir_t::S ) +, pos ( { ucoord_invalid, ucoord_invalid } ) { updateMyVisual(); }; @@ -47,6 +48,7 @@ CUnit::CUnit(CUnit&& unit) , player_id ( std::move(unit.player_id) ) , unit_vis ( std::move(unit.unit_vis) ) , dir ( std::move(unit.dir) ) +, pos ( std::move(unit.pos) ) { updateMyVisual(); } @@ -81,7 +83,7 @@ bool CUnit::setFromVisual( const unitVis_c& vis ) { if( it->second == vis ) { - dir == it->first; + dir = it->first; updateMyVisual(); return true; } @@ -90,3 +92,91 @@ bool CUnit::setFromVisual( const unitVis_c& vis ) // No matching direction to visual return false; } + +// Turn unit left +dir_t CUnit::turnLeft() +{ + switch( dir ) + { + case dir_t::N: + dir = dir_t::W; + break; + + case dir_t::E: + dir = dir_t::N; + break; + + case dir_t::S: + dir = dir_t::E; + break; + + case dir_t::W: + dir = dir_t::S; + break; + } + + updateMyVisual(); + + return getDir(); +} + +// Turn unit right +dir_t CUnit::turnRight() +{ + switch( dir ) + { + case dir_t::N: + dir = dir_t::E; + break; + + case dir_t::E: + dir = dir_t::S; + break; + + case dir_t::S: + dir = dir_t::W; + break; + + case dir_t::W: + dir = dir_t::N; + break; + } + + updateMyVisual(); + + return getDir(); +} + +// Turn unit around +dir_t CUnit::turnAround() +{ + switch( dir ) + { + case dir_t::N: + dir = dir_t::S; + break; + + case dir_t::E: + dir = dir_t::W; + break; + + case dir_t::S: + dir = dir_t::N; + break; + + case dir_t::W: + dir = dir_t::E; + break; + } + + updateMyVisual(); + + return getDir(); +} + +// Get the co-ordinate infront of the unit +uvector2 CUnit::getInFront() const +{ + vector2 delta = vecFromDir(dir); + return pos + delta; +} diff --git a/game/unit.h b/game/unit.h index cc211c8..ebd5852 100644 --- a/game/unit.h +++ b/game/unit.h @@ -13,22 +13,28 @@ class CUnit public: CUnit(); - CUnit(CUnit&& unit); + CUnit(CUnit&& unit); + CUnit& operator=(CUnit&& unit) { *this = std::move(unit); return *this; } ~CUnit() = default; inline const unit_id_t& getID() const { return unit_id; } inline const team_id_t& getTeam() const { return team_id; } inline const player_id_t& getPlayer() const { return player_id; } inline const unitVis_c& getVisual() const { return unit_vis; } + inline const dir_t& getDir() const { return dir; } // Return non-zero values on error - inline int setTeam(const team_id_t& v) { return (v == team_id_invalid) ? -1 : (( team_id = v ), 0); } - inline int setPlayer(const player_id_t& v) { return (v == player_id_invalid) ? -1 : (( player_id = v ), 0); } - inline int setVisual(const unitVis_c& v) { return (v == unitVis_invalid) ? -1 : (( unit_vis = v ), 0); } + inline int setTeam(const team_id_t& v) { return (v == team_id_invalid) ? -1 : (( team_id = v ), 0); } + inline int setPlayer(const player_id_t& v) { return (v == player_id_invalid) ? -1 : (( player_id = v ), 0); } + inline int setVisual(const unitVis_c& v) { return (v == unitVis_invalid) ? -1 : (( unit_vis = v ), 0); } + inline dir_t setDir(const dir_t& v) { return (dir = v); } inline const uvector2& getPos() const { return pos; } inline void setPos(const uvector2& v) { pos = v; } + // Get the co-ordinate infront of the unit + uvector2 getInFront() const; + // Check unit is valid inline bool valid() const; @@ -38,6 +44,10 @@ public: // Factory function for creating units from a visual static CUnit getUnitFromVis( unitVis_c vis ); + dir_t turnLeft(); + dir_t turnRight(); + dir_t turnAround(); + protected: diff --git a/maths/vector2.h b/maths/vector2.h index fe00dec..9be4019 100644 --- a/maths/vector2.h +++ b/maths/vector2.h @@ -3,16 +3,73 @@ #include "mathtypes.h" +struct uvector2; + struct vector2 { coord_t x; coord_t y; + + inline vector2 operator-() const { return { (coord_t)-x, (coord_t)-y }; } + + inline operator uvector2() const; + + // vec + inline vector2 operator+(const vector2& rhs) const { return { (coord_t)(rhs.x + x) , (coord_t)(rhs.y + y) }; } + inline vector2 operator-(const vector2& rhs) const { return *this + (-rhs); } + + inline const vector2& operator+=(const vector2& rhs) { *this = *this+rhs; return *this; } + inline const vector2& operator-=(const vector2& rhs) { return *this+=(-rhs); } + + inline bool operator==(const vector2& rhs) const { return ( rhs.x == x) && ( rhs.y == y); } + inline bool operator!=(const vector2& rhs) const { return !(*this==rhs); } }; struct uvector2 { ucoord_t x; ucoord_t y; + + // Implicit conversion to vector 2 if needed + inline operator vector2() const { return { (coord_t)x, (coord_t)y }; } + + inline uvector2 operator-() const { return { (ucoord_t)-x, (ucoord_t)-y }; } + + // uvec + inline uvector2 operator+(const uvector2& rhs) const { return { (ucoord_t)(rhs.x + x) , (ucoord_t)(rhs.y + y) }; } + inline uvector2 operator-(const uvector2& rhs) const { return *this + (-rhs); } + + inline const uvector2& operator+=(const uvector2& rhs) { *this = *this+rhs; return *this; } + inline const uvector2& operator-=(const uvector2& rhs) { return *this+=(-rhs); } + + inline bool operator==(const uvector2& rhs) const { return ( rhs.x == x) && ( rhs.y == y); } + inline bool operator!=(const uvector2& rhs) const { return !(*this==rhs); } }; -#endif //_VECTOR2_H_ \ No newline at end of file +inline vector2::operator uvector2() const { return { (ucoord_t)x, (ucoord_t)y }; } + +inline vector2 vecFromDir( dir_t dir ) +{ + switch( dir ) + { + case dir_t::N: + return { 0,1 }; + break; + + case dir_t::E: + return { 1,0 }; + break; + + case dir_t::S: + return { 0,-1 }; + break; + + case dir_t::W: + return { -1,0 }; + break; + } + + return { 0,0 }; +} + +#endif //_VECTOR2_H_ diff --git a/test/test.cpp b/test/test.cpp index c4b5864..b236270 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -68,13 +68,34 @@ const char* tests() if( game.SimulateToNextTurn() ) return "Failed to simulate a blank game"; - if( game.GetNumOrders() ) - return "Game started with non-zero order number"; - - if( game.GetNumUnits() ) - return "Game started with non-zero unit number"; + if( game.GetNumUnits() ) + return "Game started with non-zero unit number"; } + { + CTTRTSGame game( 5, 5 ); + + CUnit unit = CUnit::getUnitFromVis('>'); + const unit_id_t id = unit.getID(); + COrder order; + + unit.setPos( {2,2} ); + unit.setPlayer(0); + + game.AddUnit(std::move(unit)); + + order.unit = id; + order.order = order_c::F; + + game.IssueOrder(0,order); + + game.SimulateToNextTurn(); + + if( game.GetUnitByIDConst(id).getPos() != uvector2{3,2} ) + return "Simple movement order failed"; + + } + return nullptr; } From 9b000300390e1418d3407273431613683038b0f3 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:01 +0000 Subject: [PATCH 040/190] More tests --- game/game.cpp | 6 ++++++ test/test.cpp | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 701d6ed..8cf47d3 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -210,9 +210,15 @@ int CTTRTSGame::AddUnit( CUnit&& unit ) if( (pos.x >= dimentions.x) || (pos.y >= dimentions.y) ) return 1; + for ( const OrderUnitPair& pair: m_OrderUnitPairs ) + { + if( pair.unit.getPos() == unit.getPos() ) + return 2; + } // Add the unit with a blank order m_OrderUnitPairs.push_back( OrderUnitPair(std::move(unit), COrder()) ); + return 0; } diff --git a/test/test.cpp b/test/test.cpp index b236270..beb65bd 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -82,18 +82,47 @@ const char* tests() unit.setPos( {2,2} ); unit.setPlayer(0); - game.AddUnit(std::move(unit)); + if ( game.AddUnit(std::move(unit)) ) + return "Game failed to add valid unit"; order.unit = id; order.order = order_c::F; - game.IssueOrder(0,order); + if( game.IssueOrder(0,order) ) + return "Game failed to issue valid order"; - game.SimulateToNextTurn(); + if (game.SimulateToNextTurn() ) + return "Game failed to simulate valid turn"; if( game.GetUnitByIDConst(id).getPos() != uvector2{3,2} ) return "Simple movement order failed"; + } + + { + CTTRTSGame game( 5, 5 ); + + { + CUnit unit = CUnit::getUnitFromVis('^'); + unit.setPos( {2,2} ); + unit.setPlayer(0); + + game.AddUnit(std::move(unit)); + } + + { + CUnit unit = CUnit::getUnitFromVis('^'); + unit.setPos( {2,2} ); + unit.setPlayer(0); + + if( !game.AddUnit(std::move(unit)) ) + return "Game should have rejected unit placed on the same spot"; + + if( game.GetNumUnits() != 1 ) + return "Game ended up with too many units"; + } + + } return nullptr; From 7e2fd0a8d1b3cfb985450d56d569eab96b2fb56c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 041/190] Fix a few more bugs and implement more tests Bug in infinite recursion on move assignment operator. Bug in adding the wron units ID to the kill list. --- game/game.cpp | 2 +- game/game.h | 2 +- game/unit.cpp | 12 +++++++++ game/unit.h | 4 +-- test/test.cpp | 68 +++++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 8cf47d3..aff5fb8 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -141,7 +141,7 @@ int CTTRTSGame::SimulateToNextTurn() // if the unit is infront of our unit, then add it to the kill list if( pair2.unit.getPos() == infront ) { - toKill.push_back(pair.unit.getID()); + toKill.push_back(pair2.unit.getID()); pair.order = COrder(); break; } diff --git a/game/game.h b/game/game.h index 2ea176a..e774867 100644 --- a/game/game.h +++ b/game/game.h @@ -29,7 +29,7 @@ struct OrderUnitPair } // Move asignment operator - inline OrderUnitPair& operator=( OrderUnitPair&& rhs ) { *this = std::move(rhs); return *this; } + inline OrderUnitPair& operator=( OrderUnitPair&& rhs ) { this->unit = std::move(rhs.unit);this->order = rhs.order;rhs.order = COrder(); return *this; } CUnit unit; COrder order; diff --git a/game/unit.cpp b/game/unit.cpp index d70d0a7..3d8af12 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -53,6 +53,18 @@ CUnit::CUnit(CUnit&& unit) updateMyVisual(); } + +CUnit& CUnit::operator=(CUnit&& unit) +{ + unit_id = std::move(unit.unit_id) ; + team_id = std::move(unit.team_id) ; + player_id = std::move(unit.player_id) ; + unit_vis = std::move(unit.unit_vis) ; + dir = std::move(unit.dir) ; + pos = std::move(unit.pos) ; + return *this; +} + CUnit CUnit::getUnitFromVis( unitVis_c vis ) { CUnit unit; diff --git a/game/unit.h b/game/unit.h index ebd5852..41d157a 100644 --- a/game/unit.h +++ b/game/unit.h @@ -14,7 +14,7 @@ public: CUnit(); CUnit(CUnit&& unit); - CUnit& operator=(CUnit&& unit) { *this = std::move(unit); return *this; } + CUnit& operator=(CUnit&& unit); ~CUnit() = default; inline const unit_id_t& getID() const { return unit_id; } @@ -58,7 +58,7 @@ private: unitVis_c updateMyVisual(); // Unit ID - const unit_id_t unit_id; + unit_id_t unit_id; // Visual unitVis_c unit_vis; diff --git a/test/test.cpp b/test/test.cpp index beb65bd..96ac285 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -70,7 +70,31 @@ const char* tests() if( game.GetNumUnits() ) return "Game started with non-zero unit number"; - } + } + + { + CTTRTSGame game( 5, 5 ); + + { + CUnit unit = CUnit::getUnitFromVis('^'); + unit.setPos( {2,2} ); + unit.setPlayer(0); + + game.AddUnit(std::move(unit)); + } + + { + CUnit unit = CUnit::getUnitFromVis('^'); + unit.setPos( {2,2} ); + unit.setPlayer(0); + + if( !game.AddUnit(std::move(unit)) ) + return "Game should have rejected unit placed on the same spot"; + + if( game.GetNumUnits() != 1 ) + return "Game ended up with too many units"; + } + } { CTTRTSGame game( 5, 5 ); @@ -100,31 +124,49 @@ const char* tests() } { - CTTRTSGame game( 5, 5 ); + CTTRTSGame game( 2, 1 ); + unit_id_t id; { - CUnit unit = CUnit::getUnitFromVis('^'); - unit.setPos( {2,2} ); + CUnit unit = CUnit::getUnitFromVis('>'); + id = unit.getID(); + COrder order; + + unit.setPos( {0,0} ); unit.setPlayer(0); - game.AddUnit(std::move(unit)); - } + if ( game.AddUnit(std::move(unit)) ) + return "Game failed to add valid unit"; + order.unit = id; + order.order = order_c::A; + + if( game.IssueOrder(0,order) ) + return "Game failed to issue valid order"; + } { - CUnit unit = CUnit::getUnitFromVis('^'); - unit.setPos( {2,2} ); - unit.setPlayer(0); + CUnit unit = CUnit::getUnitFromVis('<'); - if( !game.AddUnit(std::move(unit)) ) - return "Game should have rejected unit placed on the same spot"; + unit.setPos( {1,0} ); + unit.setPlayer(1); - if( game.GetNumUnits() != 1 ) - return "Game ended up with too many units"; + if ( game.AddUnit(std::move(unit)) ) + return "Game failed to add valid unit"; } + game.SimulateToNextTurn(); + if ( game.GetNumUnits() != 1 ) + return "Game failed to kill a unit when it logically should have"; + + if ( game.GetUnitByIndex(0).getDir() != dir_t::E ) + return "Game killed the wrong unit"; + + if ( game.GetUnitByIndex(0).getID() != id ) + return "Game killed the wrong unit"; } + return nullptr; } From 50ed84c1bde34b46e2e9ec1f494d147b236e048c Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 042/190] Comment all the tests --- test/test.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/test/test.cpp b/test/test.cpp index 96ac285..2df8aeb 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -6,19 +6,15 @@ const char* tests() { - { - CBoard board = CBoard(10,5); - board.clear(); - board.fill(48); - } - + // Test if we can properly set a unit's visual { CUnit unit; unit.setFromVisual('v'); - if( unit.getVisual() != 118 ) + if( unit.getVisual() != 'v' ) return "failed to properly create V unit"; } + // Test unique unit IDs { CUnit unit; CUnit unit2; @@ -26,12 +22,14 @@ const char* tests() return "Unit IDs the same"; } + // Test if we can successfully create a unit from a visual { CUnit unit = CUnit::getUnitFromVis('v'); if( unit.getVisual() != 'v' ) return "failed to properly create V unit with factory"; } + // Test if we can successfully convert orders back and forth { COrder order; order.order = order_c::F; @@ -43,6 +41,7 @@ const char* tests() return "failed order string conversion test"; } + // Test if movement order is correctly recognised { COrder order; order.order = order_c::F; @@ -53,6 +52,7 @@ const char* tests() return "Wrongly detected an action order"; } + // Test if Attack order is correctly recognised { COrder order; order.order = order_c::A; @@ -63,6 +63,7 @@ const char* tests() return "Wrongly detected an movement order"; } + // Test of the game can logically handle a blank game { CTTRTSGame game( 15, 10 ); if( game.SimulateToNextTurn() ) @@ -72,6 +73,7 @@ const char* tests() return "Game started with non-zero unit number"; } + // Test if the game correctly rejects units placed ontop of others { CTTRTSGame game( 5, 5 ); @@ -96,6 +98,7 @@ const char* tests() } } + // Test on a small board if a movement command succeeds correctly { CTTRTSGame game( 5, 5 ); @@ -123,6 +126,7 @@ const char* tests() } + // Test on a tiny board, whether a unit can correctly attack another { CTTRTSGame game( 2, 1 ); @@ -166,8 +170,7 @@ const char* tests() return "Game killed the wrong unit"; } - - return nullptr; + return 0; } // Main program entry point From 99cd93ae22e8130ec14fd7082c610f86881febbe Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 043/190] Turn on all warnings, except reorder, and fix remaining warnings --- CMakeLists.txt | 2 +- game/game.cpp | 7 +++++++ game/orders.cpp | 2 +- game/unit.cpp | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 014ed35..7666aaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required( VERSION 2.8.7 ) -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -Wall -Wno-reorder" ) if( CMAKE_BUILD_TYPE MATCHES "Debug" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g" ) diff --git a/game/game.cpp b/game/game.cpp index aff5fb8..e9d5544 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -73,6 +73,9 @@ uvector2 CTTRTSGame::GetNewPosition( const OrderUnitPair& pair ) const { case order_c::F: return pair.unit.getInFront(); + break; + default: + break; } return { ucoord_invalid,ucoord_invalid }; @@ -118,6 +121,8 @@ int CTTRTSGame::SimulateToNextTurn() } } break; + default: + break; } } @@ -160,6 +165,8 @@ int CTTRTSGame::SimulateToNextTurn() pair.order = COrder(); } break; + default: + break; } } diff --git a/game/orders.cpp b/game/orders.cpp index 96606e9..5be3dda 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -19,7 +19,7 @@ COrder GetOrderFromString( const std::string& _order ) std::string order = _order; COrder ret; - int pos = order.find(ORDER_DELIMITER); + size_t pos = order.find(ORDER_DELIMITER); if( pos != std::string::npos ) { const std::string order_unit = order.substr(0, pos); diff --git a/game/unit.cpp b/game/unit.cpp index 3d8af12..7eead1a 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -33,8 +33,8 @@ namespace CUnit::CUnit() : unit_id ( get_unique_unit_id() ) , team_id ( team_id_invalid ) -, player_id ( player_id_invalid ) , unit_vis ( unitVis_invalid ) +, player_id ( player_id_invalid ) , dir ( dir_t::S ) , pos ( { ucoord_invalid, ucoord_invalid } ) { From 005896b13cf0a8878a43972cd2d0ca3c82b8e478 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 044/190] Add comments and clean up game.cpp --- game/game.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index e9d5544..ef0f4f2 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -6,19 +6,26 @@ int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) { COrderVector orderVector; - std::string orders = _orders; + // Copy the const orders into a buffer we can edit + std::string orders = _orders; + + // Find a line end size_t pos; - while ( (pos = orders.find("\n")) != std::string::npos ) + while ( (pos = orders.find('\n')) != std::string::npos ) { + // Grab the string up to the line end const std::string sorder = orders.substr(0, pos); + + // Erase all of string up to and including the line end orders.erase(0,pos+1); + // Create an order from the string and push it back COrder order = GetOrderFromString( sorder ); - orderVector.push_back(order); } + // Call our add order by vector method return IssueOrders(player,orderVector); } @@ -28,6 +35,7 @@ int CTTRTSGame::IssueOrders( player_id_t player, const COrderVector& orders ) // verify all the orders for ( auto order : orders ) { + // If any order returns non-zero, back out if ( IssueOrder(player,order) ) return 1; } @@ -38,9 +46,11 @@ int CTTRTSGame::IssueOrders( player_id_t player, const COrderVector& orders ) // Issue a single order int CTTRTSGame::IssueOrder( player_id_t player, const COrder& order ) { + // Verify the order if ( VerifyOrder(player,order) ) return 1; + // Get the right unit for the order for ( OrderUnitPair& pair : m_OrderUnitPairs ) { if ( pair.unit.getID() == order.unit ) @@ -50,12 +60,14 @@ int CTTRTSGame::IssueOrder( player_id_t player, const COrder& order ) } } - return 1; + // Unit was not found, return 2 + return 2; } // Verify a position int CTTRTSGame::VerifyPos(uvector2 vec) const { + // Simply check if within the bounds of our dimensions for now if ( ( vec.x >= dimentions.x ) || ( vec.y >= dimentions.y ) ) { @@ -69,16 +81,18 @@ int CTTRTSGame::VerifyPos(uvector2 vec) const // Get a units new position uvector2 CTTRTSGame::GetNewPosition( const OrderUnitPair& pair ) const { + + // Grab the order switch ( pair.order.order ) { + // For forward orders, grab in front case order_c::F: return pair.unit.getInFront(); break; + // For all other orders, just grab the old position default: - break; + return pair.unit.getPos(); } - - return { ucoord_invalid,ucoord_invalid }; } // Simulate and progress to the next turn @@ -155,12 +169,14 @@ int CTTRTSGame::SimulateToNextTurn() break; case order_c::L: { + // Simply turn left pair.unit.turnLeft(); pair.order = COrder(); } break; case order_c::R: { + // Simply turn right pair.unit.turnRight(); pair.order = COrder(); } @@ -217,11 +233,13 @@ int CTTRTSGame::AddUnit( CUnit&& unit ) if( (pos.x >= dimentions.x) || (pos.y >= dimentions.y) ) return 1; + // If any unit's position matches, reject this for ( const OrderUnitPair& pair: m_OrderUnitPairs ) { if( pair.unit.getPos() == unit.getPos() ) return 2; } + // Add the unit with a blank order m_OrderUnitPairs.push_back( OrderUnitPair(std::move(unit), COrder()) ); @@ -256,6 +274,7 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) const // Attempt to find the unit for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { + // Accept if we have the unit if ( pair.unit.getID() == unitID ) { ret = 0; @@ -270,8 +289,6 @@ int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) const // Get unit by unit ID const CUnit& CTTRTSGame::GetUnitByIDConst( unit_id_t id ) const { - CUnitVector::iterator it; - for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { // Attempt the unit add @@ -287,8 +304,6 @@ const CUnit& CTTRTSGame::GetUnitByIDConst( unit_id_t id ) const // Get unit by unit ID CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) { - CUnitVector::iterator it; - for ( OrderUnitPair& pair : m_OrderUnitPairs ) { // Attempt the unit add From 4c439a383efd508b3e04d978977f3b42c49b9f13 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 045/190] More cleanup and remove of unneeded functions --- game/game.h | 18 ++++++------------ game/orders.cpp | 22 ---------------------- game/orders.h | 22 ++-------------------- game/unit.cpp | 5 ++++- game/unit.h | 22 +++++++++++++++------- test/test.cpp | 22 ---------------------- 6 files changed, 27 insertions(+), 84 deletions(-) diff --git a/game/game.h b/game/game.h index e774867..9142265 100644 --- a/game/game.h +++ b/game/game.h @@ -5,10 +5,6 @@ #include "gametypes.h" #include "orders.h" -#include // unique_ptr and shared_ptr - -typedef std::vector< CUnit > CUnitVector; - // Type for order and unit pairs struct OrderUnitPair { @@ -16,25 +12,22 @@ struct OrderUnitPair OrderUnitPair( OrderUnitPair&& other ) : unit ( std::move(other.unit) ) , order ( other.order ) - { - - } + {} // Multi parameter constructor OrderUnitPair( CUnit&& u, COrder o ) : unit ( std::move(u) ) , order ( o ) - { - - } + {} // Move asignment operator inline OrderUnitPair& operator=( OrderUnitPair&& rhs ) { this->unit = std::move(rhs.unit);this->order = rhs.order;rhs.order = COrder(); return *this; } - CUnit unit; - COrder order; + CUnit unit; // The unit + COrder order; // Order for this unit from this turn }; +// Typedef for a vector of these unit pairs typedef std::vector< OrderUnitPair > OrderUnitPairVector; class CTTRTSGame @@ -54,6 +47,7 @@ public: int IssueOrders( player_id_t player, const std::string& orders ); int IssueOrders( player_id_t player, const COrderVector& orders ); + // Issue a single order, returns non-zero for rejection int IssueOrder( player_id_t player, const COrder& order ); // Simulate and progress to the next turn diff --git a/game/orders.cpp b/game/orders.cpp index 5be3dda..514ee51 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -35,25 +35,3 @@ COrder GetOrderFromString( const std::string& _order ) return ret; } - -bool isMovementOrder( const COrder& order ) -{ - const order_c c = order.order; - for ( unsigned int i = 0; i < _countof(sk_movementOrders); i++ ) - { - if ( c == sk_movementOrders[i] ) - return true; - } - return false; -} - -bool isActionOrder( const COrder& order ) -{ - const order_c c = order.order; - for ( unsigned int i = 0; i < _countof(sk_actionOrders); i++ ) - { - if ( c == sk_actionOrders[i] ) - return true; - } - return false; -} diff --git a/game/orders.h b/game/orders.h index 4961af7..28ee471 100644 --- a/game/orders.h +++ b/game/orders.h @@ -18,29 +18,14 @@ enum class order_c : char INVALID }; -// Movement orders -static const order_c sk_movementOrders[] = -{ - order_c::F, // Forward -}; - -// Action orders -static const order_c sk_actionOrders[] = -{ - order_c::L, // Left - order_c::R, // Right - order_c::A, // Attack -}; - // Container for an order struct COrder { + // Base constructor makes invalid order COrder() : unit ( unit_id_invalid ) , order ( order_c::INVALID ) - { - - } + {} // Unit order is for unit_id_t unit; @@ -67,7 +52,4 @@ typedef std::vector COrderVector; std::string GetStringFromOrder(const COrder& order ); COrder GetOrderFromString( const std::string& order ); -bool isMovementOrder( const COrder& order ); -bool isActionOrder( const COrder& order ); - #endif //_ORDERS_H_ diff --git a/game/unit.cpp b/game/unit.cpp index 7eead1a..b69d36d 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -39,7 +39,7 @@ CUnit::CUnit() , pos ( { ucoord_invalid, ucoord_invalid } ) { updateMyVisual(); -}; +} // Move constructor CUnit::CUnit(CUnit&& unit) @@ -54,6 +54,7 @@ CUnit::CUnit(CUnit&& unit) } +// Move asignment operator CUnit& CUnit::operator=(CUnit&& unit) { unit_id = std::move(unit.unit_id) ; @@ -65,6 +66,7 @@ CUnit& CUnit::operator=(CUnit&& unit) return *this; } +// Get a unit from a visual CUnit CUnit::getUnitFromVis( unitVis_c vis ) { CUnit unit; @@ -87,6 +89,7 @@ unitVis_c CUnit::updateMyVisual() return getVisual(); } +// Set the unit from visual bool CUnit::setFromVisual( const unitVis_c& vis ) { dir_to_vis_map::const_iterator it; diff --git a/game/unit.h b/game/unit.h index 41d157a..346131d 100644 --- a/game/unit.h +++ b/game/unit.h @@ -2,7 +2,7 @@ #define _UNIT_H_ #include -#include +#include #include "gametypes.h" #include "vector2.h" @@ -12,11 +12,17 @@ class CUnit { public: + // Constructor CUnit(); + + // Move constructor and move asignement. CUnit cannot be copied CUnit(CUnit&& unit); CUnit& operator=(CUnit&& unit); + + // Default dtor ~CUnit() = default; + // Getters for all the members inline const unit_id_t& getID() const { return unit_id; } inline const team_id_t& getTeam() const { return team_id; } inline const player_id_t& getPlayer() const { return player_id; } @@ -27,6 +33,8 @@ public: inline int setTeam(const team_id_t& v) { return (v == team_id_invalid) ? -1 : (( team_id = v ), 0); } inline int setPlayer(const player_id_t& v) { return (v == player_id_invalid) ? -1 : (( player_id = v ), 0); } inline int setVisual(const unitVis_c& v) { return (v == unitVis_invalid) ? -1 : (( unit_vis = v ), 0); } + + // Set unit direction inline dir_t setDir(const dir_t& v) { return (dir = v); } inline const uvector2& getPos() const { return pos; } @@ -44,18 +52,15 @@ public: // Factory function for creating units from a visual static CUnit getUnitFromVis( unitVis_c vis ); + // Orientation methods dir_t turnLeft(); dir_t turnRight(); dir_t turnAround(); - -protected: - - private: - // Update the visual of V - unitVis_c updateMyVisual(); + // Update my visual must be called when setting direction + unitVis_c updateMyVisual(); // Unit ID unit_id_t unit_id; @@ -76,6 +81,9 @@ private: uvector2 pos; }; +// Typedef for a vector of units +typedef std::vector< CUnit > CUnitVector; + // Simple validation inline bool CUnit::valid() const { diff --git a/test/test.cpp b/test/test.cpp index 2df8aeb..c506e68 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -41,28 +41,6 @@ const char* tests() return "failed order string conversion test"; } - // Test if movement order is correctly recognised - { - COrder order; - order.order = order_c::F; - if (!isMovementOrder(order) ) - return "Failed to detect a movement order"; - - if (isActionOrder(order) ) - return "Wrongly detected an action order"; - } - - // Test if Attack order is correctly recognised - { - COrder order; - order.order = order_c::A; - if (! isActionOrder(order) ) - return "Failed to detect a action order"; - - if (isMovementOrder(order) ) - return "Wrongly detected an movement order"; - } - // Test of the game can logically handle a blank game { CTTRTSGame game( 15, 10 ); From 7a88b5eb836e0a978c15cc498d17fee3f034b5f5 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 046/190] Remove straggling sublime project --- .gitignore | 4 ++-- ttrts.sublime-project | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 ttrts.sublime-project diff --git a/.gitignore b/.gitignore index 7ccaa71..cfa3d66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ build -.user -.sublime-workspace +*.user +*.sublime* diff --git a/ttrts.sublime-project b/ttrts.sublime-project deleted file mode 100644 index 796f40a..0000000 --- a/ttrts.sublime-project +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": - [ - { - "path": "/home/mdiluzio/Projects/ttrts" - } - ] -} From 43a2c58c56818b1e8c11521397a69156fcbc3490 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 047/190] Ignore IDEA files as well --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cfa3d66..03c1bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build *.user *.sublime* +.idea From 7a75fbd369686fed18f8b82caa45c32bc8db4b05 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 048/190] Clean up CXX Flags and remove statements relating to us being cool. If you have to say it, it ain't true --- CMakeLists.txt | 9 +++++++- README.md | 4 ++-- game/CMakeLists.txt | 3 --- game/README.md | 8 +++---- game/game.cpp | 53 +++++++++++++++++++++++++++++++++++++------- game/game.h | 16 +++++++------ game/gametypes.h | 16 +++++++------ game/orders.cpp | 2 +- game/orders.h | 4 ++-- game/unit.cpp | 2 +- game/unit.h | 13 +++++------ maths/vector2.h | 19 +++++++++++----- test/CMakeLists.txt | 3 --- test/test.cpp | 12 ++++++++-- ttrts/CMakeLists.txt | 3 --- 15 files changed, 110 insertions(+), 57 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7666aaa..bf5de03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,13 @@ cmake_minimum_required( VERSION 2.8.7 ) -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -Wall -Wno-reorder" ) +# Use c++1y (14) +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++1y" ) + +# Turn on all warnings +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-reorder" ) + +# Turn off reorder warnings as they're kind of irrelevant +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder" ) if( CMAKE_BUILD_TYPE MATCHES "Debug" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g" ) diff --git a/README.md b/README.md index da359ed..4c2c2a1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ We aim to create a simple terminal based rts where a user can program an AI to c 5. repeat until an end state is reached 4. once game is finished, host and clients disconnect and a winner is notified -*see [the game directory](game) for full game rules* +*see [game](game) for full game rules* ------------------------------------------------------------------------------------ ## Source @@ -48,7 +48,7 @@ Wrapper for user interface for the terminal, this only really needs three stages * Initialise the game with settings and connect the clients * Run the game simulation to it's conclusion * Display the game result -* Acsii Colour wrapper for separate teams +* ASCII Colour wrapper for separate teams ##### maths simple maths library for 2D calculations and types diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 79b8f0a..56140be 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -7,9 +7,6 @@ include_directories( ../maths ) -# Set to use c++11, because we're cool like that -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) - # Add the sources set( SOURCES game.cpp diff --git a/game/README.md b/game/README.md index 53a2077..f04313e 100644 --- a/game/README.md +++ b/game/README.md @@ -1,13 +1,13 @@ ttrts Game Design ================= -The game takes place in a series of simultanious turns on an arbitrarilly sized 2D board. +The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board. -Each player is in control of a set number of starting units, each turn recieves data on the status of the board. +Each player is in control of a set number of starting units, each turn receives data on the status of the board. -Each player must then issue a single command to each unit in thier control. +Each player must then issue a single command to each unit in their control. -The engine then takes all commands, evaluates all movement first simultaniously, then all other commands. +The engine then takes all commands, evaluates all movement first simultaneously, then all other commands. All attempted movement to the same square by two or more units will fail. diff --git a/game/game.cpp b/game/game.cpp index ef0f4f2..8f4e988 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1,6 +1,7 @@ #include "game.h" #include +#include // Interpret a string of orders int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) @@ -68,8 +69,8 @@ int CTTRTSGame::IssueOrder( player_id_t player, const COrder& order ) int CTTRTSGame::VerifyPos(uvector2 vec) const { // Simply check if within the bounds of our dimensions for now - if ( ( vec.x >= dimentions.x ) - || ( vec.y >= dimentions.y ) ) + if ( ( vec.x >= dimensions.x ) + || ( vec.y >= dimensions.y ) ) { return 1; } @@ -224,20 +225,19 @@ int CTTRTSGame::SimulateToNextTurn() int CTTRTSGame::AddUnit( CUnit&& unit ) { // Verify the unit - const int val = unit.valid(); - if( val ) - return val; + if( !unit.valid() ) + return 1; // Verify if the unit can be placed on the current board const uvector2 pos = unit.getPos(); - if( (pos.x >= dimentions.x) || (pos.y >= dimentions.y) ) - return 1; + if( (pos.x >= dimensions.x) || (pos.y >= dimensions.y) ) + return 2; // If any unit's position matches, reject this for ( const OrderUnitPair& pair: m_OrderUnitPairs ) { if( pair.unit.getPos() == unit.getPos() ) - return 2; + return 3; } // Add the unit with a blank order @@ -315,3 +315,40 @@ CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) static CUnit invalid_unit; return invalid_unit; } + +// Check if we have a win state +Team CTTRTSGame::CheckForWin() const +{ + // Array of units for each Team + unsigned int units[(int) Team::NUM_INVALID]; + memset(units,0,sizeof(units)); + + // Count up all the units for each Team + for ( const OrderUnitPair& pair : m_OrderUnitPairs ) + { + const int team = (int)pair.unit.getTeam(); + units[team] += 1; + } + + // Default winning Team to invalid (no win) + Team winningTeam = Team::NUM_INVALID; + + // For each of the teams + for ( unsigned int i = 0; i < _countof(units); i++ ) + { + // if there are still units in this Team, and the winning Team hasn't been set + if( units[i] > 0 && winningTeam == Team::NUM_INVALID ) + { + winningTeam = (Team)i; + } + // Otherwise, if there are units in this Team and the winning Team HAS been set + else if ( units[i] > 0 ) + { + // Set back to invalid and break out of the loop + winningTeam = Team::NUM_INVALID; + break; + } + } + + return winningTeam; +} \ No newline at end of file diff --git a/game/game.h b/game/game.h index 9142265..f58d071 100644 --- a/game/game.h +++ b/game/game.h @@ -35,7 +35,7 @@ class CTTRTSGame public: CTTRTSGame( ucoord_t c, ucoord_t r ) - : dimentions( {c,r} ) + : dimensions( c,r ) { } @@ -72,17 +72,19 @@ public: // Get orders by index as above inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].order; } - // Get dimentions - inline const uvector2& GetDimentions() const { return dimentions; } + // Get dimensions + inline const uvector2 &GetDimensions() const { return dimensions; } + + // Check for a win, returns invalid for no win state reached + // Note: this function will return invalid a draw was reached + // best practice would be to call with GetNumUnits() == 0 + Team CheckForWin() const; private: // Verify any order - non-zero is error int VerifyOrder( player_id_t player, const COrder& order ) const; - // Verify any order - non-zero is error - int VerifyUnit( const CUnit& unit ) const; - // Verify Position - non-zero is error int VerifyPos( uvector2 vec ) const; @@ -99,7 +101,7 @@ private: CUnitVector m_deadUnits; // Dimensions of the game - uvector2 dimentions; + uvector2 dimensions; }; #endif //_GAME_H_ diff --git a/game/gametypes.h b/game/gametypes.h index 903ac9a..b988a48 100644 --- a/game/gametypes.h +++ b/game/gametypes.h @@ -4,25 +4,27 @@ #include // std::numeric_limits // Type for a team IDs -typedef unsigned short team_id_t; +enum class Team : char +{ + Red = 0, + Blue, + Green, + Yellow, + NUM_INVALID +}; // Type for player IDs -typedef unsigned short player_id_t; +typedef unsigned char player_id_t; // Type for unit IDs typedef unsigned short unit_id_t; -// Type for the unit type-id -typedef char unitType_c; - // Typedef for unit visual representations typedef char unitVis_c; // Invalid data for above types -static const team_id_t team_id_invalid = std::numeric_limits::max(); static const player_id_t player_id_invalid = std::numeric_limits::max(); static const unit_id_t unit_id_invalid = std::numeric_limits::max(); -static const unitType_c unitType_invalid = std::numeric_limits::max(); static const unitVis_c unitVis_invalid = std::numeric_limits::max(); #endif //_GAME_TYPES_H_ \ No newline at end of file diff --git a/game/orders.cpp b/game/orders.cpp index 514ee51..836d4e8 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -24,7 +24,7 @@ COrder GetOrderFromString( const std::string& _order ) { const std::string order_unit = order.substr(0, pos); - ret.unit = atoi( order_unit.c_str() ); + ret.unit = (unit_id_t)atoi( order_unit.c_str() ); // Erase everything up to and including the delimiter order.erase(0, pos + 1); diff --git a/game/orders.h b/game/orders.h index 28ee471..558682f 100644 --- a/game/orders.h +++ b/game/orders.h @@ -15,7 +15,7 @@ enum class order_c : char L = 'L', R = 'R', A = 'A', - INVALID + NUM_INVALID }; // Container for an order @@ -24,7 +24,7 @@ struct COrder // Base constructor makes invalid order COrder() : unit ( unit_id_invalid ) - , order ( order_c::INVALID ) + , order ( order_c::NUM_INVALID ) {} // Unit order is for diff --git a/game/unit.cpp b/game/unit.cpp index b69d36d..5b52713 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -32,7 +32,7 @@ namespace // Plain constructor CUnit::CUnit() : unit_id ( get_unique_unit_id() ) -, team_id ( team_id_invalid ) +, team_id ( Team::NUM_INVALID ) , unit_vis ( unitVis_invalid ) , player_id ( player_id_invalid ) , dir ( dir_t::S ) diff --git a/game/unit.h b/game/unit.h index 346131d..4c6d13f 100644 --- a/game/unit.h +++ b/game/unit.h @@ -24,15 +24,14 @@ public: // Getters for all the members inline const unit_id_t& getID() const { return unit_id; } - inline const team_id_t& getTeam() const { return team_id; } + inline const Team & getTeam() const { return team_id; } inline const player_id_t& getPlayer() const { return player_id; } inline const unitVis_c& getVisual() const { return unit_vis; } inline const dir_t& getDir() const { return dir; } - // Return non-zero values on error - inline int setTeam(const team_id_t& v) { return (v == team_id_invalid) ? -1 : (( team_id = v ), 0); } - inline int setPlayer(const player_id_t& v) { return (v == player_id_invalid) ? -1 : (( player_id = v ), 0); } - inline int setVisual(const unitVis_c& v) { return (v == unitVis_invalid) ? -1 : (( unit_vis = v ), 0); } + inline Team setTeam(const Team & v) { return (team_id = v); } + inline player_id_t setPlayer(const player_id_t& v) { return ( player_id = v ); } + inline unitVis_c setVisual(const unitVis_c& v) { return ( unit_vis = v ); } // Set unit direction inline dir_t setDir(const dir_t& v) { return (dir = v); } @@ -69,7 +68,7 @@ private: unitVis_c unit_vis; // Team ID - team_id_t team_id; + Team team_id; // Owner ID player_id_t player_id; @@ -88,7 +87,7 @@ typedef std::vector< CUnit > CUnitVector; inline bool CUnit::valid() const { return (unit_id != unit_id_invalid ) - && (team_id != team_id_invalid ) + && (team_id != Team::NUM_INVALID ) && (player_id != player_id_invalid) && (unit_vis != unitVis_invalid); } diff --git a/maths/vector2.h b/maths/vector2.h index 9be4019..6552807 100644 --- a/maths/vector2.h +++ b/maths/vector2.h @@ -7,6 +7,11 @@ struct uvector2; struct vector2 { + vector2( coord_t _x, coord_t _y ) + : x(_x) + , y(_y) + {} + coord_t x; coord_t y; @@ -27,6 +32,11 @@ struct vector2 struct uvector2 { + uvector2( ucoord_t _x, ucoord_t _y ) + : x(_x) + , y(_y) + {} + ucoord_t x; ucoord_t y; @@ -54,22 +64,19 @@ inline vector2 vecFromDir( dir_t dir ) { case dir_t::N: return { 0,1 }; - break; case dir_t::E: return { 1,0 }; - break; case dir_t::S: return { 0,-1 }; - break; case dir_t::W: return { -1,0 }; - break; - } - return { 0,0 }; + default: + return { 0,0 }; + } } #endif //_VECTOR2_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6ef211e..c212c3a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,9 +13,6 @@ set( SOURCES test.cpp ) -# Set to use c++11, because we're cool like that -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) - # Add the executable add_executable( ttrts-test ${SOURCES} ) diff --git a/test/test.cpp b/test/test.cpp index c506e68..1306d1e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -59,6 +59,7 @@ const char* tests() CUnit unit = CUnit::getUnitFromVis('^'); unit.setPos( {2,2} ); unit.setPlayer(0); + unit.setTeam(Team::Red); game.AddUnit(std::move(unit)); } @@ -67,6 +68,7 @@ const char* tests() CUnit unit = CUnit::getUnitFromVis('^'); unit.setPos( {2,2} ); unit.setPlayer(0); + unit.setTeam(Team::Red); if( !game.AddUnit(std::move(unit)) ) return "Game should have rejected unit placed on the same spot"; @@ -86,6 +88,7 @@ const char* tests() unit.setPos( {2,2} ); unit.setPlayer(0); + unit.setTeam(Team::Red); if ( game.AddUnit(std::move(unit)) ) return "Game failed to add valid unit"; @@ -115,7 +118,8 @@ const char* tests() COrder order; unit.setPos( {0,0} ); - unit.setPlayer(0); + unit.setPlayer(1); + unit.setTeam(Team::Blue); if ( game.AddUnit(std::move(unit)) ) return "Game failed to add valid unit"; @@ -130,7 +134,8 @@ const char* tests() CUnit unit = CUnit::getUnitFromVis('<'); unit.setPos( {1,0} ); - unit.setPlayer(1); + unit.setPlayer(2); + unit.setTeam(Team::Red); if ( game.AddUnit(std::move(unit)) ) return "Game failed to add valid unit"; @@ -146,6 +151,9 @@ const char* tests() if ( game.GetUnitByIndex(0).getID() != id ) return "Game killed the wrong unit"; + + if ( game.CheckForWin() != Team::Blue ) + return "Game failed to recognise a win for the right Team"; } return 0; diff --git a/ttrts/CMakeLists.txt b/ttrts/CMakeLists.txt index e1dd18b..8258be5 100644 --- a/ttrts/CMakeLists.txt +++ b/ttrts/CMakeLists.txt @@ -7,8 +7,5 @@ set( SOURCES main.cpp ) -# Set to use c++11, because we're cool like that -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) - # Add the executable add_executable( ttrts ${SOURCES} ) \ No newline at end of file From b77411121a3cca24d4dcbeb30312a2f80c9251b9 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 049/190] Implement conversion of Unit to String and back --- game/orders.cpp | 24 ++++----- game/orders.h | 4 +- game/unit.cpp | 128 ++++++++++++++++++++++++++++++++++++++++++++++-- game/unit.h | 23 +++++++-- maths/vector2.h | 2 + test/test.cpp | 38 +++++++++++--- 6 files changed, 188 insertions(+), 31 deletions(-) diff --git a/game/orders.cpp b/game/orders.cpp index 836d4e8..80bd284 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -6,9 +6,10 @@ std::string GetStringFromOrder(const COrder& order ) { std::string ret; + ret += "O:"; + ret += (char)order.order; + ret += " U:"; ret += std::to_string(order.unit); - ret += ORDER_DELIMITER; - ret += (char)order.order; return ret; } @@ -19,19 +20,18 @@ COrder GetOrderFromString( const std::string& _order ) std::string order = _order; COrder ret; - size_t pos = order.find(ORDER_DELIMITER); - if( pos != std::string::npos ) - { - const std::string order_unit = order.substr(0, pos); + if( order.substr(0, 2) != "O:" ) + return COrder(); + order.erase(0, 2); // Erase the O: prefix - ret.unit = (unit_id_t)atoi( order_unit.c_str() ); + ret.order = (order_c)order[0]; // Grab single char oder + order.erase(0, 2); // Erase the order and a space - // Erase everything up to and including the delimiter - order.erase(0, pos + 1); + if( order.substr(0, 2) != "U:" ) + return COrder(); + order.erase(0, 2); // Erase the U: prefix - // Next single char is the order - ret.order = (order_c)order[0]; - } + ret.unit = (unit_id_t)atoi( order.c_str() ); // Grab the unit ID return ret; } diff --git a/game/orders.h b/game/orders.h index 558682f..dde304f 100644 --- a/game/orders.h +++ b/game/orders.h @@ -6,8 +6,6 @@ #include "gametypes.h" -#define ORDER_DELIMITER ' ' - // Type for all orders ( as a char ) enum class order_c : char { @@ -47,7 +45,7 @@ inline bool COrder::operator== ( const COrder& rhs ) const // Typedef a vector of orders typedef std::vector COrderVector; -// Order strings stored as simply "[unit id] [order char]" +// Order strings stored as simply "O:[order char] U:[unit id]" // string <--> order conversion functions std::string GetStringFromOrder(const COrder& order ); COrder GetOrderFromString( const std::string& order ); diff --git a/game/unit.cpp b/game/unit.cpp index 5b52713..9e5591b 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -29,6 +29,121 @@ namespace } } +// Get a unit from a visual +CUnit GetUnitFromVis( unitVis_c vis ) +{ + CUnit unit; + unit.setFromVisual(vis); + return unit; +} + +// Get a string descriptor of a unit +// "U:[unit_id] T:[team_id] P:[player_id] V:[unit_vis] D:[dir] POS:[pos.x],[pos.y]" +std::string GetStringFromUnit(const CUnit& unit ) +{ + std::string ret; + ret += "U:"; + ret += std::to_string(unit.getID()); + ret += " T:"; + ret += std::to_string((int)unit.getTeam()); + ret += " P:"; + ret += std::to_string(unit.getPlayer()); + ret += " V:"; + ret += (char)unit.getVisual(); + ret += " D:"; + ret += (char)unit.getDir(); + ret += " POS:"; + ret += std::to_string(unit.getPos().x); + ret += ","; + ret += std::to_string(unit.getPos().y); + + return ret; +} + +// Get a unit from a string descriptor +// "U:[unit_id] T:[team_id] P:[player_id] V:[unit_vis] D:[dir] POS:[pos.x],[pos.y]" +CUnit GetUnitFromString(const std::string& _unit ) +{ + std::string unit = _unit; + CUnit ret; + size_t pos; + + // UNIT ID + if( unit.substr(0, 2) != "U:" ) + return CUnit(); + unit.erase(0, 2); // Erase the U: prefix + + pos = unit.find(' '); + if( pos == std::string::npos ) + return CUnit(); + + const unit_id_t id = (unit_id_t)atoi( unit.substr(0,pos).c_str() ); // Grab the ID + ret.setIDForced( id ); // Force set the ID + unit.erase(0, pos+1); // Erase the ID and a space + + // TEAM + if( unit.substr(0, 2) != "T:" ) + return CUnit(); + unit.erase(0, 2); // Erase the T: prefix + + pos = unit.find(' '); + if( pos == std::string::npos ) + return CUnit(); + + const Team team = (Team)atoi( unit.substr(0,pos).c_str() ); // Grab the ID + ret.setTeam( team ); // Force set the ID + unit.erase(0, pos+1); // Erase the team and a space + + // PLAYER + if( unit.substr(0, 2) != "P:" ) + return CUnit(); + unit.erase(0, 2); // Erase the P: prefix + + pos = unit.find(' '); + if( pos == std::string::npos ) + return CUnit(); + + const player_id_t player = (player_id_t)atoi( unit.substr(0,pos).c_str() ); // Grab the ID + ret.setPlayer( player ); // Force set the ID + unit.erase(0, pos+1); // Erase the player and a space + + // VISUAL + if( unit.substr(0, 2) != "V:" ) + return CUnit(); + unit.erase(0, 2); // Erase the W: prefix + + unitVis_c vis = unit[0]; + ret.setVisual(vis); + unit.erase(0, 2); // Erase the vis and a space + + // DIRECTION + if( unit.substr(0, 2) != "D:" ) + return CUnit(); + unit.erase(0, 2); // Erase the D: prefix + + dir_t dir = (dir_t)unit[0]; + ret.setDir(dir); + unit.erase(0, 2); // Erase the ID and a space + + // POSITION + if( unit.substr(0, 4) != "POS:" ) + return CUnit(); + unit.erase(0, 4); // Erase the Pos: prefix + + pos = unit.find(','); + if( pos == std::string::npos ) + return CUnit(); + + uvector2 upos; + upos.x = (ucoord_t)atoi( unit.substr(0,pos).c_str() ); // Grab the x + unit.erase(0, pos+1); // Erase the x and the comma + + upos.y = (ucoord_t)atoi( unit.c_str() ); // Grab the y from what's left + ret.setPos( upos ); //Set the position + + return ret; +} + // Plain constructor CUnit::CUnit() : unit_id ( get_unique_unit_id() ) @@ -66,12 +181,15 @@ CUnit& CUnit::operator=(CUnit&& unit) return *this; } -// Get a unit from a visual -CUnit CUnit::getUnitFromVis( unitVis_c vis ) +// Equals operator +bool CUnit::operator==(const CUnit& rhs) { - CUnit unit; - unit.setFromVisual(vis); - return unit; + return (unit_id == rhs.unit_id) + && (team_id == rhs.team_id) + && (player_id == rhs.player_id) + && (unit_vis == rhs.unit_vis) + && (dir == rhs.dir) + && (pos == rhs.pos); } // Update the visual representation of the unit diff --git a/game/unit.h b/game/unit.h index 4c6d13f..a6ef88d 100644 --- a/game/unit.h +++ b/game/unit.h @@ -15,16 +15,19 @@ public: // Constructor CUnit(); - // Move constructor and move asignement. CUnit cannot be copied + // Move constructor and move assignment. CUnit cannot be copied CUnit(CUnit&& unit); CUnit& operator=(CUnit&& unit); + bool operator==(const CUnit& rhs); + bool operator!=(const CUnit& rhs) { return !(*this == rhs); } + // Default dtor ~CUnit() = default; // Getters for all the members inline const unit_id_t& getID() const { return unit_id; } - inline const Team & getTeam() const { return team_id; } + inline const Team & getTeam() const { return team_id; } inline const player_id_t& getPlayer() const { return player_id; } inline const unitVis_c& getVisual() const { return unit_vis; } inline const dir_t& getDir() const { return dir; } @@ -39,6 +42,9 @@ public: inline const uvector2& getPos() const { return pos; } inline void setPos(const uvector2& v) { pos = v; } + + inline const unit_id_t& setIDForced(const unit_id_t& v) { return (unit_id = v); } + // Get the co-ordinate infront of the unit uvector2 getInFront() const; @@ -48,9 +54,6 @@ public: // Set a unit based solely on it's visual bool setFromVisual( const unitVis_c& vis); - // Factory function for creating units from a visual - static CUnit getUnitFromVis( unitVis_c vis ); - // Orientation methods dir_t turnLeft(); dir_t turnRight(); @@ -92,4 +95,14 @@ inline bool CUnit::valid() const && (unit_vis != unitVis_invalid); } + +// Factory function for creating units from a visual +CUnit GetUnitFromVis( unitVis_c vis ); + +// Units strings stored as +// "U:[unit_id] T:[team_id] P:[player_id] V:[unit_vis] D:[dir] POS:[pos.x],[pos.y]" +// Unit <--> string conversion functions +std::string GetStringFromUnit(const CUnit& unit ); +CUnit GetUnitFromString(const std::string& unit ); + #endif //_UNIT_H_ diff --git a/maths/vector2.h b/maths/vector2.h index 6552807..ee36d5d 100644 --- a/maths/vector2.h +++ b/maths/vector2.h @@ -7,6 +7,7 @@ struct uvector2; struct vector2 { + vector2() : x (0), y (0) {} vector2( coord_t _x, coord_t _y ) : x(_x) , y(_y) @@ -32,6 +33,7 @@ struct vector2 struct uvector2 { + uvector2() : x (0), y (0) {} uvector2( ucoord_t _x, ucoord_t _y ) : x(_x) , y(_y) diff --git a/test/test.cpp b/test/test.cpp index 1306d1e..160f6bb 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -22,9 +22,35 @@ const char* tests() return "Unit IDs the same"; } + // Test basic invalid unit conversion + { + CUnit unit1; + + std::string unit1Desc = GetStringFromUnit(unit1); + CUnit unit2 = GetUnitFromString(unit1Desc); + + if ( unit1 != unit2 ) + return "Failed to convert an empty unit to string and back"; + } + + // Test custom unit conversion + { + CUnit unit1; + unit1.setFromVisual('v'); + unit1.setPlayer(0); + unit1.setTeam(Team::Green); + unit1.setPos( uvector2(5,10) ); + + std::string unit1Desc = GetStringFromUnit(unit1); + CUnit unit2 = GetUnitFromString(unit1Desc); + + if ( unit1 != unit2 ) + return "Failed to convert custom unit to string and back"; + } + // Test if we can successfully create a unit from a visual { - CUnit unit = CUnit::getUnitFromVis('v'); + CUnit unit = GetUnitFromVis('v'); if( unit.getVisual() != 'v' ) return "failed to properly create V unit with factory"; } @@ -56,7 +82,7 @@ const char* tests() CTTRTSGame game( 5, 5 ); { - CUnit unit = CUnit::getUnitFromVis('^'); + CUnit unit = GetUnitFromVis('^'); unit.setPos( {2,2} ); unit.setPlayer(0); unit.setTeam(Team::Red); @@ -65,7 +91,7 @@ const char* tests() } { - CUnit unit = CUnit::getUnitFromVis('^'); + CUnit unit = GetUnitFromVis('^'); unit.setPos( {2,2} ); unit.setPlayer(0); unit.setTeam(Team::Red); @@ -82,7 +108,7 @@ const char* tests() { CTTRTSGame game( 5, 5 ); - CUnit unit = CUnit::getUnitFromVis('>'); + CUnit unit = GetUnitFromVis('>'); const unit_id_t id = unit.getID(); COrder order; @@ -113,7 +139,7 @@ const char* tests() unit_id_t id; { - CUnit unit = CUnit::getUnitFromVis('>'); + CUnit unit = GetUnitFromVis('>'); id = unit.getID(); COrder order; @@ -131,7 +157,7 @@ const char* tests() return "Game failed to issue valid order"; } { - CUnit unit = CUnit::getUnitFromVis('<'); + CUnit unit = GetUnitFromVis('<'); unit.setPos( {1,0} ); unit.setPlayer(2); From 8a486fdd40295bb42aebe872ef5a7546e815703f Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 050/190] Use snprintf and sscanf instead of std::string tomfoolery Rename order_c to command_c for better readability --- game/game.cpp | 16 +++--- game/orders.cpp | 34 ++++++------- game/orders.h | 11 +++-- game/unit.cpp | 128 ++++++++++++++---------------------------------- game/unit.h | 3 +- test/test.cpp | 6 +-- 6 files changed, 71 insertions(+), 127 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 8f4e988..d5eb170 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -84,10 +84,10 @@ uvector2 CTTRTSGame::GetNewPosition( const OrderUnitPair& pair ) const { // Grab the order - switch ( pair.order.order ) + switch ( pair.order.command) { // For forward orders, grab in front - case order_c::F: + case command_c::F: return pair.unit.getInFront(); break; // For all other orders, just grab the old position @@ -105,9 +105,9 @@ int CTTRTSGame::SimulateToNextTurn() // Attempt all movement orders for ( OrderUnitPair& pair : m_OrderUnitPairs ) { - switch ( pair.order.order ) + switch ( pair.order.command) { - case order_c::F: + case command_c::F: { // Verify new unit position will be on the board uvector2 newpos = GetNewPosition(pair); @@ -147,9 +147,9 @@ int CTTRTSGame::SimulateToNextTurn() // Attempt all actions for ( OrderUnitPair& pair : m_OrderUnitPairs ) { - switch ( pair.order.order ) + switch ( pair.order.command) { - case order_c::A: + case command_c::A: { // Verify that there's a unit in front to attack uvector2 infront = pair.unit.getInFront(); @@ -168,14 +168,14 @@ int CTTRTSGame::SimulateToNextTurn() } } break; - case order_c::L: + case command_c::L: { // Simply turn left pair.unit.turnLeft(); pair.order = COrder(); } break; - case order_c::R: + case command_c::R: { // Simply turn right pair.unit.turnRight(); diff --git a/game/orders.cpp b/game/orders.cpp index 80bd284..ef0c7aa 100644 --- a/game/orders.cpp +++ b/game/orders.cpp @@ -1,3 +1,4 @@ +#include #include "orders.h" #include "mathtypes.h" @@ -5,33 +6,30 @@ // Convert an order to a string std::string GetStringFromOrder(const COrder& order ) { - std::string ret; - ret += "O:"; - ret += (char)order.order; - ret += " U:"; - ret += std::to_string(order.unit); + static char buff[128]; + memset(buff,0,sizeof(buff)); - return ret; + snprintf(buff,128, ORDER_FORMATTER, + order.command, + order.unit); + + return buff; } // Convert a string to an order -COrder GetOrderFromString( const std::string& _order ) +COrder GetOrderFromString( const std::string& order ) { - std::string order = _order; COrder ret; - if( order.substr(0, 2) != "O:" ) - return COrder(); - order.erase(0, 2); // Erase the O: prefix + char corder; + unsigned int unit; - ret.order = (order_c)order[0]; // Grab single char oder - order.erase(0, 2); // Erase the order and a space + sscanf(order.c_str(), ORDER_FORMATTER, + &corder, + &unit); - if( order.substr(0, 2) != "U:" ) - return COrder(); - order.erase(0, 2); // Erase the U: prefix - - ret.unit = (unit_id_t)atoi( order.c_str() ); // Grab the unit ID + ret.command = (command_c)corder; + ret.unit = unit; return ret; } diff --git a/game/orders.h b/game/orders.h index dde304f..cb1cf80 100644 --- a/game/orders.h +++ b/game/orders.h @@ -7,7 +7,7 @@ #include "gametypes.h" // Type for all orders ( as a char ) -enum class order_c : char +enum class command_c : char { F = 'F', L = 'L', @@ -22,14 +22,14 @@ struct COrder // Base constructor makes invalid order COrder() : unit ( unit_id_invalid ) - , order ( order_c::NUM_INVALID ) + , command( command_c::NUM_INVALID ) {} // Unit order is for unit_id_t unit; // Order command issued - order_c order; + command_c command; // Basic operators inline bool operator==( const COrder& rhs ) const; @@ -39,13 +39,14 @@ struct COrder // Simple == operator inline bool COrder::operator== ( const COrder& rhs ) const { - return ( unit == rhs.unit ) && ( order == rhs.order ); + return ( unit == rhs.unit ) && ( command == rhs.command); } // Typedef a vector of orders typedef std::vector COrderVector; -// Order strings stored as simply "O:[order char] U:[unit id]" +#define ORDER_FORMATTER "ORDER co:%c un:%u" + // string <--> order conversion functions std::string GetStringFromOrder(const COrder& order ); COrder GetOrderFromString( const std::string& order ); diff --git a/game/unit.cpp b/game/unit.cpp index 9e5591b..c9b917a 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -1,3 +1,4 @@ +#include #include "unit.h" #include // for std::map @@ -38,108 +39,53 @@ CUnit GetUnitFromVis( unitVis_c vis ) } // Get a string descriptor of a unit -// "U:[unit_id] T:[team_id] P:[player_id] V:[unit_vis] D:[dir] POS:[pos.x],[pos.y]" +// "U id:[unit_id] team:[team_id] player:[player_id] vis:[unit_vis] dir:[dir] pos:[pos.x],[pos.y]" std::string GetStringFromUnit(const CUnit& unit ) { - std::string ret; - ret += "U:"; - ret += std::to_string(unit.getID()); - ret += " T:"; - ret += std::to_string((int)unit.getTeam()); - ret += " P:"; - ret += std::to_string(unit.getPlayer()); - ret += " V:"; - ret += (char)unit.getVisual(); - ret += " D:"; - ret += (char)unit.getDir(); - ret += " POS:"; - ret += std::to_string(unit.getPos().x); - ret += ","; - ret += std::to_string(unit.getPos().y); + static char buff[128]; + memset(buff,0,sizeof(buff)); - return ret; + snprintf(buff,128, UNIT_FORMATTER, + unit.getID(), + (int)unit.getTeam(), + unit.getPlayer(), + unit.getVisual(), + unit.getDir(), + unit.getPos().x, + unit.getPos().y ); + + return buff; } // Get a unit from a string descriptor -// "U:[unit_id] T:[team_id] P:[player_id] V:[unit_vis] D:[dir] POS:[pos.x],[pos.y]" -CUnit GetUnitFromString(const std::string& _unit ) +// "U id:[unit_id] team:[team_id] player:[player_id] vis:[unit_vis] dir:[dir] pos:[pos.x],[pos.y]" +CUnit GetUnitFromString(const std::string& unit ) { - std::string unit = _unit; CUnit ret; - size_t pos; - // UNIT ID - if( unit.substr(0, 2) != "U:" ) - return CUnit(); - unit.erase(0, 2); // Erase the U: prefix + unsigned int id; + int team; + unsigned int player; + char vis; + char dir; + unsigned int posx; + unsigned int posy; - pos = unit.find(' '); - if( pos == std::string::npos ) - return CUnit(); + sscanf(unit.c_str(), UNIT_FORMATTER, + &id, + &team, + &player, + &vis, + &dir, + &posx, + &posy ); - const unit_id_t id = (unit_id_t)atoi( unit.substr(0,pos).c_str() ); // Grab the ID - ret.setIDForced( id ); // Force set the ID - unit.erase(0, pos+1); // Erase the ID and a space - - // TEAM - if( unit.substr(0, 2) != "T:" ) - return CUnit(); - unit.erase(0, 2); // Erase the T: prefix - - pos = unit.find(' '); - if( pos == std::string::npos ) - return CUnit(); - - const Team team = (Team)atoi( unit.substr(0,pos).c_str() ); // Grab the ID - ret.setTeam( team ); // Force set the ID - unit.erase(0, pos+1); // Erase the team and a space - - // PLAYER - if( unit.substr(0, 2) != "P:" ) - return CUnit(); - unit.erase(0, 2); // Erase the P: prefix - - pos = unit.find(' '); - if( pos == std::string::npos ) - return CUnit(); - - const player_id_t player = (player_id_t)atoi( unit.substr(0,pos).c_str() ); // Grab the ID - ret.setPlayer( player ); // Force set the ID - unit.erase(0, pos+1); // Erase the player and a space - - // VISUAL - if( unit.substr(0, 2) != "V:" ) - return CUnit(); - unit.erase(0, 2); // Erase the W: prefix - - unitVis_c vis = unit[0]; - ret.setVisual(vis); - unit.erase(0, 2); // Erase the vis and a space - - // DIRECTION - if( unit.substr(0, 2) != "D:" ) - return CUnit(); - unit.erase(0, 2); // Erase the D: prefix - - dir_t dir = (dir_t)unit[0]; - ret.setDir(dir); - unit.erase(0, 2); // Erase the ID and a space - - // POSITION - if( unit.substr(0, 4) != "POS:" ) - return CUnit(); - unit.erase(0, 4); // Erase the Pos: prefix - - pos = unit.find(','); - if( pos == std::string::npos ) - return CUnit(); - - uvector2 upos; - upos.x = (ucoord_t)atoi( unit.substr(0,pos).c_str() ); // Grab the x - unit.erase(0, pos+1); // Erase the x and the comma - - upos.y = (ucoord_t)atoi( unit.c_str() ); // Grab the y from what's left - ret.setPos( upos ); //Set the position + ret.setIDForced((unit_id_t)id); + ret.setTeam((Team)team); + ret.setPlayer((player_id_t)player); + ret.setVisual((unitVis_c)vis); + ret.setDir((dir_t)dir); + ret.setPos(uvector2(posx,posy)); return ret; } diff --git a/game/unit.h b/game/unit.h index a6ef88d..9c1ee0d 100644 --- a/game/unit.h +++ b/game/unit.h @@ -99,9 +99,8 @@ inline bool CUnit::valid() const // Factory function for creating units from a visual CUnit GetUnitFromVis( unitVis_c vis ); -// Units strings stored as -// "U:[unit_id] T:[team_id] P:[player_id] V:[unit_vis] D:[dir] POS:[pos.x],[pos.y]" // Unit <--> string conversion functions +#define UNIT_FORMATTER "UNIT id:%u tm:%u pl:%u vs:%c dr:%c ps:%u,%u" std::string GetStringFromUnit(const CUnit& unit ); CUnit GetUnitFromString(const std::string& unit ); diff --git a/test/test.cpp b/test/test.cpp index 160f6bb..84e6b39 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -58,7 +58,7 @@ const char* tests() // Test if we can successfully convert orders back and forth { COrder order; - order.order = order_c::F; + order.command = command_c::F; order.unit = 10; std::string order_string = GetStringFromOrder(order); COrder order2 = GetOrderFromString(order_string); @@ -120,7 +120,7 @@ const char* tests() return "Game failed to add valid unit"; order.unit = id; - order.order = order_c::F; + order.command = command_c::F; if( game.IssueOrder(0,order) ) return "Game failed to issue valid order"; @@ -151,7 +151,7 @@ const char* tests() return "Game failed to add valid unit"; order.unit = id; - order.order = order_c::A; + order.command = command_c::A; if( game.IssueOrder(0,order) ) return "Game failed to issue valid order"; From 714fd2894d785d8cdd5292791cf24fa2ddcea133 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 051/190] Rename orders -> order --- game/CMakeLists.txt | 2 +- game/game.h | 2 +- game/{orders.cpp => order.cpp} | 2 +- game/{orders.h => order.h} | 0 test/test.cpp | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename game/{orders.cpp => order.cpp} (96%) rename game/{orders.h => order.h} (100%) diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 56140be..2ed80ff 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -11,7 +11,7 @@ include_directories( set( SOURCES game.cpp unit.cpp - orders.cpp + order.cpp ) add_library( game ${SOURCES} ) \ No newline at end of file diff --git a/game/game.h b/game/game.h index f58d071..fbc9855 100644 --- a/game/game.h +++ b/game/game.h @@ -3,7 +3,7 @@ #include "unit.h" #include "gametypes.h" -#include "orders.h" +#include "order.h" // Type for order and unit pairs struct OrderUnitPair diff --git a/game/orders.cpp b/game/order.cpp similarity index 96% rename from game/orders.cpp rename to game/order.cpp index ef0c7aa..731b3fc 100644 --- a/game/orders.cpp +++ b/game/order.cpp @@ -1,5 +1,5 @@ #include -#include "orders.h" +#include "order.h" #include "mathtypes.h" diff --git a/game/orders.h b/game/order.h similarity index 100% rename from game/orders.h rename to game/order.h diff --git a/test/test.cpp b/test/test.cpp index 84e6b39..8ea3136 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,7 +1,7 @@ #include // std::cout #include "board.h" -#include "orders.h" +#include "order.h" #include "game.h" const char* tests() From 24ac05847506119954c74aea7df316f09cafb549 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 052/190] Update the game readme --- game/README.md | 41 ++++++++++------------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/game/README.md b/game/README.md index f04313e..d8bf38c 100644 --- a/game/README.md +++ b/game/README.md @@ -1,4 +1,4 @@ -ttrts Game Design +Game Design ================= The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board. @@ -11,43 +11,22 @@ The engine then takes all commands, evaluates all movement first simultaneously, All attempted movement to the same square by two or more units will fail. +Friendly fire is enabled by default + -------------------------------------------------------- + Units ----- Currently only one unit, this will be expanded in the future. -Units have a set of properties, and commands than can be issued. -Commands take the form of a single char literal. -All units have 1 health. +Units have a set of properties, and commands than can be issued. - -## V -V is your basic slow melee unit. It has very basic data and controls, basically acting like a turtle. -Can be represented based on direction by `<` `^` `>` `v` +All units take one hit to kill. ##### properties -| property | type | description | -|:---------|:--------|:----------------------------------| -| pos | char[2] | x,y position on the board | -| dir | char | compass direction unit is facing | +See [the unit header](unit.h) for full details on unit properties -##### commands -| command | description | -|:---------|:--------------------------------------------| -| L/R | turn left/right | -| F | move forward | -| A | deal 1 damage to unit directly in front | - --------------------------------------------------------- -The Board --------- - -As an example, let's start with a basic starting `[10,5]` board -```` -0000000000 -0000000000 -0000000000 -0000000000 -0000000000 -```` +##### orders +Commands take the form of a single char literal. +See [the order header](order.h) for details on the orders From 002c1e592735af5888b754dc436f01ba4c529337 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 053/190] Add new game string outputer and use it. modify some of the other outputters to look better --- game/game.cpp | 26 ++++++++++++++++++++++++++ game/game.h | 9 +++++++++ game/order.h | 2 +- game/unit.h | 2 +- test/test.cpp | 2 ++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index d5eb170..253f0a4 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -218,6 +218,9 @@ int CTTRTSGame::SimulateToNextTurn() pair.order = COrder(); } + // Increment the current turn + turn++; + return error; } @@ -351,4 +354,27 @@ Team CTTRTSGame::CheckForWin() const } return winningTeam; +} + +// Get the game information as a string +std::string CTTRTSGame::GetStateAsString() const +{ + // Print out the header + char header[64]; + snprintf(header, 512, GAME_HEADER_FORMATTER , turn, dimensions.x, dimensions.y ); + + // Gather unit information + std::string units; + for ( const OrderUnitPair& pair : m_OrderUnitPairs ) + { + units += GetStringFromUnit(pair.unit); + units += '\n'; + } + + // Append the header and units + std::string state(header); + state += '\n'; + state += units; + + return state; } \ No newline at end of file diff --git a/game/game.h b/game/game.h index fbc9855..7527768 100644 --- a/game/game.h +++ b/game/game.h @@ -36,6 +36,7 @@ public: CTTRTSGame( ucoord_t c, ucoord_t r ) : dimensions( c,r ) + , turn (0) { } @@ -79,6 +80,9 @@ public: // Note: this function will return invalid a draw was reached // best practice would be to call with GetNumUnits() == 0 Team CheckForWin() const; + + // Get the game information as a string + std::string GetStateAsString() const; private: @@ -102,6 +106,11 @@ private: // Dimensions of the game uvector2 dimensions; + + // Int to store the current turn + unsigned int turn; }; +#define GAME_HEADER_FORMATTER "===== GAME TURN =====\nSIZE:[%u,%u]\nTURN:%u" + #endif //_GAME_H_ diff --git a/game/order.h b/game/order.h index cb1cf80..b35719b 100644 --- a/game/order.h +++ b/game/order.h @@ -45,7 +45,7 @@ inline bool COrder::operator== ( const COrder& rhs ) const // Typedef a vector of orders typedef std::vector COrderVector; -#define ORDER_FORMATTER "ORDER co:%c un:%u" +#define ORDER_FORMATTER "ORDER:%c id:%u" // string <--> order conversion functions std::string GetStringFromOrder(const COrder& order ); diff --git a/game/unit.h b/game/unit.h index 9c1ee0d..61162e3 100644 --- a/game/unit.h +++ b/game/unit.h @@ -100,7 +100,7 @@ inline bool CUnit::valid() const CUnit GetUnitFromVis( unitVis_c vis ); // Unit <--> string conversion functions -#define UNIT_FORMATTER "UNIT id:%u tm:%u pl:%u vs:%c dr:%c ps:%u,%u" +#define UNIT_FORMATTER "UNIT:%u tm:%u pl:%u vs:%c dr:%c ps:[%u,%u]" std::string GetStringFromUnit(const CUnit& unit ); CUnit GetUnitFromString(const std::string& unit ); diff --git a/test/test.cpp b/test/test.cpp index 8ea3136..8637e03 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -180,6 +180,8 @@ const char* tests() if ( game.CheckForWin() != Team::Blue ) return "Game failed to recognise a win for the right Team"; + + std::cout< Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 054/190] Finalise implementation of reading in a game from a string --- game/game.cpp | 65 ++++++++++++++++++++++++-- game/game.h | 116 ++++++++++++++++++++++++----------------------- game/gametypes.h | 11 ++--- game/order.cpp | 2 - game/order.h | 4 +- game/unit.cpp | 32 ++++++------- game/unit.h | 43 ++++++++---------- test/test.cpp | 30 +++++++----- 8 files changed, 178 insertions(+), 125 deletions(-) diff --git a/game/game.cpp b/game/game.cpp index 253f0a4..c3d8df4 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -3,6 +3,32 @@ #include #include +CTTRTSGame::CTTRTSGame( ucoord_t c, ucoord_t r ) +: dimensions( c,r ) +, turn (0), name ( "Custom_Game" ) +{ + +} + +// Move constructor +CTTRTSGame::CTTRTSGame(CTTRTSGame&& game) +: m_OrderUnitPairs(std::move(game.m_OrderUnitPairs)) +, dimensions(std::move(game.dimensions)) +, turn(std::move(game.turn)) +, name(std::move(game.name)) +{ + +} + +CTTRTSGame& CTTRTSGame::operator=(CTTRTSGame&& game) +{ + m_OrderUnitPairs = std::move(game.m_OrderUnitPairs); + dimensions = std::move(game.dimensions); + turn = std::move(game.turn); + name = std::move(game.name); + return *this; +} + // Interpret a string of orders int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) { @@ -201,9 +227,6 @@ int CTTRTSGame::SimulateToNextTurn() { if( (*it).unit.getID() == id ) { - // Add the dead unit to our dead unit list - m_deadUnits.push_back(std::move((*it).unit)); - // Remove the unit from our alive unit pairs m_OrderUnitPairs.erase(it); break; @@ -361,20 +384,52 @@ std::string CTTRTSGame::GetStateAsString() const { // Print out the header char header[64]; - snprintf(header, 512, GAME_HEADER_FORMATTER , turn, dimensions.x, dimensions.y ); + snprintf(header, 512, GAME_HEADER_FORMATTER , name.c_str(), dimensions.x, dimensions.y, turn ); // Gather unit information std::string units; for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { - units += GetStringFromUnit(pair.unit); + units += CUnit::GetStringFromUnit(pair.unit); units += '\n'; } // Append the header and units std::string state(header); state += '\n'; + state += GAME_HEADER_DELIMITER; state += units; return state; +} + +// Get the game information as a string +CTTRTSGame CTTRTSGame::CreateFromString( const std::string& input ) +{ + size_t headerEnd = input.find(GAME_HEADER_DELIMITER); + + std::string header = input.substr(0, headerEnd); + std::string units = input.substr(headerEnd + strlen(GAME_HEADER_DELIMITER)); + + // Grab information from the header + char buf[64]; + unsigned int turn; + unsigned int sizex; + unsigned int sizey; + sscanf(header.c_str(), GAME_HEADER_FORMATTER, buf, &sizex, &sizey, &turn ); + + CTTRTSGame game(sizex,sizey); + game.SetName(buf); + game.SetTurn(turn); + + // For each line, construct a unit + size_t pos; + while ( ( pos = units.find('\n') ) != std::string::npos ) + { + std::string unit_string = units.substr(0,pos); + units.erase(0,pos+1); + game.AddUnit(CUnit::GetUnitFromString(unit_string)); + } + + return game; } \ No newline at end of file diff --git a/game/game.h b/game/game.h index 7527768..fad3b68 100644 --- a/game/game.h +++ b/game/game.h @@ -5,6 +5,9 @@ #include "gametypes.h" #include "order.h" +#define GAME_HEADER_FORMATTER "===== %s =====\nSIZE:[%u,%u]\nTURN:%u" +#define GAME_HEADER_DELIMITER "~~~~\n" + // Type for order and unit pairs struct OrderUnitPair { @@ -20,61 +23,41 @@ struct OrderUnitPair , order ( o ) {} - // Move asignment operator - inline OrderUnitPair& operator=( OrderUnitPair&& rhs ) { this->unit = std::move(rhs.unit);this->order = rhs.order;rhs.order = COrder(); return *this; } + // Move assignment operator + inline OrderUnitPair& operator=( OrderUnitPair&& rhs ) + { + this->unit = std::move(rhs.unit); + this->order = std::move(rhs.order); + return *this; + } - CUnit unit; // The unit - COrder order; // Order for this unit from this turn + CUnit unit; // The unit + COrder order; // Order for this unit from this turn }; // Typedef for a vector of these unit pairs typedef std::vector< OrderUnitPair > OrderUnitPairVector; +// Full TTRTS Game class +// Stores information about the game +// Can convert from a string or to a string class CTTRTSGame { public: - CTTRTSGame( ucoord_t c, ucoord_t r ) - : dimensions( c,r ) - , turn (0) - { + // Get the game information as a string + static CTTRTSGame CreateFromString( const std::string& input ); - } + // Constructors + CTTRTSGame( ucoord_t c, ucoord_t r ); + CTTRTSGame(CTTRTSGame&& game); - // Default dtor - ~CTTRTSGame() = default; + // move asignment operator + CTTRTSGame& operator=(CTTRTSGame&& game); - // Issue orders to the game, returns non-zero if orders are incorrect - int IssueOrders( player_id_t player, const std::string& orders ); - int IssueOrders( player_id_t player, const COrderVector& orders ); - - // Issue a single order, returns non-zero for rejection - int IssueOrder( player_id_t player, const COrder& order ); - - // Simulate and progress to the next turn - // Returns non-zero if simulation failed - int SimulateToNextTurn(); - - // Add a unit, nonzero return value indicates error - int AddUnit( CUnit&& unit ); - - // Add a units, nonzero return value indicates error - int AddUnits( CUnitVector&& units ); - - // Get the number of units - inline unsigned int GetNumUnits() const { return m_OrderUnitPairs.size(); } - - // Get unit by index as above (not unit ID) - inline const CUnit& GetUnitByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].unit; } - - // Get unit by unit ID - const CUnit& GetUnitByIDConst( unit_id_t id ) const; - - // Get orders by index as above - inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].order; } - - // Get dimensions - inline const uvector2 &GetDimensions() const { return dimensions; } + // Simulate and progress to the next turn + // Returns non-zero if simulation failed + int SimulateToNextTurn(); // Check for a win, returns invalid for no win state reached // Note: this function will return invalid a draw was reached @@ -83,13 +66,40 @@ public: // Get the game information as a string std::string GetStateAsString() const; + + // Issue orders to the game, returns non-zero if orders are incorrect + int IssueOrders( player_id_t player, const std::string& orders ); + int IssueOrders( player_id_t player, const COrderVector& orders ); + int IssueOrder( player_id_t player, const COrder& order ); + + // Add a units to the game, nonzero return value indicates error + int AddUnit( CUnit&& unit ); + int AddUnits( CUnitVector&& units ); + + // Get the number of units + inline unsigned int GetNumUnits() const { return m_OrderUnitPairs.size(); } + + // Get unit and orderby index as above (not unit ID) + inline const CUnit& GetUnitByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].unit; } + inline const COrder& GetOrdersByIndex( unsigned int i ) const { return m_OrderUnitPairs[i].order; } + + // Get a unit by it's ID + const CUnit& GetUnitByIDConst( unit_id_t id ) const; + + // Get dimensions + inline const uvector2 &GetDimensions() const { return dimensions; } + + // Set the game name + // NOTE: Names with spaces not allowed + inline std::string SetName( const std::string& in ) { return (name = in); } + + // Set the turn of the game + inline int SetTurn( int in ) { return (turn = in); } private: - // Verify any order - non-zero is error + // Verify any order or position - non-zero is error int VerifyOrder( player_id_t player, const COrder& order ) const; - - // Verify Position - non-zero is error int VerifyPos( uvector2 vec ) const; // Get a units new position after an order @@ -98,19 +108,11 @@ private: // Get unit by unit ID CUnit& GetUnitByID( unit_id_t id ); - // Vector to store points to all units - OrderUnitPairVector m_OrderUnitPairs; - - // List of dead units - CUnitVector m_deadUnits; - - // Dimensions of the game - uvector2 dimensions; - - // Int to store the current turn - unsigned int turn; + std::string name; // Game Name + unsigned int turn; // Int to store the current turn + uvector2 dimensions; // Dimensions of the game + OrderUnitPairVector m_OrderUnitPairs; // Vector to store all units and orders }; -#define GAME_HEADER_FORMATTER "===== GAME TURN =====\nSIZE:[%u,%u]\nTURN:%u" #endif //_GAME_H_ diff --git a/game/gametypes.h b/game/gametypes.h index b988a48..b603cf9 100644 --- a/game/gametypes.h +++ b/game/gametypes.h @@ -13,16 +13,11 @@ enum class Team : char NUM_INVALID }; -// Type for player IDs -typedef unsigned char player_id_t; -// Type for unit IDs -typedef unsigned short unit_id_t; +typedef unsigned char player_id_t; // Type for player IDs +typedef unsigned short unit_id_t; // Type for unit IDs +typedef char unitVis_c; // Typedef for unit visual representations -// Typedef for unit visual representations -typedef char unitVis_c; - -// Invalid data for above types static const player_id_t player_id_invalid = std::numeric_limits::max(); static const unit_id_t unit_id_invalid = std::numeric_limits::max(); static const unitVis_c unitVis_invalid = std::numeric_limits::max(); diff --git a/game/order.cpp b/game/order.cpp index 731b3fc..8bd95a9 100644 --- a/game/order.cpp +++ b/game/order.cpp @@ -1,8 +1,6 @@ #include #include "order.h" -#include "mathtypes.h" - // Convert an order to a string std::string GetStringFromOrder(const COrder& order ) { diff --git a/game/order.h b/game/order.h index b35719b..77250fc 100644 --- a/game/order.h +++ b/game/order.h @@ -6,6 +6,8 @@ #include "gametypes.h" +#define ORDER_FORMATTER "ORDER:%c id:%u" + // Type for all orders ( as a char ) enum class command_c : char { @@ -45,8 +47,6 @@ inline bool COrder::operator== ( const COrder& rhs ) const // Typedef a vector of orders typedef std::vector COrderVector; -#define ORDER_FORMATTER "ORDER:%c id:%u" - // string <--> order conversion functions std::string GetStringFromOrder(const COrder& order ); COrder GetOrderFromString( const std::string& order ); diff --git a/game/unit.cpp b/game/unit.cpp index c9b917a..d7470d0 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -31,7 +31,7 @@ namespace } // Get a unit from a visual -CUnit GetUnitFromVis( unitVis_c vis ) +CUnit CUnit::GetUnitFromVis( unitVis_c vis ) { CUnit unit; unit.setFromVisual(vis); @@ -40,26 +40,26 @@ CUnit GetUnitFromVis( unitVis_c vis ) // Get a string descriptor of a unit // "U id:[unit_id] team:[team_id] player:[player_id] vis:[unit_vis] dir:[dir] pos:[pos.x],[pos.y]" -std::string GetStringFromUnit(const CUnit& unit ) +std::string CUnit::GetStringFromUnit(const CUnit& unit ) { static char buff[128]; memset(buff,0,sizeof(buff)); snprintf(buff,128, UNIT_FORMATTER, - unit.getID(), - (int)unit.getTeam(), - unit.getPlayer(), - unit.getVisual(), - unit.getDir(), - unit.getPos().x, - unit.getPos().y ); + unit.unit_id, + (int)unit.team_id, + unit.player_id, + unit.unit_vis, + unit.dir, + unit.pos.x, + unit.pos.y ); return buff; } // Get a unit from a string descriptor // "U id:[unit_id] team:[team_id] player:[player_id] vis:[unit_vis] dir:[dir] pos:[pos.x],[pos.y]" -CUnit GetUnitFromString(const std::string& unit ) +CUnit CUnit::GetUnitFromString(const std::string& unit ) { CUnit ret; @@ -80,12 +80,12 @@ CUnit GetUnitFromString(const std::string& unit ) &posx, &posy ); - ret.setIDForced((unit_id_t)id); - ret.setTeam((Team)team); - ret.setPlayer((player_id_t)player); - ret.setVisual((unitVis_c)vis); - ret.setDir((dir_t)dir); - ret.setPos(uvector2(posx,posy)); + ret.unit_id = (unit_id_t)id; + ret.team_id = (Team)team; + ret.player_id = (player_id_t)player; + ret.unit_vis = (unitVis_c)vis; + ret.dir = (dir_t)dir; + ret.pos = uvector2(posx,posy); return ret; } diff --git a/game/unit.h b/game/unit.h index 61162e3..cd9cff7 100644 --- a/game/unit.h +++ b/game/unit.h @@ -7,11 +7,20 @@ #include "gametypes.h" #include "vector2.h" +#define UNIT_FORMATTER "UNIT:%u tm:%u pl:%u vs:%c dr:%c ps:[%u,%u]" + // Base unit type class CUnit { public: + // Factory function for creating units from a visual + static CUnit GetUnitFromVis( unitVis_c vis ); + + // Unit <--> string conversion functions + static std::string GetStringFromUnit(const CUnit& unit ); + static CUnit GetUnitFromString(const std::string& unit ); + // Constructor CUnit(); @@ -31,28 +40,23 @@ public: inline const player_id_t& getPlayer() const { return player_id; } inline const unitVis_c& getVisual() const { return unit_vis; } inline const dir_t& getDir() const { return dir; } - - inline Team setTeam(const Team & v) { return (team_id = v); } - inline player_id_t setPlayer(const player_id_t& v) { return ( player_id = v ); } - inline unitVis_c setVisual(const unitVis_c& v) { return ( unit_vis = v ); } - - // Set unit direction - inline dir_t setDir(const dir_t& v) { return (dir = v); } - inline const uvector2& getPos() const { return pos; } - inline void setPos(const uvector2& v) { pos = v; } + // Set + inline Team setTeam(const Team & v) { return (team_id = v); } + inline player_id_t setPlayer(const player_id_t& v) { return ( player_id = v ); } + inline unitVis_c setVisual(const unitVis_c& v) { return ( unit_vis = v ); } + inline dir_t setDir(const dir_t& v) { return (dir = v); } + inline void setPos(const uvector2& v) { pos = v; } - inline const unit_id_t& setIDForced(const unit_id_t& v) { return (unit_id = v); } - - // Get the co-ordinate infront of the unit - uvector2 getInFront() const; + // Get the co-ordinate in front of the unit + uvector2 getInFront() const; // Check unit is valid - inline bool valid() const; + inline bool valid() const; // Set a unit based solely on it's visual - bool setFromVisual( const unitVis_c& vis); + bool setFromVisual( const unitVis_c& vis); // Orientation methods dir_t turnLeft(); @@ -95,13 +99,4 @@ inline bool CUnit::valid() const && (unit_vis != unitVis_invalid); } - -// Factory function for creating units from a visual -CUnit GetUnitFromVis( unitVis_c vis ); - -// Unit <--> string conversion functions -#define UNIT_FORMATTER "UNIT:%u tm:%u pl:%u vs:%c dr:%c ps:[%u,%u]" -std::string GetStringFromUnit(const CUnit& unit ); -CUnit GetUnitFromString(const std::string& unit ); - #endif //_UNIT_H_ diff --git a/test/test.cpp b/test/test.cpp index 8637e03..903ecc1 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -26,8 +26,8 @@ const char* tests() { CUnit unit1; - std::string unit1Desc = GetStringFromUnit(unit1); - CUnit unit2 = GetUnitFromString(unit1Desc); + std::string unit1Desc = CUnit::GetStringFromUnit(unit1); + CUnit unit2 = CUnit::GetUnitFromString(unit1Desc); if ( unit1 != unit2 ) return "Failed to convert an empty unit to string and back"; @@ -41,8 +41,8 @@ const char* tests() unit1.setTeam(Team::Green); unit1.setPos( uvector2(5,10) ); - std::string unit1Desc = GetStringFromUnit(unit1); - CUnit unit2 = GetUnitFromString(unit1Desc); + std::string unit1Desc = CUnit::GetStringFromUnit(unit1); + CUnit unit2 = CUnit::GetUnitFromString(unit1Desc); if ( unit1 != unit2 ) return "Failed to convert custom unit to string and back"; @@ -50,7 +50,7 @@ const char* tests() // Test if we can successfully create a unit from a visual { - CUnit unit = GetUnitFromVis('v'); + CUnit unit = CUnit::GetUnitFromVis('v'); if( unit.getVisual() != 'v' ) return "failed to properly create V unit with factory"; } @@ -82,7 +82,7 @@ const char* tests() CTTRTSGame game( 5, 5 ); { - CUnit unit = GetUnitFromVis('^'); + CUnit unit = CUnit::GetUnitFromVis('^'); unit.setPos( {2,2} ); unit.setPlayer(0); unit.setTeam(Team::Red); @@ -91,7 +91,7 @@ const char* tests() } { - CUnit unit = GetUnitFromVis('^'); + CUnit unit = CUnit::GetUnitFromVis('^'); unit.setPos( {2,2} ); unit.setPlayer(0); unit.setTeam(Team::Red); @@ -108,7 +108,7 @@ const char* tests() { CTTRTSGame game( 5, 5 ); - CUnit unit = GetUnitFromVis('>'); + CUnit unit = CUnit::GetUnitFromVis('>'); const unit_id_t id = unit.getID(); COrder order; @@ -139,7 +139,7 @@ const char* tests() unit_id_t id; { - CUnit unit = GetUnitFromVis('>'); + CUnit unit = CUnit::GetUnitFromVis('>'); id = unit.getID(); COrder order; @@ -157,7 +157,7 @@ const char* tests() return "Game failed to issue valid order"; } { - CUnit unit = GetUnitFromVis('<'); + CUnit unit = CUnit::GetUnitFromVis('<'); unit.setPos( {1,0} ); unit.setPlayer(2); @@ -181,7 +181,15 @@ const char* tests() if ( game.CheckForWin() != Team::Blue ) return "Game failed to recognise a win for the right Team"; - std::cout< Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 055/190] New ttrts-gen target to generate game files --- CMakeLists.txt | 5 ++++- game/game.h | 1 + gen/CMakeLists.txt | 17 +++++++++++++++++ gen/gen.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ test/test.cpp | 1 + 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 gen/CMakeLists.txt create mode 100644 gen/gen.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bf5de03..f0e2055 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,5 +16,8 @@ endif() # Subprojects add_subdirectory( ttrts ) add_subdirectory( game ) -add_subdirectory( test ) add_subdirectory( ui ) + +# Auxhilary binaries +add_subdirectory( test ) +add_subdirectory( gen ) diff --git a/game/game.h b/game/game.h index fad3b68..a218b92 100644 --- a/game/game.h +++ b/game/game.h @@ -92,6 +92,7 @@ public: // Set the game name // NOTE: Names with spaces not allowed inline std::string SetName( const std::string& in ) { return (name = in); } + inline std::string GetName() const { return name; } // Set the turn of the game inline int SetTurn( int in ) { return (turn = in); } diff --git a/gen/CMakeLists.txt b/gen/CMakeLists.txt new file mode 100644 index 0000000..9eff3b8 --- /dev/null +++ b/gen/CMakeLists.txt @@ -0,0 +1,17 @@ +# ====================== gen ======================= +# Project name +project( ttrts-gen ) + +include_directories( + ../game + ../maths +) + +set( SOURCES + gen.cpp +) + +# Add the executable +add_executable( ttrts-gen ${SOURCES} ) + +target_link_libraries( ttrts-gen game ) \ No newline at end of file diff --git a/gen/gen.cpp b/gen/gen.cpp new file mode 100644 index 0000000..f020348 --- /dev/null +++ b/gen/gen.cpp @@ -0,0 +1,44 @@ +#include "game.h" + +#include +#include + +void AddUnitToGame( player_id_t player, Team team, char vis, uvector2 vec, CTTRTSGame& game ) +{ + CUnit unit = CUnit::GetUnitFromVis(vis); + unit.setPos( vec ); + unit.setPlayer(player); + unit.setTeam(team); + game.AddUnit(std::move(unit)); +} + +void OutputGame( CTTRTSGame& game ) +{ + std::ofstream output; + output.open (game.GetName() + ".txt"); + output << game.GetStateAsString(); + output.close(); +} + +int main() +{ + // Basic 5v5 game + { + CTTRTSGame game(21, 11); + game.SetName("Basic_5_v_5"); + + AddUnitToGame(0, Team::Blue, '>', uvector2(1, 1), game); + AddUnitToGame(0, Team::Blue, '>', uvector2(1, 3), game); + AddUnitToGame(0, Team::Blue, '>', uvector2(1, 5), game); + AddUnitToGame(0, Team::Blue, '>', uvector2(1, 7), game); + AddUnitToGame(0, Team::Blue, '>', uvector2(1, 9), game); + + AddUnitToGame(1, Team::Red, '<', uvector2(19, 1), game); + AddUnitToGame(1, Team::Red, '<', uvector2(19, 3), game); + AddUnitToGame(1, Team::Red, '<', uvector2(19, 5), game); + AddUnitToGame(1, Team::Red, '<', uvector2(19, 7), game); + AddUnitToGame(1, Team::Red, '<', uvector2(19, 9), game); + + OutputGame(game); + } +} \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp index 903ecc1..b604f92 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -136,6 +136,7 @@ const char* tests() // Test on a tiny board, whether a unit can correctly attack another { CTTRTSGame game( 2, 1 ); + game.SetName("Test_578"); unit_id_t id; { From d0d3834449872fb62ddac4e16e561d03d3dd66fb Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:02 +0000 Subject: [PATCH 056/190] Move source code into source subfolder --- CMakeLists.txt => source/CMakeLists.txt | 0 {game => source/game}/CMakeLists.txt | 0 {game => source/game}/README.md | 0 {game => source/game}/game.cpp | 0 {game => source/game}/game.h | 0 {game => source/game}/gametypes.h | 0 {game => source/game}/order.cpp | 0 {game => source/game}/order.h | 0 {game => source/game}/unit.cpp | 0 {game => source/game}/unit.h | 0 {gen => source/gen}/CMakeLists.txt | 0 {gen => source/gen}/gen.cpp | 0 {maths => source/maths}/mathtypes.h | 0 {maths => source/maths}/vector2.h | 0 {net => source/net}/CMakeLists.txt | 0 {player => source/player}/CMakeLists.txt | 0 {test => source/test}/CMakeLists.txt | 0 {test => source/test}/test.cpp | 0 {ttrts => source/ttrts}/CMakeLists.txt | 0 {ttrts => source/ttrts}/main.cpp | 0 {ui => source/ui}/CMakeLists.txt | 0 {ui => source/ui}/board.cpp | 0 {ui => source/ui}/board.h | 0 23 files changed, 0 insertions(+), 0 deletions(-) rename CMakeLists.txt => source/CMakeLists.txt (100%) rename {game => source/game}/CMakeLists.txt (100%) rename {game => source/game}/README.md (100%) rename {game => source/game}/game.cpp (100%) rename {game => source/game}/game.h (100%) rename {game => source/game}/gametypes.h (100%) rename {game => source/game}/order.cpp (100%) rename {game => source/game}/order.h (100%) rename {game => source/game}/unit.cpp (100%) rename {game => source/game}/unit.h (100%) rename {gen => source/gen}/CMakeLists.txt (100%) rename {gen => source/gen}/gen.cpp (100%) rename {maths => source/maths}/mathtypes.h (100%) rename {maths => source/maths}/vector2.h (100%) rename {net => source/net}/CMakeLists.txt (100%) rename {player => source/player}/CMakeLists.txt (100%) rename {test => source/test}/CMakeLists.txt (100%) rename {test => source/test}/test.cpp (100%) rename {ttrts => source/ttrts}/CMakeLists.txt (100%) rename {ttrts => source/ttrts}/main.cpp (100%) rename {ui => source/ui}/CMakeLists.txt (100%) rename {ui => source/ui}/board.cpp (100%) rename {ui => source/ui}/board.h (100%) diff --git a/CMakeLists.txt b/source/CMakeLists.txt similarity index 100% rename from CMakeLists.txt rename to source/CMakeLists.txt diff --git a/game/CMakeLists.txt b/source/game/CMakeLists.txt similarity index 100% rename from game/CMakeLists.txt rename to source/game/CMakeLists.txt diff --git a/game/README.md b/source/game/README.md similarity index 100% rename from game/README.md rename to source/game/README.md diff --git a/game/game.cpp b/source/game/game.cpp similarity index 100% rename from game/game.cpp rename to source/game/game.cpp diff --git a/game/game.h b/source/game/game.h similarity index 100% rename from game/game.h rename to source/game/game.h diff --git a/game/gametypes.h b/source/game/gametypes.h similarity index 100% rename from game/gametypes.h rename to source/game/gametypes.h diff --git a/game/order.cpp b/source/game/order.cpp similarity index 100% rename from game/order.cpp rename to source/game/order.cpp diff --git a/game/order.h b/source/game/order.h similarity index 100% rename from game/order.h rename to source/game/order.h diff --git a/game/unit.cpp b/source/game/unit.cpp similarity index 100% rename from game/unit.cpp rename to source/game/unit.cpp diff --git a/game/unit.h b/source/game/unit.h similarity index 100% rename from game/unit.h rename to source/game/unit.h diff --git a/gen/CMakeLists.txt b/source/gen/CMakeLists.txt similarity index 100% rename from gen/CMakeLists.txt rename to source/gen/CMakeLists.txt diff --git a/gen/gen.cpp b/source/gen/gen.cpp similarity index 100% rename from gen/gen.cpp rename to source/gen/gen.cpp diff --git a/maths/mathtypes.h b/source/maths/mathtypes.h similarity index 100% rename from maths/mathtypes.h rename to source/maths/mathtypes.h diff --git a/maths/vector2.h b/source/maths/vector2.h similarity index 100% rename from maths/vector2.h rename to source/maths/vector2.h diff --git a/net/CMakeLists.txt b/source/net/CMakeLists.txt similarity index 100% rename from net/CMakeLists.txt rename to source/net/CMakeLists.txt diff --git a/player/CMakeLists.txt b/source/player/CMakeLists.txt similarity index 100% rename from player/CMakeLists.txt rename to source/player/CMakeLists.txt diff --git a/test/CMakeLists.txt b/source/test/CMakeLists.txt similarity index 100% rename from test/CMakeLists.txt rename to source/test/CMakeLists.txt diff --git a/test/test.cpp b/source/test/test.cpp similarity index 100% rename from test/test.cpp rename to source/test/test.cpp diff --git a/ttrts/CMakeLists.txt b/source/ttrts/CMakeLists.txt similarity index 100% rename from ttrts/CMakeLists.txt rename to source/ttrts/CMakeLists.txt diff --git a/ttrts/main.cpp b/source/ttrts/main.cpp similarity index 100% rename from ttrts/main.cpp rename to source/ttrts/main.cpp diff --git a/ui/CMakeLists.txt b/source/ui/CMakeLists.txt similarity index 100% rename from ui/CMakeLists.txt rename to source/ui/CMakeLists.txt diff --git a/ui/board.cpp b/source/ui/board.cpp similarity index 100% rename from ui/board.cpp rename to source/ui/board.cpp diff --git a/ui/board.h b/source/ui/board.h similarity index 100% rename from ui/board.h rename to source/ui/board.h From 572bc39ecf6ff5003823f31a94edbd2f9718d34a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:03 +0000 Subject: [PATCH 057/190] Add basic 5v5 game file --- games/Basic_5_v_5.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 games/Basic_5_v_5.txt diff --git a/games/Basic_5_v_5.txt b/games/Basic_5_v_5.txt new file mode 100644 index 0000000..c4aeb10 --- /dev/null +++ b/games/Basic_5_v_5.txt @@ -0,0 +1,14 @@ +===== Basic_5_v_5 ===== +SIZE:[21,11] +TURN:0 +~~~~ +UNIT:0 tm:1 pl:0 vs:> dr:E ps:[1,1] +UNIT:1 tm:1 pl:0 vs:> dr:E ps:[1,3] +UNIT:2 tm:1 pl:0 vs:> dr:E ps:[1,5] +UNIT:3 tm:1 pl:0 vs:> dr:E ps:[1,7] +UNIT:4 tm:1 pl:0 vs:> dr:E ps:[1,9] +UNIT:5 tm:0 pl:1 vs:< dr:W ps:[19,1] +UNIT:6 tm:0 pl:1 vs:< dr:W ps:[19,3] +UNIT:7 tm:0 pl:1 vs:< dr:W ps:[19,5] +UNIT:8 tm:0 pl:1 vs:< dr:W ps:[19,7] +UNIT:9 tm:0 pl:1 vs:< dr:W ps:[19,9] From e0013967445a5bd47657b6c6f8de2dd8dfc95f2d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:03 +0000 Subject: [PATCH 058/190] Add bootstrap script to generate build files, binaries, run tests and generate maps --- bootstrap.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100755 bootstrap.sh diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..d882c3a --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,36 @@ +#! /bin/bash + +echo "TTRTS: Running cmake" +cd build/ +cmake ../source +if [[ $? != 0 ]]; then + echo "TTRTS: CMake failed, exiting Bootstrap" +fi + +echo "TTRTS: Running make" +make +if [[ $? != 0 ]]; then + echo "TTRTS: make failed, exiting Bootstrap" +fi + +echo "TTRTS: Running tests" +./test/ttrts-test +if [[ $? != 0 ]]; then + echo "TTRTS: Tests failed, build must be broken" +fi + +echo "TTRTS: Generating maps" +cd ../games +./../build/gen/ttrts-gen +if [[ $? != 0 ]]; then + echo "TTRTS: Generating maps, exiting Bootstrap" +fi + +echo "TTRTS: Moving binaries" +cd .. +if [ ! -e build/ttrts/ttrts ]; then + echo "TTRTS: No TTRTS Binary found, something has gone wrong" +fi + +cp build/ttrts/ttrts . +chmod a+x ttrts \ No newline at end of file From a90ad9f90cfb7d5b3a1b6bd8e135ba251210c0f5 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:03 +0000 Subject: [PATCH 059/190] Remove player IDs, keep it simple stupid --- games/Basic_5_v_5.txt | 20 ++++++++++---------- source/game/game.cpp | 14 +++++++------- source/game/game.h | 8 ++++---- source/game/gametypes.h | 2 -- source/game/unit.cpp | 8 -------- source/game/unit.h | 8 +------- source/gen/gen.cpp | 23 +++++++++++------------ source/test/test.cpp | 10 ++-------- 8 files changed, 35 insertions(+), 58 deletions(-) diff --git a/games/Basic_5_v_5.txt b/games/Basic_5_v_5.txt index c4aeb10..6ea6afb 100644 --- a/games/Basic_5_v_5.txt +++ b/games/Basic_5_v_5.txt @@ -2,13 +2,13 @@ SIZE:[21,11] TURN:0 ~~~~ -UNIT:0 tm:1 pl:0 vs:> dr:E ps:[1,1] -UNIT:1 tm:1 pl:0 vs:> dr:E ps:[1,3] -UNIT:2 tm:1 pl:0 vs:> dr:E ps:[1,5] -UNIT:3 tm:1 pl:0 vs:> dr:E ps:[1,7] -UNIT:4 tm:1 pl:0 vs:> dr:E ps:[1,9] -UNIT:5 tm:0 pl:1 vs:< dr:W ps:[19,1] -UNIT:6 tm:0 pl:1 vs:< dr:W ps:[19,3] -UNIT:7 tm:0 pl:1 vs:< dr:W ps:[19,5] -UNIT:8 tm:0 pl:1 vs:< dr:W ps:[19,7] -UNIT:9 tm:0 pl:1 vs:< dr:W ps:[19,9] +UNIT:0 tm:1 vs:> dr:E ps:[1,1] +UNIT:1 tm:1 vs:> dr:E ps:[1,3] +UNIT:2 tm:1 vs:> dr:E ps:[1,5] +UNIT:3 tm:1 vs:> dr:E ps:[1,7] +UNIT:4 tm:1 vs:> dr:E ps:[1,9] +UNIT:5 tm:0 vs:< dr:W ps:[19,1] +UNIT:6 tm:0 vs:< dr:W ps:[19,3] +UNIT:7 tm:0 vs:< dr:W ps:[19,5] +UNIT:8 tm:0 vs:< dr:W ps:[19,7] +UNIT:9 tm:0 vs:< dr:W ps:[19,9] diff --git a/source/game/game.cpp b/source/game/game.cpp index c3d8df4..1f811e5 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -30,7 +30,7 @@ CTTRTSGame& CTTRTSGame::operator=(CTTRTSGame&& game) } // Interpret a string of orders -int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) +int CTTRTSGame::IssueOrders( Team team, const std::string& _orders ) { COrderVector orderVector; @@ -53,17 +53,17 @@ int CTTRTSGame::IssueOrders( player_id_t player, const std::string& _orders ) } // Call our add order by vector method - return IssueOrders(player,orderVector); + return IssueOrders(team,orderVector); } // Issue orders by vector to the game -int CTTRTSGame::IssueOrders( player_id_t player, const COrderVector& orders ) +int CTTRTSGame::IssueOrders( Team team, const COrderVector& orders ) { // verify all the orders for ( auto order : orders ) { // If any order returns non-zero, back out - if ( IssueOrder(player,order) ) + if ( IssueOrder(team,order) ) return 1; } @@ -71,10 +71,10 @@ int CTTRTSGame::IssueOrders( player_id_t player, const COrderVector& orders ) } // Issue a single order -int CTTRTSGame::IssueOrder( player_id_t player, const COrder& order ) +int CTTRTSGame::IssueOrder( Team team, const COrder& order ) { // Verify the order - if ( VerifyOrder(player,order) ) + if ( VerifyOrder(team,order) ) return 1; // Get the right unit for the order @@ -290,7 +290,7 @@ int CTTRTSGame::AddUnits( CUnitVector&& units ) } // Verify any order -int CTTRTSGame::VerifyOrder( player_id_t player, const COrder& order ) const +int CTTRTSGame::VerifyOrder( Team team, const COrder& order ) const { int ret = 1; diff --git a/source/game/game.h b/source/game/game.h index a218b92..b0c3fad 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -68,9 +68,9 @@ public: std::string GetStateAsString() const; // Issue orders to the game, returns non-zero if orders are incorrect - int IssueOrders( player_id_t player, const std::string& orders ); - int IssueOrders( player_id_t player, const COrderVector& orders ); - int IssueOrder( player_id_t player, const COrder& order ); + int IssueOrders( Team team, const std::string& orders ); + int IssueOrders( Team team, const COrderVector& orders ); + int IssueOrder( Team team, const COrder& order ); // Add a units to the game, nonzero return value indicates error int AddUnit( CUnit&& unit ); @@ -100,7 +100,7 @@ public: private: // Verify any order or position - non-zero is error - int VerifyOrder( player_id_t player, const COrder& order ) const; + int VerifyOrder( Team team, const COrder& order ) const; int VerifyPos( uvector2 vec ) const; // Get a units new position after an order diff --git a/source/game/gametypes.h b/source/game/gametypes.h index b603cf9..c3ce010 100644 --- a/source/game/gametypes.h +++ b/source/game/gametypes.h @@ -14,11 +14,9 @@ enum class Team : char }; -typedef unsigned char player_id_t; // Type for player IDs typedef unsigned short unit_id_t; // Type for unit IDs typedef char unitVis_c; // Typedef for unit visual representations -static const player_id_t player_id_invalid = std::numeric_limits::max(); static const unit_id_t unit_id_invalid = std::numeric_limits::max(); static const unitVis_c unitVis_invalid = std::numeric_limits::max(); diff --git a/source/game/unit.cpp b/source/game/unit.cpp index d7470d0..4bdaaa8 100644 --- a/source/game/unit.cpp +++ b/source/game/unit.cpp @@ -48,7 +48,6 @@ std::string CUnit::GetStringFromUnit(const CUnit& unit ) snprintf(buff,128, UNIT_FORMATTER, unit.unit_id, (int)unit.team_id, - unit.player_id, unit.unit_vis, unit.dir, unit.pos.x, @@ -65,7 +64,6 @@ CUnit CUnit::GetUnitFromString(const std::string& unit ) unsigned int id; int team; - unsigned int player; char vis; char dir; unsigned int posx; @@ -74,7 +72,6 @@ CUnit CUnit::GetUnitFromString(const std::string& unit ) sscanf(unit.c_str(), UNIT_FORMATTER, &id, &team, - &player, &vis, &dir, &posx, @@ -82,7 +79,6 @@ CUnit CUnit::GetUnitFromString(const std::string& unit ) ret.unit_id = (unit_id_t)id; ret.team_id = (Team)team; - ret.player_id = (player_id_t)player; ret.unit_vis = (unitVis_c)vis; ret.dir = (dir_t)dir; ret.pos = uvector2(posx,posy); @@ -95,7 +91,6 @@ CUnit::CUnit() : unit_id ( get_unique_unit_id() ) , team_id ( Team::NUM_INVALID ) , unit_vis ( unitVis_invalid ) -, player_id ( player_id_invalid ) , dir ( dir_t::S ) , pos ( { ucoord_invalid, ucoord_invalid } ) { @@ -106,7 +101,6 @@ CUnit::CUnit() CUnit::CUnit(CUnit&& unit) : unit_id ( std::move(unit.unit_id) ) , team_id ( std::move(unit.team_id) ) -, player_id ( std::move(unit.player_id) ) , unit_vis ( std::move(unit.unit_vis) ) , dir ( std::move(unit.dir) ) , pos ( std::move(unit.pos) ) @@ -120,7 +114,6 @@ CUnit& CUnit::operator=(CUnit&& unit) { unit_id = std::move(unit.unit_id) ; team_id = std::move(unit.team_id) ; - player_id = std::move(unit.player_id) ; unit_vis = std::move(unit.unit_vis) ; dir = std::move(unit.dir) ; pos = std::move(unit.pos) ; @@ -132,7 +125,6 @@ bool CUnit::operator==(const CUnit& rhs) { return (unit_id == rhs.unit_id) && (team_id == rhs.team_id) - && (player_id == rhs.player_id) && (unit_vis == rhs.unit_vis) && (dir == rhs.dir) && (pos == rhs.pos); diff --git a/source/game/unit.h b/source/game/unit.h index cd9cff7..90003b5 100644 --- a/source/game/unit.h +++ b/source/game/unit.h @@ -7,7 +7,7 @@ #include "gametypes.h" #include "vector2.h" -#define UNIT_FORMATTER "UNIT:%u tm:%u pl:%u vs:%c dr:%c ps:[%u,%u]" +#define UNIT_FORMATTER "UNIT:%u tm:%u vs:%c dr:%c ps:[%u,%u]" // Base unit type class CUnit @@ -37,14 +37,12 @@ public: // Getters for all the members inline const unit_id_t& getID() const { return unit_id; } inline const Team & getTeam() const { return team_id; } - inline const player_id_t& getPlayer() const { return player_id; } inline const unitVis_c& getVisual() const { return unit_vis; } inline const dir_t& getDir() const { return dir; } inline const uvector2& getPos() const { return pos; } // Set inline Team setTeam(const Team & v) { return (team_id = v); } - inline player_id_t setPlayer(const player_id_t& v) { return ( player_id = v ); } inline unitVis_c setVisual(const unitVis_c& v) { return ( unit_vis = v ); } inline dir_t setDir(const dir_t& v) { return (dir = v); } inline void setPos(const uvector2& v) { pos = v; } @@ -77,9 +75,6 @@ private: // Team ID Team team_id; - // Owner ID - player_id_t player_id; - // Direction dir_t dir; @@ -95,7 +90,6 @@ inline bool CUnit::valid() const { return (unit_id != unit_id_invalid ) && (team_id != Team::NUM_INVALID ) - && (player_id != player_id_invalid) && (unit_vis != unitVis_invalid); } diff --git a/source/gen/gen.cpp b/source/gen/gen.cpp index f020348..d598d89 100644 --- a/source/gen/gen.cpp +++ b/source/gen/gen.cpp @@ -3,11 +3,10 @@ #include #include -void AddUnitToGame( player_id_t player, Team team, char vis, uvector2 vec, CTTRTSGame& game ) +void AddUnitToGame( Team team, char vis, uvector2 vec, CTTRTSGame& game ) { CUnit unit = CUnit::GetUnitFromVis(vis); unit.setPos( vec ); - unit.setPlayer(player); unit.setTeam(team); game.AddUnit(std::move(unit)); } @@ -27,17 +26,17 @@ int main() CTTRTSGame game(21, 11); game.SetName("Basic_5_v_5"); - AddUnitToGame(0, Team::Blue, '>', uvector2(1, 1), game); - AddUnitToGame(0, Team::Blue, '>', uvector2(1, 3), game); - AddUnitToGame(0, Team::Blue, '>', uvector2(1, 5), game); - AddUnitToGame(0, Team::Blue, '>', uvector2(1, 7), game); - AddUnitToGame(0, Team::Blue, '>', uvector2(1, 9), game); + AddUnitToGame( Team::Blue, '>', uvector2(1, 1), game); + AddUnitToGame( Team::Blue, '>', uvector2(1, 3), game); + AddUnitToGame( Team::Blue, '>', uvector2(1, 5), game); + AddUnitToGame( Team::Blue, '>', uvector2(1, 7), game); + AddUnitToGame( Team::Blue, '>', uvector2(1, 9), game); - AddUnitToGame(1, Team::Red, '<', uvector2(19, 1), game); - AddUnitToGame(1, Team::Red, '<', uvector2(19, 3), game); - AddUnitToGame(1, Team::Red, '<', uvector2(19, 5), game); - AddUnitToGame(1, Team::Red, '<', uvector2(19, 7), game); - AddUnitToGame(1, Team::Red, '<', uvector2(19, 9), game); + AddUnitToGame( Team::Red, '<', uvector2(19, 1), game); + AddUnitToGame( Team::Red, '<', uvector2(19, 3), game); + AddUnitToGame( Team::Red, '<', uvector2(19, 5), game); + AddUnitToGame( Team::Red, '<', uvector2(19, 7), game); + AddUnitToGame( Team::Red, '<', uvector2(19, 9), game); OutputGame(game); } diff --git a/source/test/test.cpp b/source/test/test.cpp index b604f92..fdabe5b 100644 --- a/source/test/test.cpp +++ b/source/test/test.cpp @@ -37,7 +37,6 @@ const char* tests() { CUnit unit1; unit1.setFromVisual('v'); - unit1.setPlayer(0); unit1.setTeam(Team::Green); unit1.setPos( uvector2(5,10) ); @@ -84,7 +83,6 @@ const char* tests() { CUnit unit = CUnit::GetUnitFromVis('^'); unit.setPos( {2,2} ); - unit.setPlayer(0); unit.setTeam(Team::Red); game.AddUnit(std::move(unit)); @@ -93,7 +91,6 @@ const char* tests() { CUnit unit = CUnit::GetUnitFromVis('^'); unit.setPos( {2,2} ); - unit.setPlayer(0); unit.setTeam(Team::Red); if( !game.AddUnit(std::move(unit)) ) @@ -113,7 +110,6 @@ const char* tests() COrder order; unit.setPos( {2,2} ); - unit.setPlayer(0); unit.setTeam(Team::Red); if ( game.AddUnit(std::move(unit)) ) @@ -122,7 +118,7 @@ const char* tests() order.unit = id; order.command = command_c::F; - if( game.IssueOrder(0,order) ) + if( game.IssueOrder(Team::Red,order) ) return "Game failed to issue valid order"; if (game.SimulateToNextTurn() ) @@ -145,7 +141,6 @@ const char* tests() COrder order; unit.setPos( {0,0} ); - unit.setPlayer(1); unit.setTeam(Team::Blue); if ( game.AddUnit(std::move(unit)) ) @@ -154,14 +149,13 @@ const char* tests() order.unit = id; order.command = command_c::A; - if( game.IssueOrder(0,order) ) + if( game.IssueOrder(Team::Blue,order) ) return "Game failed to issue valid order"; } { CUnit unit = CUnit::GetUnitFromVis('<'); unit.setPos( {1,0} ); - unit.setPlayer(2); unit.setTeam(Team::Red); if ( game.AddUnit(std::move(unit)) ) From 2a410d98c0af914bee78fa5f443aa45d005d8ca9 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:03 +0000 Subject: [PATCH 060/190] Set up a basic ttrts binary It takes a game description from the command line. Looks in a folder 'ttrts_gamename' for turn commands Reads those in and simulates until a winner is found --- source/game/game.cpp | 19 +++++ source/game/game.h | 4 + source/ttrts/CMakeLists.txt | 9 ++- source/ttrts/main.cpp | 155 +++++++++++++++++++++++++++++++++++- 4 files changed, 184 insertions(+), 3 deletions(-) diff --git a/source/game/game.cpp b/source/game/game.cpp index 1f811e5..8e01b2e 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -342,6 +342,25 @@ CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) return invalid_unit; } +// Get a vector of the teams in the current game +std::vector CTTRTSGame::GetTeams() const +{ + std::vector teams; + teams.reserve(GetNumUnits()); + + // Grab all teams + for ( const OrderUnitPair& pair : m_OrderUnitPairs ) + { + teams.push_back(pair.unit.getTeam()); + } + + // Remove dupes + std::sort( teams.begin(), teams.end() ); + teams.erase( std::unique( teams.begin(), teams.end() ), teams.end() ); + + return teams; +} + // Check if we have a win state Team CTTRTSGame::CheckForWin() const { diff --git a/source/game/game.h b/source/game/game.h index b0c3fad..b763071 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -96,6 +96,10 @@ public: // Set the turn of the game inline int SetTurn( int in ) { return (turn = in); } + inline int GetTurn() const { return turn; } + + // Get a vector of the teams in the current game + std::vector GetTeams() const; private: diff --git a/source/ttrts/CMakeLists.txt b/source/ttrts/CMakeLists.txt index 8258be5..08af9fb 100644 --- a/source/ttrts/CMakeLists.txt +++ b/source/ttrts/CMakeLists.txt @@ -2,10 +2,17 @@ # Project name project( ttrts ) +include_directories( + ../maths + ../game +) + # Add the sources set( SOURCES main.cpp ) # Add the executable -add_executable( ttrts ${SOURCES} ) \ No newline at end of file +add_executable( ttrts ${SOURCES} ) + +target_link_libraries( ttrts game ) \ No newline at end of file diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index 02fcc4c..6b1740e 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -1,5 +1,156 @@ -// Main program entry point -int main() +#include +#include +#include +#include +#include + +#include "game.h" + +static const char* sk_usage = "NAME\n" + "\tttrts - Tiny Terminal RTS\n" + "\n" + "SYNOPSYS\n" + "\tttrts [OPTIONS...] infile\n" + "\n" + "DESCRIPTION\n" + "\tWhen invoked, ttrts will set up a full game and output a single file representing\n" + "\tthe current game state.\n" + "\tThis file can be read in and interpretted by human, robot, or cat.\n" + "\tttrts will wait for orders files to be placed in it's current working directory.\n" + "\tOnce orders have been set for each player taking part, ttrts consumes order files,\n" + "\tcalculates new game state and outputs a new file.\n" + "\n" + "\tThis process repeats until a winner is chosen!\n"; + +// time for waiting between file stats +static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100); + +// Check if a file exists +inline bool FileExists( const std::string& name ) { + struct stat buffer; + return (stat (name.c_str(), &buffer) == 0); +} + +// Wait for a file to exist +inline void WaitForFile( const std::string& name, const std::chrono::milliseconds& time ) +{ + while( !FileExists(name) ) std::this_thread::sleep_for(time); +} + +// Main program entry point +int main(int argc, char* argv[]) +{ + // If no args, print usage + if ( argc == 1 ) + { + std::cerr<(file)),std::istreambuf_iterator()); + + if( gameDescriptor.size() == 0 ) + { + std::cerr<<"Error: failed to read in any information from "<(turnFile)),std::istreambuf_iterator()); + + // Issue the orders to the game + if( game.IssueOrders(team, orders) ) + std::cerr<<"Warning: Orders for team "<<(int)team<<" failed to correctly parse"< Date: Tue, 16 Dec 2014 13:13:03 +0000 Subject: [PATCH 061/190] Remove the prebuilt games directory, these should be generated with bootstrap.sh --- games/Basic_5_v_5.txt | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 games/Basic_5_v_5.txt diff --git a/games/Basic_5_v_5.txt b/games/Basic_5_v_5.txt deleted file mode 100644 index 6ea6afb..0000000 --- a/games/Basic_5_v_5.txt +++ /dev/null @@ -1,14 +0,0 @@ -===== Basic_5_v_5 ===== -SIZE:[21,11] -TURN:0 -~~~~ -UNIT:0 tm:1 vs:> dr:E ps:[1,1] -UNIT:1 tm:1 vs:> dr:E ps:[1,3] -UNIT:2 tm:1 vs:> dr:E ps:[1,5] -UNIT:3 tm:1 vs:> dr:E ps:[1,7] -UNIT:4 tm:1 vs:> dr:E ps:[1,9] -UNIT:5 tm:0 vs:< dr:W ps:[19,1] -UNIT:6 tm:0 vs:< dr:W ps:[19,3] -UNIT:7 tm:0 vs:< dr:W ps:[19,5] -UNIT:8 tm:0 vs:< dr:W ps:[19,7] -UNIT:9 tm:0 vs:< dr:W ps:[19,9] From 24e17450aad3908e8c3cd9c3958fa087de4e618d Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:13:04 +0000 Subject: [PATCH 062/190] Change the games folder to maps and add it to the gitignore, updating the USAGE text to reflect this --- .gitignore | 2 ++ USAGE.txt | 56 +++++++++++++++++++++++++++++++++++++++++++ bootstrap.sh | 5 ++-- source/ttrts/main.cpp | 48 +++++++++++++++++++++++++++++++++---- 4 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 USAGE.txt diff --git a/.gitignore b/.gitignore index 03c1bdc..8685940 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ build +maps +ttrts *.user *.sublime* .idea diff --git a/USAGE.txt b/USAGE.txt new file mode 100644 index 0000000..ad7dacb --- /dev/null +++ b/USAGE.txt @@ -0,0 +1,56 @@ +To convert this file to c++ syntax use +$ cat USAGE.txt | sed 's/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n\n/g' | sed 's/^/"/' | sed 's/$/"/' + +NAME + ttrts - Tiny Terminal RTS + +SYNOPSYS + ttrts [OPTIONS...] MAPFILE + +DESCRIPTION + ttrts is a tiny terminal based RTS where that uses text files as order lists to control it's units. + + This means that any user, program or cat that can read and write to text files can play the game. + +USAGE + When invoked, ttrts will set up a full game and output a single file representing the current gamestate into a local directory called ttrts_{GAME_NAME}. + + This file can be read in and interpretted by human, robot, or cat. + ttrts will wait for orders files to be placed in it's current working directory. + + File name formats: + gamestate Turn_{TURN_NUMBER}.txt + orders Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt + + Once orders have been set for each player taking part, ttrts consumes order files, + calculates new game state and outputs a new file. + + This process repeats until a winner is chosen! + +OPTIONS + MAPFILE: + File to read in the initial game state from + +GAMESTATE FILE FORMAT + Name + Turn_{TURN_NUMBER}.txt + Contents + ===== {GAME_NAME} ===== + SIZE:[{X},{Y}] + TURN:{TURN_NUMBER} + ~~~~ + UNIT:{UNIT_ID} tm:{TEAM_NUMBER} vs:{VISUAL} dr:{DIRECTION(NESW)} ps:[{X},{Y}] + ... + +ORDER FILE FORMAT + Name + Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt + Contents + ORDER:{ORDER_CHAR} id:{UNIT_ID} + ... + +ORDERS + F - Move unit forward one space + L/R - Rotate unit left or right + A - Attack space in front of unit + diff --git a/bootstrap.sh b/bootstrap.sh index d882c3a..8780bc3 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -20,7 +20,8 @@ if [[ $? != 0 ]]; then fi echo "TTRTS: Generating maps" -cd ../games +test ! -e ../maps && mkdir ../maps +cd ../maps ./../build/gen/ttrts-gen if [[ $? != 0 ]]; then echo "TTRTS: Generating maps, exiting Bootstrap" @@ -33,4 +34,4 @@ if [ ! -e build/ttrts/ttrts ]; then fi cp build/ttrts/ttrts . -chmod a+x ttrts \ No newline at end of file +chmod a+x ttrts diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index 6b1740e..2c6f8e4 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -6,21 +6,59 @@ #include "game.h" -static const char* sk_usage = "NAME\n" +static const char* sk_usage = + "NAME\n" "\tttrts - Tiny Terminal RTS\n" "\n" "SYNOPSYS\n" - "\tttrts [OPTIONS...] infile\n" + "\tttrts [OPTIONS...] MAPFILE\n" "\n" "DESCRIPTION\n" - "\tWhen invoked, ttrts will set up a full game and output a single file representing\n" - "\tthe current game state.\n" + "\tttrts is a tiny terminal based RTS where that uses text files as order lists to control it's units.\n" + "\n" + "\tThis means that any user, program or cat that can read and write to text files can play the game.\n" + "\n" + "USAGE\n" + "\tWhen invoked, ttrts will set up a full game and output a single file representing the current gamestate into a local directory called ttrts_{GAME_NAME}.\n" + "\n" "\tThis file can be read in and interpretted by human, robot, or cat.\n" "\tttrts will wait for orders files to be placed in it's current working directory.\n" + "\n" + "\tFile name formats:\n" + "\t gamestate Turn_{TURN_NUMBER}.txt\n" + "\t orders Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" + "\n" "\tOnce orders have been set for each player taking part, ttrts consumes order files,\n" "\tcalculates new game state and outputs a new file.\n" "\n" - "\tThis process repeats until a winner is chosen!\n"; + "\tThis process repeats until a winner is chosen!\n" + "\n" + "OPTIONS\n" + "\tMAPFILE:\n" + "\t\tFile to read in the initial game state from\n" + "\n" + "GAMESTATE FILE FORMAT\n" + "\tName\n" + "\t\tTurn_{TURN_NUMBER}.txt\n" + "\tContents\n" + "\t\t===== {GAME_NAME} =====\n" + "\t\tSIZE:[{X},{Y}]\n" + "\t\tTURN:{TURN_NUMBER}\n" + "\t\t~~~~\n" + "\t\tUNIT:{UNIT_ID} tm:{TEAM_NUMBER} vs:{VISUAL} dr:{DIRECTION(NESW)} ps:[{X},{Y}]\n" + "\t\t...\n" + "\n" + "ORDER FILE FORMAT\n" + "\tName\n" + "\t\tTurn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" + "\tContents\n" + "\t\tORDER:{ORDER_CHAR} id:{UNIT_ID}\n" + "\t\t...\n" + "\n" + "ORDERS\n" + "\tF - Move unit forward one space\n" + "\tL/R - Rotate unit left or right\n" + "\tA - Attack space in front of unit\n"; // time for waiting between file stats static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100); From dbfd932a6442e54c6688097afd6aca884f88ffbf Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Tue, 16 Dec 2014 13:33:11 +0000 Subject: [PATCH 063/190] Update usage and gitignore to not exlude code folders --- .gitignore | 7 ++- USAGE.txt | 44 +++++++++-------- source/ttrts/main.cpp | 108 +++++++++++++++++++++--------------------- 3 files changed, 81 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index 8685940..030061a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ -build -maps -ttrts +build/ +maps/ *.user *.sublime* -.idea +*.idea diff --git a/USAGE.txt b/USAGE.txt index ad7dacb..c3bd574 100644 --- a/USAGE.txt +++ b/USAGE.txt @@ -8,22 +8,24 @@ SYNOPSYS ttrts [OPTIONS...] MAPFILE DESCRIPTION - ttrts is a tiny terminal based RTS where that uses text files as order lists to control it's units. + ttrts is a tiny terminal based RTS where that uses text + files as order lists to control it's units. - This means that any user, program or cat that can read and write to text files can play the game. + This means that any user, program or cat that can read + and write to text files can play the game. USAGE - When invoked, ttrts will set up a full game and output a single file representing the current gamestate into a local directory called ttrts_{GAME_NAME}. + When invoked, ttrts will set up a full game and output a + single file representing the current gamestate into a + local directory called ttrts_{GAME_NAME}. - This file can be read in and interpretted by human, robot, or cat. - ttrts will wait for orders files to be placed in it's current working directory. + This file can be read in and interpretted by human, robot + or cat. ttrts will wait for orders files to be placed in + it's current working directory. - File name formats: - gamestate Turn_{TURN_NUMBER}.txt - orders Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt - - Once orders have been set for each player taking part, ttrts consumes order files, - calculates new game state and outputs a new file. + Once orders have been set for each player taking part + ttrts will calculate the new game state and output a new + gamestate file for the next turn. This process repeats until a winner is chosen! @@ -33,21 +35,21 @@ OPTIONS GAMESTATE FILE FORMAT Name - Turn_{TURN_NUMBER}.txt + Turn_{TURN_NUMBER}.txt Contents - ===== {GAME_NAME} ===== - SIZE:[{X},{Y}] - TURN:{TURN_NUMBER} - ~~~~ - UNIT:{UNIT_ID} tm:{TEAM_NUMBER} vs:{VISUAL} dr:{DIRECTION(NESW)} ps:[{X},{Y}] - ... + ===== {GAME_NAME} ===== + SIZE:[{X},{Y}] + TURN:{TURN_NUMBER} + ~~~~ + UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}] + ... ORDER FILE FORMAT Name - Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt + Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt Contents - ORDER:{ORDER_CHAR} id:{UNIT_ID} - ... + ORDER:{ORDER_CHAR} id:{UNIT_ID} + ... ORDERS F - Move unit forward one space diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index 2c6f8e4..ccf47a0 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -7,58 +7,60 @@ #include "game.h" static const char* sk_usage = - "NAME\n" - "\tttrts - Tiny Terminal RTS\n" - "\n" - "SYNOPSYS\n" - "\tttrts [OPTIONS...] MAPFILE\n" - "\n" - "DESCRIPTION\n" - "\tttrts is a tiny terminal based RTS where that uses text files as order lists to control it's units.\n" - "\n" - "\tThis means that any user, program or cat that can read and write to text files can play the game.\n" - "\n" - "USAGE\n" - "\tWhen invoked, ttrts will set up a full game and output a single file representing the current gamestate into a local directory called ttrts_{GAME_NAME}.\n" - "\n" - "\tThis file can be read in and interpretted by human, robot, or cat.\n" - "\tttrts will wait for orders files to be placed in it's current working directory.\n" - "\n" - "\tFile name formats:\n" - "\t gamestate Turn_{TURN_NUMBER}.txt\n" - "\t orders Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" - "\n" - "\tOnce orders have been set for each player taking part, ttrts consumes order files,\n" - "\tcalculates new game state and outputs a new file.\n" - "\n" - "\tThis process repeats until a winner is chosen!\n" - "\n" - "OPTIONS\n" - "\tMAPFILE:\n" - "\t\tFile to read in the initial game state from\n" - "\n" - "GAMESTATE FILE FORMAT\n" - "\tName\n" - "\t\tTurn_{TURN_NUMBER}.txt\n" - "\tContents\n" - "\t\t===== {GAME_NAME} =====\n" - "\t\tSIZE:[{X},{Y}]\n" - "\t\tTURN:{TURN_NUMBER}\n" - "\t\t~~~~\n" - "\t\tUNIT:{UNIT_ID} tm:{TEAM_NUMBER} vs:{VISUAL} dr:{DIRECTION(NESW)} ps:[{X},{Y}]\n" - "\t\t...\n" - "\n" - "ORDER FILE FORMAT\n" - "\tName\n" - "\t\tTurn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" - "\tContents\n" - "\t\tORDER:{ORDER_CHAR} id:{UNIT_ID}\n" - "\t\t...\n" - "\n" - "ORDERS\n" - "\tF - Move unit forward one space\n" - "\tL/R - Rotate unit left or right\n" - "\tA - Attack space in front of unit\n"; +"NAME\n" +"\tttrts - Tiny Terminal RTS\n" +"\n" +"SYNOPSYS\n" +"\tttrts [OPTIONS...] MAPFILE\n" +"\n" +"DESCRIPTION\n" +"\tttrts is a tiny terminal based RTS where that uses text\n" +"\tfiles as order lists to control it's units.\n" +"\n" +"\tThis means that any user, program or cat that can read\n" +"\tand write to text files can play the game.\n" +"\n" +"USAGE\n" +"\tWhen invoked, ttrts will set up a full game and output a\n" +"\tsingle file representing the current gamestate into a \n" +"\tlocal directory called ttrts_{GAME_NAME}.\n" +"\n" +"\tThis file can be read in and interpretted by human, robot\n" +"\tor cat. ttrts will wait for orders files to be placed in \n" +"\tit's current working directory.\n" +"\n" +"\tOnce orders have been set for each player taking part \n" +"\tttrts will calculate the new game state and output a new \n" +"\tgamestate file for the next turn.\n" +"\n" +"\tThis process repeats until a winner is chosen!\n" +"\n" +"OPTIONS\n" +"\tMAPFILE:\n" +"\t\tFile to read in the initial game state from\n" +"\n" +"GAMESTATE FILE FORMAT\n" +"\tName\n" +"\t Turn_{TURN_NUMBER}.txt\n" +"\tContents\n" +"\t ===== {GAME_NAME} =====\n" +"\t SIZE:[{X},{Y}]\n" +"\t TURN:{TURN_NUMBER}\n" +"\t ~~~~\n" +"\t UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]\n" +"\t ...\n" +"\n" +"ORDER FILE FORMAT\n" +"\tName\n" +"\t Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" +"\tContents\n" +"\t ORDER:{ORDER_CHAR} id:{UNIT_ID}\n" +"\t ...\n" +"\n" +"ORDERS\n" +"\tF - Move unit forward one space\n" +"\tL/R - Rotate unit left or right\n" +"\tA - Attack space in front of unit\n"; // time for waiting between file stats static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100); @@ -191,4 +193,4 @@ int main(int argc, char* argv[]) std::cout<<"TTRTS: Game over! Winner:"<<(int)winningTeam< Date: Tue, 16 Dec 2014 22:35:56 +0000 Subject: [PATCH 064/190] Update with more pre-generated levels Also clean up test code to not output unless erroring --- source/game/unit.cpp | 16 +++++++++++-- source/game/unit.h | 3 +++ source/gen/gen.cpp | 54 +++++++++++++++++++++++++++++++++----------- source/test/test.cpp | 5 +--- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/source/game/unit.cpp b/source/game/unit.cpp index 4bdaaa8..24d02af 100644 --- a/source/game/unit.cpp +++ b/source/game/unit.cpp @@ -4,11 +4,16 @@ #include // for std::map namespace -{ +{ // Helper function for generating unique unit ids during static init - unit_id_t get_unique_unit_id() + unit_id_t get_unique_unit_id(unit_id_t* set = nullptr) { static unit_id_t p = 0; + + // If we have a set value, then set our int + if( set ) + p = *set; + return p++; } @@ -30,6 +35,13 @@ namespace } } +// force a reset of the unit ID value +void forceResetUnitId() +{ + unit_id_t i = 0; + get_unique_unit_id(&i); +} + // Get a unit from a visual CUnit CUnit::GetUnitFromVis( unitVis_c vis ) { diff --git a/source/game/unit.h b/source/game/unit.h index 90003b5..84a22f4 100644 --- a/source/game/unit.h +++ b/source/game/unit.h @@ -9,6 +9,9 @@ #define UNIT_FORMATTER "UNIT:%u tm:%u vs:%c dr:%c ps:[%u,%u]" +// force a reset of the unit ID value +void forceResetUnitId(); + // Base unit type class CUnit { diff --git a/source/gen/gen.cpp b/source/gen/gen.cpp index d598d89..6dd2d38 100644 --- a/source/gen/gen.cpp +++ b/source/gen/gen.cpp @@ -11,33 +11,61 @@ void AddUnitToGame( Team team, char vis, uvector2 vec, CTTRTSGame& game ) game.AddUnit(std::move(unit)); } -void OutputGame( CTTRTSGame& game ) +void OutputGame( CTTRTSGame&& game ) { std::ofstream output; output.open (game.GetName() + ".txt"); output << game.GetStateAsString(); output.close(); + + forceResetUnitId(); } int main() { - // Basic 5v5 game + // Tiny 2v2 Game { - CTTRTSGame game(21, 11); - game.SetName("Basic_5_v_5"); + CTTRTSGame game(7, 5); + game.SetName("Tiny2v2"); AddUnitToGame( Team::Blue, '>', uvector2(1, 1), game); AddUnitToGame( Team::Blue, '>', uvector2(1, 3), game); - AddUnitToGame( Team::Blue, '>', uvector2(1, 5), game); - AddUnitToGame( Team::Blue, '>', uvector2(1, 7), game); - AddUnitToGame( Team::Blue, '>', uvector2(1, 9), game); - AddUnitToGame( Team::Red, '<', uvector2(19, 1), game); - AddUnitToGame( Team::Red, '<', uvector2(19, 3), game); - AddUnitToGame( Team::Red, '<', uvector2(19, 5), game); - AddUnitToGame( Team::Red, '<', uvector2(19, 7), game); - AddUnitToGame( Team::Red, '<', uvector2(19, 9), game); + AddUnitToGame( Team::Red, '<', uvector2(5, 1), game); + AddUnitToGame( Team::Red, '<', uvector2(5, 3), game); - OutputGame(game); + OutputGame(std::move(game)); + } + + // Basic 5v5 game + { + CTTRTSGame game(21, 11); + game.SetName("Basic5v5"); + + for ( ucoord_t y : { 1,3,5,7,9 } ) + AddUnitToGame( Team::Blue, '>', uvector2(1, y), game); + + for ( ucoord_t y : { 1,3,5,7,9 } ) + AddUnitToGame( Team::Red, '<', uvector2(19, y), game); + + OutputGame(std::move(game)); + } + + // Chess 10v10 game + { + CTTRTSGame game(8, 8); + game.SetName("Chess"); + + for ( ucoord_t y : { 0,1,2,3,4,5,6,7 } ) { + AddUnitToGame(Team::Blue, '>', uvector2(0, y), game); + AddUnitToGame(Team::Blue, '>', uvector2(1, y), game); + } + + for ( ucoord_t y : { 0,1,2,3,4,5,6,7 } ) { + AddUnitToGame(Team::Red, '<', uvector2(6, y), game); + AddUnitToGame(Team::Red, '<', uvector2(7, y), game); + } + + OutputGame(std::move(game)); } } \ No newline at end of file diff --git a/source/test/test.cpp b/source/test/test.cpp index fdabe5b..0231552 100644 --- a/source/test/test.cpp +++ b/source/test/test.cpp @@ -193,16 +193,13 @@ const char* tests() // Main program entry point int main() { - std::cout<<"Running tests"< Date: Tue, 16 Dec 2014 22:35:58 +0000 Subject: [PATCH 065/190] Make the build folder if needed --- bootstrap.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap.sh b/bootstrap.sh index 8780bc3..9b9a839 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,6 +1,7 @@ #! /bin/bash echo "TTRTS: Running cmake" +test ! -e build && mkdir build cd build/ cmake ../source if [[ $? != 0 ]]; then From e8610c81317976a03b1aeb2b5f44cf70df14c266 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Wed, 17 Dec 2014 07:51:49 +0000 Subject: [PATCH 066/190] Remove player source directory in favor of new higher level players directory with a simple (and not-fully-functioning) perl AI --- players/simplePlayer.pl | 65 ++++++++++++++++++++++++++++++++++++ source/player/CMakeLists.txt | 0 2 files changed, 65 insertions(+) create mode 100755 players/simplePlayer.pl delete mode 100644 source/player/CMakeLists.txt diff --git a/players/simplePlayer.pl b/players/simplePlayer.pl new file mode 100755 index 0000000..b05583d --- /dev/null +++ b/players/simplePlayer.pl @@ -0,0 +1,65 @@ +#! /usr/bin/perl +use strict; +use warnings; + +our $usage_text=< $orderFile"); + + $turn++; + +} \ No newline at end of file diff --git a/source/player/CMakeLists.txt b/source/player/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 From 489ca882185480862db1606b644d587a59db6162 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Wed, 17 Dec 2014 08:36:49 +0000 Subject: [PATCH 067/190] Some addded functionality to simplePlayer --- players/simplePlayer.pl | 76 +++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/players/simplePlayer.pl b/players/simplePlayer.pl index b05583d..fb42a89 100755 --- a/players/simplePlayer.pl +++ b/players/simplePlayer.pl @@ -14,6 +14,16 @@ DESCRIPTION TEXT +# Exit with usage if not given a number +scalar(@ARGV) or printf $usage_text and exit 1; + +# Grab the team +our $team = $ARGV[0]; +our $turn = 0; + +# If team is non-numeric +($team =~ m/\D+/) and printf $usage_text and exit 1; + # Wait for a file to exist sub WaitForFile { @@ -25,19 +35,59 @@ sub WaitForFile } } -# Exit with usage if not given a number -scalar(@ARGV) or printf $usage_text and exit 1; +# Get the units from a turn file +sub GetUnitsForTurn +{ + my $turnFile = $_[0]; -# Grab the team -our $team = $ARGV[0]; + # Open the turn file + open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; + + # Pull in the header information + my $headerLine = <$TURNHANDLE>; + chomp $headerLine; + my $sizeLine = <$TURNHANDLE>; + chomp $sizeLine; + my $turnLine = <$TURNHANDLE>; + chomp $turnLine; + ( <$TURNHANDLE> =~ m/~~~~/ ) or die "Gamestate file did not match expected format"; -# If team is non-numeric -($team =~ m/\D+/) and printf $usage_text and exit 1; + my @units; + while( my $unitLine = <$TURNHANDLE> ) + { + chomp $unitLine; + push(@units,$unitLine); + } + return @units; +} + +# Output the commands file +sub OutputCommandsFile +{ + my $commands = $_[0]; + + # Get output file + our $orderFile = "Turn_TURN_Team_TEAM.txt"; + $orderFile =~ s/TURN/$turn/; + $orderFile =~ s/TEAM/$team/; + + system ("echo $commands > $orderFile"); +} + +# Get commands for a turn +sub GetCommandsForTurn +{ + my @units = @_; + + # perform AI here + + return ""; +} + +# Show launch params printf("Launching with team %i\n",$team); -our $turn = 0; - # Stay looping the AI while ( 1 ) { @@ -49,16 +99,12 @@ while ( 1 ) WaitForFile $turnFile; # Read in the game state from turnFile + my @units = GetUnitsForTurn($turnFile); # Generate some commands - my $commands = ""; + my $commands = GetCommandsForTurn @units; - # Get output file - our $orderFile = "Turn_TURN_Team_TEAM.txt"; - $orderFile =~ s/TURN/$turn/; - $orderFile =~ s/TEAM/$team/; - - system ("echo $commands > $orderFile"); + OutputCommandsFile $commands; $turn++; From 4b37246e53a6674200e82fe42d1b392ab4d3cac9 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Wed, 17 Dec 2014 08:49:27 +0000 Subject: [PATCH 068/190] Some more code for parsing units --- players/simplePlayer.pl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/players/simplePlayer.pl b/players/simplePlayer.pl index fb42a89..093fff9 100755 --- a/players/simplePlayer.pl +++ b/players/simplePlayer.pl @@ -75,10 +75,30 @@ sub OutputCommandsFile system ("echo $commands > $orderFile"); } +# Sort units into teams +sub getUnitsOnTeam +{ + my $theTeam = shift; + my @allUnits = @_;; + my @myUnits; + + for my $unit (@allUnits) + { + my ($unitTeam) = $unit =~ /tm:(\d+)/; + if ( $unitTeam == $theTeam ) + { + push(@myUnits,$unit); + } + } + + return @myUnits; +} + # Get commands for a turn sub GetCommandsForTurn { my @units = @_; + my @myUnits = getUnitsOnTeam($team,@units); # perform AI here From 3fc1f5ee5d23df6b27a40b62c123282a1ee75f23 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Wed, 17 Dec 2014 13:38:06 +0000 Subject: [PATCH 069/190] Change unit Attack behaviour to a charge All charges on a turn are evaluated at the same time, step by step --- source/game/game.cpp | 197 +++++++++++++++++++++++++++++++------------ source/game/game.h | 9 +- source/game/order.h | 8 +- source/game/unit.h | 1 + 4 files changed, 156 insertions(+), 59 deletions(-) diff --git a/source/game/game.cpp b/source/game/game.cpp index 8e01b2e..0608415 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -108,14 +108,12 @@ int CTTRTSGame::VerifyPos(uvector2 vec) const // Get a units new position uvector2 CTTRTSGame::GetNewPosition( const OrderUnitPair& pair ) const { - // Grab the order switch ( pair.order.command) { // For forward orders, grab in front case command_c::F: return pair.unit.getInFront(); - break; // For all other orders, just grab the old position default: return pair.unit.getPos(); @@ -158,7 +156,6 @@ int CTTRTSGame::SimulateToNextTurn() if ( possible ) { pair.unit.setPos(newpos); - pair.order = COrder(); } } break; @@ -167,60 +164,115 @@ int CTTRTSGame::SimulateToNextTurn() } } - // Vector of units to kill - std::vector< unit_id_t > toKill; - - // Attempt all actions + // Turn all units that need turning for ( OrderUnitPair& pair : m_OrderUnitPairs ) { switch ( pair.order.command) { - case command_c::A: - { - // Verify that there's a unit in front to attack - uvector2 infront = pair.unit.getInFront(); - - // Check if there's any unit in front - // FRIENDLY FIRE IS ENABLED - for ( const OrderUnitPair& pair2 : m_OrderUnitPairs ) - { - // if the unit is infront of our unit, then add it to the kill list - if( pair2.unit.getPos() == infront ) - { - toKill.push_back(pair2.unit.getID()); - pair.order = COrder(); - break; - } - } - } - break; - case command_c::L: + case command_c::L: { // Simply turn left pair.unit.turnLeft(); - pair.order = COrder(); } - break; - case command_c::R: + break; + case command_c::R: { // Simply turn right pair.unit.turnRight(); - pair.order = COrder(); } - break; - default: - break; + break; + default: + break; } } - // Sort and erase all duplicates - std::sort( toKill.begin(), toKill.end() ); - toKill.erase( std::unique( toKill.begin(), toKill.end() ), toKill.end() ); - - // Iterate through all kill orders - for ( auto id : toKill ) + // Iterate through all charge states + bool charging = true; + while(charging) + { + // Assume no more charging + charging = false; + // Initially move all units + for ( OrderUnitPair& pair : m_OrderUnitPairs ) + { + if ( pair.order.command == command_c::A ) + { + uvector2 newpos = pair.unit.getInFront(); + // If move would be within the arena + if ( ( newpos.x <= dimensions.x-1 ) && ( newpos.y <= dimensions.y-1 ) ) + { + pair.unit.setPos(newpos); + + // Unit moved, so more charging needs to be done + charging = true; + } + } + } + + std::vector< unit_id_t > toKill; // Vector to store which units to kill + + // Initially move all units to check for pass through + for ( OrderUnitPair& pair1 : m_OrderUnitPairs ) + if ( pair1.order.command == command_c::A ) + for ( OrderUnitPair& pair2 : m_OrderUnitPairs ) + if ( pair1.unit.getID() != pair2.unit.getID() // Don't check the same units + && pair2.order.command == command_c::A ) + { + if( CheckForPassThrough(pair1.unit,pair2.unit) ) + { + toKill.push_back(pair1.unit.getID()); + toKill.push_back(pair2.unit.getID()); + } + } + + // Kill all units to kill + KillAll(toKill); + toKill.clear(); + + // Check for all matching spots + for ( OrderUnitPair& pair1 : m_OrderUnitPairs ) + for ( OrderUnitPair& pair2 : m_OrderUnitPairs ) + { + if( pair1.unit.getID() == pair2.unit.getID() ) continue; // Don't check the same units + + if( pair1.unit.getPos() == pair2.unit.getPos() ) + { + if( pair1.order.command == command_c::A ) + { + toKill.push_back(pair2.unit.getID()); + } + + if( pair2.order.command == command_c::A ) + { + toKill.push_back(pair1.unit.getID()); + } + } + } + + // Kill all units to kill + KillAll(toKill); + toKill.clear(); + } + + // Clear all orders + for ( OrderUnitPair& pair : m_OrderUnitPairs ) + pair.order = COrder(); + + // Increment the current turn + turn++; + + return error; +} + + +// Kill all units in list +void CTTRTSGame::KillAll( std::vector< unit_id_t >& vec ) +{ + // Sort and erase all duplicates + std::sort( vec.begin(), vec.end() ); + vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() ); + for ( auto id : vec ) { - // Kill the units for ( OrderUnitPairVector::iterator it = m_OrderUnitPairs.begin(); it != m_OrderUnitPairs.end(); it++ ) @@ -230,21 +282,42 @@ int CTTRTSGame::SimulateToNextTurn() // Remove the unit from our alive unit pairs m_OrderUnitPairs.erase(it); break; + } } + } +} +// Check if two units passed through each other +bool CTTRTSGame::CheckForPassThrough( const CUnit& one, const CUnit& two ) +{ + uvector2 pos1 = one.getPos(); + uvector2 pos2 = two.getPos(); + dir_t dir1 = one.getDir(); + dir_t dir2 = two.getDir(); + + if( pos1.x == pos2.x ) { // Same col + if (pos1.y == (pos2.y + 1)) { + if (dir1 == dir_t::N && dir2 == dir_t::S) + return true; + } + else if (pos1.y == (pos2.y - 1)) { + if (dir1 == dir_t::S && dir2 == dir_t::N) + return true; + } + } + else if( pos1.y == pos2.y ) { // Same row + if( pos1.x == (pos2.x+1) ) { + if( dir1 == dir_t::E && dir2 == dir_t::W ) + return true; + } + else if( pos1.x == (pos2.x-1) ) { + if( dir1 == dir_t::E && dir2 == dir_t::W ) + return true; + } } - // Clear all orders - for ( OrderUnitPair& pair : m_OrderUnitPairs ) - { - pair.order = COrder(); - } - - // Increment the current turn - turn++; - - return error; + return false; } // Add a unit, nonzero return value indicates error @@ -301,7 +374,8 @@ int CTTRTSGame::VerifyOrder( Team team, const COrder& order ) const for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { // Accept if we have the unit - if ( pair.unit.getID() == unitID ) + if ( pair.unit.getID() == unitID + && pair.unit.getTeam() == team ) { ret = 0; break; @@ -318,7 +392,7 @@ const CUnit& CTTRTSGame::GetUnitByIDConst( unit_id_t id ) const for ( const OrderUnitPair& pair : m_OrderUnitPairs ) { // Attempt the unit add - if ( pair.unit.getID() ) + if ( pair.unit.getID() == id ) return pair.unit; } @@ -327,13 +401,28 @@ const CUnit& CTTRTSGame::GetUnitByIDConst( unit_id_t id ) const return invalid_unit; } +// Get an order by unit ID +const COrder& CTTRTSGame::GetOrderByIDConst( unit_id_t id ) const +{ + for ( const OrderUnitPair& pair : m_OrderUnitPairs ) + { + // Attempt the unit add + if ( pair.unit.getID() == id ) + return pair.order; + } + + // Return an invalid unit + static COrder invalid_order; + return invalid_order; +} + // Get unit by unit ID CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) { for ( OrderUnitPair& pair : m_OrderUnitPairs ) { // Attempt the unit add - if ( pair.unit.getID() ) + if ( pair.unit.getID() == id ) return pair.unit; } diff --git a/source/game/game.h b/source/game/game.h index b763071..5e68b42 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -85,6 +85,7 @@ public: // Get a unit by it's ID const CUnit& GetUnitByIDConst( unit_id_t id ) const; + const COrder& GetOrderByIDConst( unit_id_t id ) const; // Get dimensions inline const uvector2 &GetDimensions() const { return dimensions; } @@ -100,7 +101,7 @@ public: // Get a vector of the teams in the current game std::vector GetTeams() const; - + private: // Verify any order or position - non-zero is error @@ -110,6 +111,12 @@ private: // Get a units new position after an order uvector2 GetNewPosition( const OrderUnitPair& pair ) const; + // Check for a pass through + static bool CheckForPassThrough( const CUnit& one, const CUnit& two ); + + // Kill all units in list + void KillAll( std::vector< unit_id_t >& vec ); + // Get unit by unit ID CUnit& GetUnitByID( unit_id_t id ); diff --git a/source/game/order.h b/source/game/order.h index 77250fc..1072515 100644 --- a/source/game/order.h +++ b/source/game/order.h @@ -11,10 +11,10 @@ // Type for all orders ( as a char ) enum class command_c : char { - F = 'F', - L = 'L', - R = 'R', - A = 'A', + F = 'F', // Move forward one square + L = 'L', // Turn left + R = 'R', // Turn right + A = 'A', // Attack forwards until a unit or edge of the arena is hit NUM_INVALID }; diff --git a/source/game/unit.h b/source/game/unit.h index 84a22f4..b08a707 100644 --- a/source/game/unit.h +++ b/source/game/unit.h @@ -87,6 +87,7 @@ private: // Typedef for a vector of units typedef std::vector< CUnit > CUnitVector; +typedef std::vector< unit_id_t > CUnitIDVector; // Simple validation inline bool CUnit::valid() const From 67420151ae4726df246b0e3bbed6abbac0c79f9a Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 08:30:10 +0000 Subject: [PATCH 070/190] Create gh-pages branch via GitHub --- fonts/opensans-bold-webfont.eot | Bin 0 -> 33498 bytes fonts/opensans-bold-webfont.svg | 251 +++++++++++ fonts/opensans-bold-webfont.ttf | Bin 0 -> 33320 bytes fonts/opensans-bold-webfont.woff | Bin 0 -> 21112 bytes fonts/opensans-bolditalic-webfont.eot | Bin 0 -> 36806 bytes fonts/opensans-bolditalic-webfont.svg | 251 +++++++++++ fonts/opensans-bolditalic-webfont.ttf | Bin 0 -> 36600 bytes fonts/opensans-bolditalic-webfont.woff | Bin 0 -> 23264 bytes fonts/opensans-extrabold-webfont.eot | Bin 0 -> 33242 bytes fonts/opensans-extrabold-webfont.svg | 251 +++++++++++ fonts/opensans-extrabold-webfont.ttf | Bin 0 -> 33044 bytes fonts/opensans-extrabold-webfont.woff | Bin 0 -> 21296 bytes fonts/opensans-italic-webfont.eot | Bin 0 -> 37438 bytes fonts/opensans-italic-webfont.svg | 251 +++++++++++ fonts/opensans-italic-webfont.ttf | Bin 0 -> 37252 bytes fonts/opensans-italic-webfont.woff | Bin 0 -> 23740 bytes fonts/opensans-regular-webfont.eot | Bin 0 -> 32574 bytes fonts/opensans-regular-webfont.svg | 252 +++++++++++ fonts/opensans-regular-webfont.ttf | Bin 0 -> 32384 bytes fonts/opensans-regular-webfont.woff | Bin 0 -> 20948 bytes images/bg-ramp.jpg | Bin 0 -> 2161 bytes images/blockquote-gfx-2x.png | Bin 0 -> 366 bytes images/blockquote-gfx.png | Bin 0 -> 224 bytes images/chevron-2x.png | Bin 0 -> 138 bytes images/chevron.png | Bin 0 -> 128 bytes images/download-fallback-bg.png | Bin 0 -> 110 bytes images/download-sprite.png | Bin 0 -> 10019 bytes images/footer-ramp.jpg | Bin 0 -> 1285 bytes images/fork-sprite.png | Bin 0 -> 1940 bytes images/hr-2x.jpg | Bin 0 -> 1591 bytes images/hr.jpg | Bin 0 -> 1307 bytes images/octocat-2x.png | Bin 0 -> 1334 bytes images/octocat.png | Bin 0 -> 654 bytes images/ribbon-tail-sprite-2x.png | Bin 0 -> 312 bytes images/ribbon-tail-sprite.png | Bin 0 -> 1373 bytes images/shield-fallback.png | Bin 0 -> 340 bytes images/shield.png | Bin 0 -> 1125 bytes images/site-2.png | Bin 0 -> 654 bytes images/small-ribbon-tail-sprite-2x.png | Bin 0 -> 338 bytes images/small-ribbon-tail-sprite.png | Bin 0 -> 311 bytes index.html | 152 +++++++ javascripts/headsmart.min.js | 1 + javascripts/main.js | 1 + javascripts/modernizr.js | 4 + params.json | 1 + stylesheets/core.css | 3 + stylesheets/mobile.css | 510 ++++++++++++++++++++++ stylesheets/non-screen.css | 154 +++++++ stylesheets/print.css | 34 ++ stylesheets/pygment_trac.css | 69 +++ stylesheets/screen.css | 569 +++++++++++++++++++++++++ 51 files changed, 2754 insertions(+) create mode 100644 fonts/opensans-bold-webfont.eot create mode 100644 fonts/opensans-bold-webfont.svg create mode 100644 fonts/opensans-bold-webfont.ttf create mode 100644 fonts/opensans-bold-webfont.woff create mode 100644 fonts/opensans-bolditalic-webfont.eot create mode 100644 fonts/opensans-bolditalic-webfont.svg create mode 100644 fonts/opensans-bolditalic-webfont.ttf create mode 100644 fonts/opensans-bolditalic-webfont.woff create mode 100644 fonts/opensans-extrabold-webfont.eot create mode 100644 fonts/opensans-extrabold-webfont.svg create mode 100644 fonts/opensans-extrabold-webfont.ttf create mode 100644 fonts/opensans-extrabold-webfont.woff create mode 100644 fonts/opensans-italic-webfont.eot create mode 100644 fonts/opensans-italic-webfont.svg create mode 100644 fonts/opensans-italic-webfont.ttf create mode 100644 fonts/opensans-italic-webfont.woff create mode 100644 fonts/opensans-regular-webfont.eot create mode 100644 fonts/opensans-regular-webfont.svg create mode 100644 fonts/opensans-regular-webfont.ttf create mode 100644 fonts/opensans-regular-webfont.woff create mode 100644 images/bg-ramp.jpg create mode 100644 images/blockquote-gfx-2x.png create mode 100644 images/blockquote-gfx.png create mode 100644 images/chevron-2x.png create mode 100644 images/chevron.png create mode 100644 images/download-fallback-bg.png create mode 100644 images/download-sprite.png create mode 100644 images/footer-ramp.jpg create mode 100644 images/fork-sprite.png create mode 100644 images/hr-2x.jpg create mode 100644 images/hr.jpg create mode 100644 images/octocat-2x.png create mode 100644 images/octocat.png create mode 100644 images/ribbon-tail-sprite-2x.png create mode 100644 images/ribbon-tail-sprite.png create mode 100644 images/shield-fallback.png create mode 100644 images/shield.png create mode 100644 images/site-2.png create mode 100644 images/small-ribbon-tail-sprite-2x.png create mode 100644 images/small-ribbon-tail-sprite.png create mode 100644 index.html create mode 100644 javascripts/headsmart.min.js create mode 100644 javascripts/main.js create mode 100644 javascripts/modernizr.js create mode 100644 params.json create mode 100644 stylesheets/core.css create mode 100644 stylesheets/mobile.css create mode 100644 stylesheets/non-screen.css create mode 100644 stylesheets/print.css create mode 100644 stylesheets/pygment_trac.css create mode 100644 stylesheets/screen.css diff --git a/fonts/opensans-bold-webfont.eot b/fonts/opensans-bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..b5bad08afe4fe15229159ccf6ecb7c39ddb2b797 GIT binary patch literal 33498 zcmc$H33yaR)^^psx3?^v?oK+LkU+ZASqNF0q)B2}!y;gS2mvubM3#W;J0f62WD{Hv z6~wq9n}`f^yJ-;_R0P?^QO0p3qln_DC>q6aToB03|DL+tAv*Yd-@iQ1PpH1P>n>Gw z>eQ)I?>VRXgL@cjkHne8=${mAHp&J?k`&Gkh^&EwFG%d{C^su_FYaLM8P42v8gQ0n zgtJ;59PCCmi%noNaMMsW7RR}`YG%FIOg4?x;yj5B$MreLpNu@ZuaFfWb;PyQYdr04 z^(=1TNDh1`%-y%|ut8(GE&mLedGv6YJoEkd+vmBXR>Z7iEVdEX zvnI~EZF*eD_Z(x^1kh^2ZDZ%oLcR_E@5Ir3+q8uf?Q)zQ-1DF37BWAB(H zIrtZh-T5Kzch49*eL_ZIc~^X=>j!4doI5Yy?KsESUHedviHR(6~n=MVFZ z_`4+4NV}0flfII6%WkPgei-R5$~i-lAxYY8w3^KPVN-Y0VAEYl58-Sx5`7dF#WpXS}HZ}SS)52-&=Me|`c7-l=?&x^!TUMOXZwgdn(J8>QU|0Wq)tessKa&54Xh0J zc1G%ov>N%3BCSW-fbjr0ueKaBh%I8q4>%z!sWA!Xsb15!SZ zLTL>stwEGlhE{b((o1SUNew8e0VOq{qz07KfRZ9_ZV+$&0Pj14gt3Y%ad_H}l!5b7 zw6mQ{ziXQZxZ-+xt` zCvfkRNKYX>jr0uery3r?@dLd73=(7mdMHl&iP@1da88ucsZW@@`G#GChf>ed`p*Rl1@g^LH<9*dQ zjzBF&B8@_-LAnL~@q6RszoNlrymt%A--@&iX*<$$NIQ_8$F-eEFCgtg`U6r3=|!Zy zNG~DnL)wq@C)DWx(kn=>BE5z>5iQ@qwFcDfA4rj2i~(gDL74{heFJDp9M=e1HiDLo zpk*Uy*$7$^r!}D8L2o><8&A~ZiTW_N>_-1LVw5yugfwD=G=h!|pko8**Z?{aevj)(t0W@m>%^EaevqsRY5j1NA%^E?kM$oGPt*%F_>oK!1^!BegXvKeUeIMM}7pWgof20b$;i^Qf z4@=bguteQ0BghDSeyj>*R9%yQTfhDP1=57GxpzBHN4kdp~=P z9b~Vw!*gx3&Xys}4A^Qj|V5t8<)TFh3mm&1K0-@hsCp0qB=}6ngzu!Hhi?Xs< zIslq5L%qQdr7lfB%I~m={EAt+y0SRSqzmQDJqpi2^JT2C=g{8F&D@t2<3w#DW2U)M zf}8PY=$bOxuonln&=aB8lWvc>km}A@BfimDDr)yv96RDZ1={#steF?`q5KZ%sPuvK zp*zZ*;BMn?=T3KLxqa@=?mq5Y-SgbLJn3iIWks~f%{JkQs+K31+vZMkr-q*>`_&V? zxw-k%=0nZvnrAl;(pb|sO^s(yoPFc$>t_$1edX-_v%#~^pWSqJ<=HuByL^1qeco_B zTwAK~|M(v_>MAllz)44@)cPOVEh`42$!v*=j)}F#+3b$^1ZScvDY;EbYTI^cZcn;5 zBQq*>&iNJ>DsM(kMf?qdSkWJum24d0|pMNym9c5s-eSf z8h&&2h>@deZW(>+n6Yf-s@3;BvhnFHo40P?_T2L??A-N-KkilodtMCf-S^UgKfUrQ zn^QYs^2c{PJ8SBfw@+dB*3&}owgsY+)9z$D_AebjgT73!J3H>46^|W$qv0PPo_YU+ zm)Yw_KKtUIpMLctQ z)`%3`SKXBI&4?7QM>XVDt9@@DAufygCyYH>3Nl_VIW6BF; zh^+FKEOVa2?k@A?tCl=>9ToBh-tSiB%p1IJRmrMQ+2HCm6V{A%)5&!y9?yuBHR4;9 z{*4NZ(raf+v3XEPbe{WfqB${nZl7w(t*Lgq`+570o#L)`*N)R)LHET{$tc>r#@%mC z-?82`?loRfoL6O4)oMBJMEj^%wR{47!82A-Qnyn{9#4w<)EbZh58Z&e-4w1H>WACn z^1SX-;ljP{>H$MjJSrbiy#@`s!Mn!mUUS15?^vn{HHSW_J~rwG2fD#d4WJXpZyJD# zdi#3EP8rj>CG==~9vZP`6_Kr?*1N`}x(8QxO*x1w33+>1IWOO- z&E0FNs=eb-dvC849Jsd^C|_Px9fZv2J-%0vyEzW3d%T)FAyw2AZ*=CV$OT>Skxv}S z8Ad-Oz66QNlqkq1nVBHngSe6#QVc9c$qjND=O%H^RkKfJr$UOEMJu_gVhb2}Zb%m2 z0v4XjIu`ov9($(8?s;9lp(#yT*tE*9_fo|q`6M z)mqlEu(VT&zbMg}VDzSE#d~D${E@?lj;g5|R-^Ed{vXy38(v*?+GIu=sl z9BBo2pwDpD2S9@*9|jj|2MHd>-$ksUNBQMr4{4JJ!&&>S68R8+)OaB!Pbp=7<5 z$+kcuYUvc;g3d&`+Zk`k-D7qpg0Lq2E|V{0*6%Qx=?;@($yJ@UKr|kW5f=g;o~xF$ z+24KtzXvd9Zq)wnr~mx}ov3YWd!#m|_}o3R_%za|nrz!+NjBpo(Y7ZlF&ZaM+n!iw z3~sZDPrLX`pijETCGNpfN#ZHIGbQq#)X00<(mi|HMea&c-)2!4}8?v)Rn&0|j)r&Gw!NK%5n^(Bi|L%Z?AZl8Ga=DcY~&@yowBm4KYerc=P!?ba_ix3 zgU+2?{lmVSe;m{M=r*3;t(b#I?OO7gltB|N@9s_Lt9tM>kY8Oy7mc|HEE6NVw& zoZAa8X;b;qs=TsxU&miL<+`;)XDG-}rC5*VZ;d}0-h`%>0Hnl=F`%-blQ`dlk3hEJ zd@vE?FDT(7;4qvInXMivHaB3#X-cj*O);G3YJreL(Yi>8wgqfpQiJ}@U<)MUTZaBE zLr^NzP8S(IB388B5j0EAva&MOUZ3|t=FB&tZD1We=``wlI z_#3~;RwRDnn-9-@@y$m|>aJW`e0eE}O}?KpKmX#JGa{3;#j55;BLn@~g2xJ39SbHA z?NW$#9_4({LS*w1fr^Mgfp$^n)pj;D+kMI&FoLIyzCbn(?QDTOP$CBGJzx)*mVr}+%BoIfdKdAcsiTjaLlQ(NRVOrLOdS} zsT`C+O|Du6t3gO+D!G9;{Xv_f)NjN9a=5%%Qq>a=UwYu7NAA7&NWE0ZE&T1>uWLm= z{HT?_yptcrJG>}P7EvC^{@AM}+>^8~dEpZfglDb`br2dJAFRps@A;aE3 zYK7V_`0Ko5A3yQa5BzPsvxhWXX=_X%R4cyJAbaFYmm%I1&9mcsWb^4c+?d1Xd$e62 zfBNL=#xsiJ(j-1hduY;?B&}f~FVjve;5mAGtJ$YYnQ{bk@=X|t(7D8fqZ%;jags|c z3X=+FbjoECQ87L>>Xa%Kg)I84p%euzQFPft(@&IzPNSG5HxR2+s@P+PO5pU^z4mIp z>@;7d)t#1VPxF=9qSM+EzLeUcUF2`FZ@JuCDRUItneu&atmi++NZL-o;>lkg$j$kqx+i?R{XGjuwmpX5G* zFPWcvxK;9SXVb?t$mzO72$#~|@D|E;VO<@x(14CJo);__a8d)3#DK<-Rby-c2MA(} zI!}Tw;D~8aft;ib8CjW-QVvfM14DVz8Mk35me_4H1S>9arHRXzwaZOfGxy&zW7?Rp zQ>TxX&@+6dcDMGRwp4qF-^u4{|32Tyqj>Dsg02fu|4P(93VjrWF-=zsIZb8Csep{C z#Gop&f<6KTAsaX;f<3q~jxXd5n)aq_7}Rcu(t|I!R3TU1w{cJs);XYgC3K*{LY@s} zK|8hD3NfaT7;B@T)$M$$>r}vOJ|8r@h_%c%aG6W!CoVgc7qFxBkEZsqTuzXKty@8$0Bt zO*bo-Y6j-ihUQHO?B5lzYF8Tg+~=0g=u$nrvcFzu^i?m^Ig8C>!Az>Nf}TpEIy;Q# zLov~rNtm%>jOX__Gn33<cLanhyqY3S)2}&lKz-ZbI@N_gF z#t~?jhNIK2+R9XiLrp1DlkAwJpx$D-Mt7naxx84Mpj%DZC7vQh%;?l!#gkrn(_d;I ze|X2fnX`93_1FIEN3A{1ou6{k_6>o(+S}S^-*(}q+>%LiZyWy2h-uq9c6slieOp%_ z%rGXtuAfZ2lCSF+i~yb{d>zo-(m65UnIAuXIX9^Ik%%QMcHUebB( zlJ<$o`t8XdwTn89`=ic-4acAhWx#kWG@(FhWZavyZ?8MTHi;A*B20m&dSGL0pkb>Mc<|%SHAr-9L z}w2NPD{?{_@uNyOd%J|yRE2QZ> z$alp3oG|9VA9nuq{#V+C4cuL`VC#y-Qx^mG;J^)OjE5lK-7IK8zX-uhqub;o(ar*# z_&}yAVXA<5ELD=58Zc{SDk@hFUA8JJ={`iO3d0FV$aF~P+r!j?I;g8Xd8GFE1c{PB zA|nkoFHVwdfp$8rREJHCq3j%F=lF#F(-D`F!un5+7kZvE#bY7$r=4Am14HCN_j{4W zIAl+juKj^6N$JV(>%&cqwurhTG-qQx)aZ!D(|^Sybu+l~&fhq{bIsPZ+PQPzY2Wg8 zcTAc)XZDi!iUv&>S6N$m<5)ho?oh>zp{Y+DdTsNpKVDz`+MLkozq~bk%=jU_=l2>X zm3Fjw{^aoJ1I;TTJXES}DSQN-yjbA=Z?<7yj4JmVss$r1!9(? z2|SaHV;ka`s2Fg4nd-KK`l`th1n4FPrVA5^8e68ewX5-Es@D-P>61z)hLyF$3C2m! zF2CLT&XfO7;oH0|Ma|6xtK(ZUdkJC<63d z)ol|*GK3j1CKjs>$?j_FrD5l`2P}|O0Yd^f(2N0?+!nMnB*cK00m&t#Gf1jSE2GKE z+j0+072yGA!Wy>){4suf>Fs~l-ap2#{aiJDa_=$2Hym6V!Ga8xbw{_&*i_rEqHEWR zF%J$NFuHTkKI5*WhS4Ev=fviT#$%8-omdZ8D1=fH$~3S9G@-~B%G1vrJ~hT4D%HQV zD^k1pLP_EaSIcSWg>rN~IY+GKL&f^HVjE#8VZ0^K6D;E?wmZr#F^VH8GtXDl1*CxN zDME8QV*0nMzB0Ac0fD4;vxm&g>7jM0nnY>=h|$TBfyv)NY7Uq?+iS#Uu^6f@IYDR$ z1-PI{G;WJZN}WcJn{m3(gOiA#_ʶIlE62WocAteBH*oAzAbFFfWS!%y~h4J;qH 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 + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/opensans-bold-webfont.ttf b/fonts/opensans-bold-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..338220f25b96ffbd754ccb103150f97b8e12497b GIT binary patch literal 33320 zcmc$H349bq_J37(&y`~`Gs$EU639#@2O-Ch3`q=UI0Ot3As_~f$PtkHMg)wAT!IH8 zf*3F45)s*+VMJt65#(A|S=W`UB8sb`XcX7=Kp>s}_f^k?=(4}x?|*zge?m=n*K~JP zy?XWP)%U$u!#HEife(eb`}Q3+Xmqz_pEJhukek}C&-HzI3X^f&AJ^`FgKr!<*Z1`W zTu)@oa<<>ln|iJ5dhYnnP;vHlRQH{yEqxM^c%zFadE=g}AO%n#%4nCp&O z9NkdKqIAYA22# zQ+s9Ff>Vs$@fE%=nTU+o%Z7h3Hs6iwjEU3c-kJLFp!T@#!Wpz?;?DevXBS>U z`5#XkbLULS!M|keu8(lPd-|AZ<1-4&yW*O@KQME~oVodK$2rFC-iP{}n>l;@%&$8- zZewi8Cft7?5>bntST&=Nj&F^OLS4l1Bk_$D7fF=EBys&MJ{57LKN-b_v9V0)(|6!7 z)@Iy-*;858?X$;EVMSBN%$?47-yT2 z=z6f}d(++K>E`LC&F1NN+oh|2c-w9y{jUaZZ~0Ts>3>G6OmDsVNAGL(XMWT)7;i?B zJ~vv;+s)fWoj0Q18(9%(FpFg%m9iXGhSV9UD@$N~ns>9l&C6Lor2a@1&4<}wq#;OE zxE_k*FdT2faX3;n(!F@kQlw=_%aK+ftweeT@7s)fx8R*yk+vaiM|uuv2h#KSZYRLat-&a&o4ys?pW!F5+0yCL0+^QB13kd`B@Kw63P48A*pbQ;fpj1(*07^!Qd zo~v-@f2!AEd`A>r6n-`iEw>}3gFC23|Ec8DxaVV})G(E5K&6@%Dn&{z#T%|e{mO9c zjMNqF`>$&A1nzwj=_#bAk)FZ*RKp`Ueu(#OrM?b{hA6jMNuT&H}w#xA1Duw$vFd?}Kvs;@*Bp{gEn=2IEabkg9M#6vtsW z-h|_DyssL^5vavTq+5_`kZwhP{HJm9U(sMQ-n#|mZ$;XMv>oX=q#a1l<8&A~ZiTW_N>_-1LVw5yugfwD=G=h!|pko8* z*Z?{aevj)(t0W@m>%^EaevqsRY5j1NA%^E?kM$oGPt*%F_>oK!1^!9H#X!*Z!eIMM}7pWgo zf20b$;i^Qf4@=bguteQ0B+Yc3ghDSeyj>*R9%yQTfhDP1!4WKHXLpp;5Z3X$INSxhy#SSHJ2*{nU@QowxB0{pBKG=S@% z0d!_vF#mT0PxoQhLpJtjH?RS$k_~1U?bTrtcHzZ_p+sI83RwUmC)Z- zvDNH;_5gd3J;EMk>)8hO7~9A&pRy;}Q|xKz0h`!nwuNnF+t_yY9NWoWV7u6hEXYFa zCAJsy_kQ*|JILN(hcTx&u)nhp(8|;7Bla;`z)=5zs7Y)6E<@;d1wy~OPH1GL(~-7~ zfB*4}zLb?k(gDzf8R`vwD0gZ4QGSG4M@n!r{tSJmj56%S!7cPe==G%AV=kn+GuDVJI!i_E{(@si+^0Yr zzneAlLOzt=DIJwQls|ud&&+<0pM`=d&}Xe09fUc3(Z4!`SWfMJ1=+#dhppGHyCuOshLP z_TJ@>9e%Un?;oA{;KNtg8%I9>@*kgl{S8}i`eU~2fu#?wUi0wUht_Xkk3RX-6Nio- zpT*d*h2k|DG&kY^P~aczXQrW#3V8|p8~c%6;dwlZ$FdU`Cro8|DvKYWb{Jf(R@^Z{ zW!@f1sxh~^+X#_YH^TilmB$w(<*Pi;{h=C@o3BcF1FEX8_m1%7t8(6?B)3{VxZ0zZ zkH}Y*JbKjQ^(?GDlk)b66x>(cl=AI}6t71$9xGvvkAjLcV!d4Y6( zA4+%MS5uRsGQ7r=7swD<ryaU>tVyR>l?OyHfx4Q2b?`rpIuPDx|vZ`ve9CxC9RIFM)o-Xi=Rg~22RFcP&;y$$+ zWWYl=pl&yX>xTN_wzxd6`&77aue*A{&=il#M^vvygKqGy_PST!u-ZF@YC_GSPpXfN zy1{{Nuu}u*#PPcZprYQs-Z7I$w{8hN8lQ(otX@fEtEly^HmUBx)m>8#;+urLJ*=FU z_v*z5?6a|POnlPKH=*Y4)m7Erv8cVbR|*c?+Y6L0uc{70X7nD{E6Cj(2h}}JO&*^r zYKk{H^Hk)5F8IhNj^qrZ9}-`JL}f}8WRuKHknTZzlN(YDEJn!YuHCM{@MY1n(IVxn>mWz1{-g1?6{ zXpXss1!M4D)#3}u3X4~AxoV?Vt1(3(V-#zLx5q}#_E?R~oEwTWvm|84`9fB47O?Z& zfHltU2w3E@G8TxD?GDvi*0Hd(Q;EMQ(V1ZMrf0=_WbeF@!-w8dQ#Gtc;UoP&t{FDG zy6VPTMi}nA;v*VJY`MH$?iFtPK(w9erTD2{W|U_v5;@^ZK>_(xNHQ}!zLabM6DVL5 zS44q;NkWCVf(mskq{2DUa_-7G!t3bMu(#=Z$=38em5VM=G((7`uryxFf{CDc(3#xE z@As?BwHm5?#dM%S}fkf2ODXxOfM7rAqk0K+t~I3w#Sle z#z~@WPgG(wPMo$qvCbIWW)q)w@tHuMbdO8igQt?jQ+Q`exvYNAh#_Xko9=hf_@U}7o}HPH#8M*GxEf52!wugXP1V>IO(L2Rz(iy@Fy z6!4hOs~%gx$#a8pY6f~VlpqFK&=8%CD>cCu$N{t2%;y6Ibj+bqV5U)!nut-5RHjDT zUt$(pazbx zva3IPYtvF61 z!GP(@s-Jly{_PWnA>5qX3omI?`SQxVvUcCZUpeKvtwU!h$WWzNkLK@;KO5eHrj`Jt z#EUVYvY?YV--C}pw&8p*5#ui?;UnNMoDZ3;9w|0AV8&@mt~gCGoabtRkVDbBNQkxt zY+zD@er2!)l5v%xUu6hNh1%&N!$-u5wmX7m$yrubrW)*^RUji7G)pW~bL_zsj|=%M zV9vHX_9VB<$N~8Rjuc!NqHPQpprXwakuIePiT;WTq%IFJZUFNiJK?jFqZ zwUu*T|L2d|KX}h2Lr#w{G13;@bH~Mrhy6+qY6{ z9;cn2wfyWy!zXm(6?vnjy6VXtZ|z>(t(@jF$a4Oqkmc#RByW-1icf8k+ca&3<+G5@ zB$bIjIjhnRNDJ_METnQ!1~s{A6|4p!nW^Lk;`9e?j#9r71IXd>W=T~~JaXy5hu7VA zab3Ms$SwSx-EU|`KmMeZzOs`a#XGy%!ch&F^f<{S7KKTLGdkrmiKrN#8g)vQib58B)=-LqmMHq#Leo!_g-)ZGB{vYO zQ>xfwhf3h|*uC~@zVtL-s@0vAYESbO+QQS?V!njhqFv-~vG14>t4`D*Bq_{73W3R| zN~Zz}nA8j=RWPxtMlr2ANd6tJs`NsE4hmP7T?m6Pxx+{o4n zM~ku%(KB>9qo3qHf-jk$d$?8daA(seG|1_Di4ZQOzu|3^?ZUb`XrTceXFM-hFyN#H zB#8lyA*;sN0uB(w7j zFKd^Zv}W$Vb^6rNW2Q_SC81~d4DBB6A#I8FFu#k>(f)P5kw@{^Zv2c3R>OHr@BrB%;xh!vx`{EYy+3Mgnr_(gUiyu zWdW-l#X8E=Hg??AmgKtPP#F=SyW}rw$DIjQ9>>9ho?_lz(nXP}a_U1nCa(JH+M0jqYUfzNqv$@DJO!z=sibw*$HLY=eN zTo%lvIxFa@B&xH+cs>*potcCgE5>+!k25pL3?gM?T%8J}Ll)ae5T~FuwiIfOEgDT| zPfbuV(F8`*c7Ug&0Wpq1yEGh~cGXs1yi&B*1&;so7l$}aH~ zDPl&a_9~wA%A5XN`}iX}_RW~J^Qph|Uw_M*V!VqOlhXba|K=*R&E9mWWC zGr^}?NOz~6BBch)cP*0F!R#y9>|$PtW`bW-iCT&7D&~+DPOn%( zU|uQp8ckkl32&>a+F!Lzd~}p-&g{f}4YGP=()ZfC-1q&(?v3$PU?SqBTJ%dX>YD;9 zU$Bkn7t{G*EcJ^`IS&o0O)UB)fd*5mPc@zjxWKk;Q70GXKddBZ-dEb_{b05Sc{Hg4 zYJwvWgW87a?k?`+A!^5indDM^oxwcmwKx8C6i?Ak7vIx~^*l{1e`LTBK>&^dM z%Kdet$4wqrJ8HQ!jR*OTxL*=RA9!)+&mVlPUD&|gHS@PFUo>SAa1RdLkj8ij^4-mX z2K0*%%rv@9J`(LLz=;oJsuHFOh{sYTxv2rOW~QQY<$rfm*(@J&N)ELUnL3WN$=sz9tRZ>|0$?-zZbEbGK zr2e$Ct8rk6Jm`Keu^5N!$(RlhVUspGsEARf5 z^Sf4WU89{l_r3NVZ+GX!IkRUie!pnY__39>l{b#zbLtLN+!&hj)S=fm&-}yn)vwPE zo&NLN!$*%B(tBR7u~KQ*+lmTC_qcxW5E_@g1RajCWXR%aEEq+c-ZEEs!85M5QN%MY z;+ZT#kTz&=8(YAQ#+tx0**LZ#o{5S9*O#eoJE*Uk96^9?Vqm&3k*Kj{YFoP+U#5B; z0h2zdbYfUpJDgyg^z33xD&2Xh*UG^&ty!iQk3P5gU-xQ{YHRN8J*?)5FPrhr>;Q3pZp^aiLR_2KL zu#5%cBw!Fu0Tf)FnZt8pE6BhRfedJ3#4OZ$Vzx*0RMfU*k|t!})*-`cB9pazKcNFd zMpP4>NiTU1bucmqFz;X#)xj19E8ST)}aJ&s0+iM7!+A1phe^Z+Ly%p#1=U3Gyp|_zN@-zf=GriBgVvH)gjqkZM`(?-1dM4k}6e!2?Eh?%8MTmDDggMD3i=Ji&Mj@}?8(0SkptN&ZD{H6JS0uZnGirG)X8Ku@rYr`YZ& zx5Oxpq|7{DQ5TQ`vZn~m?TG2$uKLQ>4>LD%M|KcfU097ryR` zkQVyj;S2Zm>%8RsSKi~2$ECUM|KnrN{&Mej?bzE|qxOL>|KKhBVP5y%;x%WYpCtO? zpJBP-05~=l79@c=s6~z^eDs(;=z(A-XmpY8zqG=l8jn#a?>}H_wl-QnP!jSz@^VQZXZ9!OL_953HLA3 zKDqeqpYBQ*a!OH|WqNdyvJFb{0^gkR$X2@Rz^HWg;d5 z4|$48?MAPg&EL*T-+jNKwrcy}KOE#U>Q9G0*51%|OCNmBZ+`jqE7_g3U%$~@gS0CH z_)>JWL^L+c1C7m~@^vg|rkYtI6pk~2tO5cKSU^aNO)Myglz@3rJ*bT>B*U_Xd1Sxa zZu5Be@wfN{{(v@KyKCZdeoz~|&9GNnp}nX*rriU0`%~V4vbpGA31vsKembX#l4%VC zE;>)p5wV+rDk0W+q7t;W6-!?W^mMFw^+j%Y(WkF!X=UrN?~^N<8uUA&;rB zl}$~r>1Cnc4cX}THf({83yHN9ffxZZ^j}AO5@`hv8d52uyl4t=m1Fb}``VLqL3UZbO;vJ+@M&k=JeWA*F_vkvM31S842o!`;P{(73 zTmrxF_5Aiv-@Sd)izj|Kynjrs_Kno%!C&vyUedNmXZTqDz{rb(wI4qJ?s5XGVp3C7 zK_*`wp`l@d=(`?aDPhv3glYgvA0?*lzqgF{p)G+9lpeIR=t9>TwBl?V&NWFQn z_EyuYfPW4HajB5zH?7k9jDLsnWaeqrX9OV-q(;Jrk`5uEQ|$YANq8a8nx23QI8qR<|7X*JEAL7ZM>YK%9ydKumH~*iC<|-+^Wn9>F+z`^ zEJlfJ0NcPEqVuXw7-@)M@8t>_3}Or#$^Mf7`@jA!Q=DI~c|bDw{akg5Hzt_RhwNsS zhItk^H<(|CE^QJ0A;w4M3^GI<`smvPVSk7#u&TC$0XgPZgDM@ASwOZG6!1Ng9EW2; z0pR~YG)ZHF6$~3=2jRnu4w^4O6b?`Z74B3L4}%T&I-DTRc0SUG#m{!_6FJ7Bt<*l( zHd*4)n$5~6uu7=#9-eS{rLsudJg*7rqTW8k3A8Vc-5Tx}OSoT5XqztE;xNwQYyl%0 zMN4I~jcjSuQmATV&w}@bBM=Q`2aFMr&}Rf?$l=-sWQ@0PeI;U+NL5!#i%fCcrQOlA zOjPj|-u4bDbg4qA(oWYk4TkI`+3=;nmz-DeCG@0_4)MgFFjj!OiEn@}QB??Ea^Ywv zd`TA5SUllN%&6l_%;Oe<*bQAGQ3&I7<9t4e55eDfZKI~*ZzI2c|0}?f*1U2+`h-v8 zOSHSNELg8Ca>ZX9`m)Wf=+SJZbz|TEX`3MPcSx~1Y<1b0i9$nB3T0F9-N&ZI1S%>pzlG)3yxJ=JWp7 zpr@ff=xK#M16wP&GgpoBhjn$3G!{l*kTjM^sw0b5G$XD^wb}ylXaSLwz{PkgY7NE3 z0L2B&hl!Y)VWwuZCbZGSTxdjVV(;V~c)N%0w9OBu!W`4k`*;I*+}*3Ba!_vX?tOa9t-t-y$0ddT?3h*6E2mpo#n>Zjj((0_0PL=ieZd4js-O|= z)b#c zck|-=p4zyI7q8X6`=fSFJ1&{!-B*@8|MW9E<+>}Cn}58Qdr&t18T=?a8XjW7Xlfs( z(OlJxa`oAngUz^@nn}f?c^T6*q)L?FE*2TU5N)7*3Fa#NmGO92p)tCgJ7K$s({>y7 zUa24ScKe!h@&p|}7v-7`pxm}Rkp#u6;!TsI${i__=(YSe!(EvO5 z{(~2OYAbT0ZF^#(V`0orf-yTO5npwG_J_ZTuTpG#+N31MQ)imUd&6tQz$yzOj}CaM zRVE%qCM%CI#U`Z~jL|WPuH-hY4ch!ae}$QjqkSM~Jb-W(U|2n?3YgsPi6>O5G~N>r z!ip0tlCq?1V1KfQ7yhv@(U=?mKF@sL+95hB&#|8uY7c179E~nkj2&Z-Xq!Ovf1KG| zcA=N-zw&nJKZaDE?=QCln9p)J&nvm$Z<2`jsQ-tH{?7_a9q1;65I|QW77&V9K%!Yr z5gJJ$bdkt^{)N_=59eI~JydGN{5$PwZI`x7`aycL=?BT$lnaNFoF-kLM>+7FptS^a zAX5vU$tc7k&~_5VQRrtQ)`+S^l+aIz5~Tis!}(zDgQ{7nrD%VK>a$%Md1YzSF$rEF z9JW;*`?dgS4C^WwL&7d0RLv+KDJBfK7+%k@Sfa5*izPo{*aTwQ3#C_^hAVBF`bZ~F z$lr2y{E9=@gVq4qoP)Xx86eOT1y~KR&@fg5P7Rr0LxWZTI~$fJ&<`RRa?w>80HECO zT*E&z%G|lgEZ4S6$h??*V#_4^%#oA2fQv2kjZG+hjWw&O~4WP*KsXec%Hq+ywG+^bqi` zM3>WBfQR|;9=rb3<40qRbNum?yd%&<;7zV{QRV@UPlxZSut*8VoB+YhF+czVCU_#q z?!=f_7!$)lk5>qnWJosP^1zyD(E&vd4s$v*4@n0_uAMz-ib71K!J1!aA3gri(-vAk5TOvP77L--?-&@fTi2kfQOBqF7+X3qOkhmRkW}S%G+r z1k755d8*0K7NBXfKq3tf7_d_9YP7IBgq1UUf-pFc!5hmRuL+R3fIbM0GQoE>CT7ZC zkDmSV*pZn|fBz|SN6nZyX4XW>n4oRAe&crSE$v_0H`-s;ERy`8CtuvQZQK2uh~H|O z8|5>=Yn?Rr2;3uVASV=AVb;M2^9yR4;{l4wR$C(cz63oDFz%>Pq~Q>vY;ypR@n_K* z02~3lPa^FRRn?6)HD8=~Z$^}h7k%9&cH;X-KIO%;C(oN^m{B)jmXyz7o84MBmQVP# zaqTu<#G|)uyKjpJgpAaCENGYjE7~L$L||fWAR4SMsF)1acbXkQ5kk%|>%))+j=-u` zbrxYYLklxntqHSQA^^Fd91S#AtY&lrTQqP3!ZCpwSOF@8?X))76)<;ugk`hTDewaB z5h_>>PrmpqH#hkV58pdJGeLUwBu(OPW1q%ycOm*tB2!;k!rgeWXJ{WBY8V zof~H@S_FQ8t|iYirhy+u>ii(g2;^0yvZ9~_zM#c9+_1#rCxN^g1aDi5g7FEYa>c{K z983_Je*$S=f^DKn&%_9YS}%O6ia~RXP54xSchLj^2<7ZJaPsLzl{J+;J9D#kQ9CIw zSXWi`y6QXIc2L*8UtC!!!=O&Mqftvxrh(o$tb~=bPg$@NQQQr_&1Nt+YxknQX6?Gk^?1{?WMLr zH*}N>x4CR;2Hnm4&^@yu(++2@8oBjvN1L*x z2Y=XoxakKPhwD&@Pf)%YvpKYQVS6V76x0No1+Z zDC+cC{P1DQ&dWK$H7N^=LP-FtkuYj?vgv$|J%8u)%Og_q|1kZv!`eGTZk{HKvOC_sl;0C++0$QTNP7<%SG>@0eb*7mn& zU`VKz&I~lDNUwqqM2ju}PDp6cr)X8&qE9Jt5q-*t`5%=A`Vyc#(x@;A@dz>Fi3uQ1 z9rgr?#+x_MnJiQSofS-*F6LTErJ8hT>5-W>+myL)kDm&SN=q87uve(W-wD)@Tx4{!+p8|D!KppSI#ujd# zeDo|Yoic0gRKtu#4Nz(vywl)eYku9!-|seM$S^GHVs>m_zCAMpOJkUa<@sobjVw>Z z!&ir9Acn9!yBL(@P))DA(;BD^z%XOx!~4KK+St1~s7 z$mf7lK^){9$d?MblAE{1zK0nd$@FbRd)2iO(w$&|aS1nXvI-ZIZdKe#U|cm{{_Rh% z*9?1P;L4lVEnV38!gmXI4Y=w3A=~q&j$E>!RPM5VOZ7(?1vlh$?NVHG=g?gtkNZmd zlF=FYWmzRP6cNRl4{a+o48;5hc_;k#;O`bR5Oc#-oAfJ#@cS~ruU#-qSjU2wx^CkV z3Ksc{VnPWf2zRlFjuo(wW=?DN7;@}uQ5FsQ`Ypb=2Z!%I^ej>eV9h3Lx81j+`Y_O; zV>fgiJahK;m!FdtESb>w-4{(?^z82a%>T+OQi`ZIqJ9+s_|R`5%V=ckDlU{j!4Lu< z(`6ZieFPwj$T9(t!A2Uby9OC)Fp^da_667KDhf^_YqWY^NHUBUew|}049z)moG+I? zXqvNN!3#$v>*dXBDsQ3%yTDA82T`YJvN!3N8O<0vW~Kus0vBt6ar`BKae!g;_(4o3;`-Xphfb3|jaP~w;SrNMtBR*s4Gl-n4xEF!9nB88Mq7#zaAOj+msfk;Fz5XR-likUzznWjTQr)ch#Jqf(yk~FtbNwclygO@6H zTP!zP3Zho$Gj(G)i40ul6LbZ93C* zgfG^X2!4RfC4NXG8)7VJV{j`K9Vd9g<I4)DwURWonDMVfax{tvtDKg5o!g{L0ch);PZ%5Q~z zL2fBDwgD88vHHKjzR1DI<1B^8MY7yWGfPn+>5cP8n}(dntMTqMU{CJ@drD)|q16(+ z9|dudj3$xP5dPX?T1-6!jg}&o$Tk*R6m$v{!kG+tVJQlx3Nv*ot;uMrbi%)pfa?H* zaXJuBOAc-i#KDUJE9L-AIN*(9!b`{r469409g#1IZ8^rQ%SqCsQ&!sD_a6Fj+yLX} zUsu%rcxWkq<3e?nnRkD@?o+vMAMO1E=`r4bMSH(bU-=((^}Nf?HKINInC6o<8?6Gj z3ag3~4~N*W@FA=)a=YKokDbdnh>2OlVp^Yz)OiC4S zCHWNWTok<`tf580b}}tY+qEG(R69DAK?K-s1lH7!KuGeLBHzA%ftFxEVkj;MkP{4` zL|(uttEFtEOs>Pc;Ag^d$4~RYo_Mp> zEVu`)p_xEmN{5Y0(5q2zg686j#flJOoxGH2)}ooV`u4|fSTbPA*y6iO#y`>P&XG$; zm)1$yUruwoOUui?oYp4uy51-pK7-mC`0I^AU&J9UU8vom*f1Ul>#IU*rBIaavIl%q zKZ7iUI}FA1VQnld0}`RrPv zBmQ24`Nal1Rtme51(PwqgzO57LKG!I$DtTS#7)A$N{A~tPV4wd98@S+g<)_b;5ErC z?t(i;QZjXM5@N;#pppb*fpA7<0Xv;K+n{F=ro$}cwMhzqKqL^HqDOg=4q$W&2+Ju# zy)xFk`(AMSa>B3{%>A}%S?%K0L9Ismf*W{h+V-a{J-=z2@!SjAD{1*!H_L%HyXbvd z1(`P)ZE8#2erRKEAO%=Bg>MF+c85$x1~61bOd_CBz(8VF0kO?R0@DIsku2g9liQL* zOR@{BtB7_X%tJWv!@`IDPXXViklF<_-A-6NL>m?j6wv7}?#{b$ct!mlX>lf)Joc*F z2L*#)ynFcYtxwIV9LDEss~(fyxtKS*cJ1lICqAe;S3Y9s8>iRotkkZ6M`kfi@hCgd zKiTXqpx-3aGEr@ZFOWvE4Hm}aQ=wGdOAkgy=&&I~nPEKZMn;hRB`LwlU5L#rQ`7Bm zrvc@o9WNP8me@EOGFc!E5Z6nwRvUy1-IQq$B$JX%BJjiH zGEvM_wkf+5j(eppU6l}vxLNBK-!gg9EsHlSDK4&iWbtj|=M1Y`ySSv}@o9sH%$Rj! zOcSw9L%T<;hlxJGJ>+&S9MG+N@{pr_4`q%V{PODl-Ijl{ zcjp^beIBeFQZa7!mioHxJuaMhtLEOhmE)IZ7I!(BQ#LQVqd&dksd+apZ9i=N(x->L z=JpqKEUd^ce|}nEX!}VOkN!cvvG>G+vJrho6DqHpWqH>jhPs}8 zE4p>Pp>L1AJ^T0Q*1sntJ7PSHeISeCpg|O~wK}p+TuPDZLdK@*mLq?Di~?NLFZwj( zZ7;68;)<&!wAc&TbU6#(4fw@}a`iEvYa`|*&s;>f2TIYCNx62%-WVm-o7vGX#&7rv6@iz_LJ3Xn<@9E!gvgF^Z;$ORYpX9O#b2xd%k zwa59WKT$fQZT`ziZS&!Ww(``?TY1Wst=i|Cw`yN(Ieq5X`X?)9O&l}%t+HihcP{($ zS?RQRNZb6UBii=Aoa7^q(tWL-+w{>-l{-2=x>WnBUE9~o5aC91q)+-@+J|@(7uFgN z=rQ;qqYf+s=}Xbi(tP3Ae-4d=D1{k`qTTgK3L0^U#ixuo8#!vxRseQiz)Q3u#e-a4 z$=U_88cjR8?Wws<&)E-0)K`y)E}NbZLI|uZJ zCwqJFNHG@vFF_N_>_A3_|9&*VicbQ>6psl0tG-A7{b=I(-2Bq>YrfTjzO-It*InQC zza30X>*PltShKT-Hj`IryZNQEUOh`NuQp#YCMZVZ8KM89k3(FSH~OKK-CD-F$f(2^ z6Hc5Ev!dtg899y8r4ZukvaK1>A-o--EMP{C)%oD6 zz5tIVHL68lpw$aZ7x2g-g9$Aj!S-;-1iB-b|4LdUX!!JLY4d5mTwA1v4Wl+bsX}@U zG)yG>9{Ev3v=va$5~d)O7W*mkp@6sw8tlZ!26E!Fi)!0rpn(eacMEVna>q@u<1S&O zm%^sW@EjFKmc8PZFcaUpS64kfYRM!%x@p1L31uDI4;g2uSh9THO&jLjc6`U_+7U%r z?WfAa7cnH6B37U3XxC+I1~xv?&#(nblsFTJpy5==qm#@^X1Nr-=2j6dYPF!c z+35Hbvbwaz`VNk+8K^k)2MYp6;ps*TUiw0{+XD$H`p6<5xA@{jLjPJqMrmDT%GO0~ zg2@?HA-X)eXiatH%$lJ?iu}G|W#hX)cKhPZJ4a1a_pgx_zczA8aq*^3Wm#z2gTC(f zOk7hMn=t*x1%!+I)(->YAhM~8?uTJ$@xurh!pJK5VPL-iEXGZI;I1Bgc&E$#ln+vl zk9T%@y9n$D+7XVv1M9gY>fdW(bpk|B7RW7y{J|)PDT;PsA;hi?jx-;^L<#CpY(q3H zDp-JUyP#FXNP;leLhiEDIN9l{N8Jtd?#tY`4tG{}pXfFi2ref8@5MI>z}s-sO$B*3 zJ*6E)U1w=mlr^v$#jzV1!mYxtAwjc{>gHHl`Ir$(47g>jB>IZ zyC!S*p~>~#lE#!+XYu_=Jg*u~;Q4uJ;&~ZNBc9*i^fKDkOFK<1F@2j3YTM zQz-Jn0~q_hWCB7lk*=!yClJ`+!IBEjRU(F^q|_y5($;I2Ze!NmR?N*KrcNundBlyO z(G$l0GWny+yfOj}wVnF(BrIOKRE&LSP-S(GvWm_f&Rlo>8_L26RwVZ6L;Ra*jw1`P z-$aYv%*d;*k$(q!_nP@f+3;5Rqg&o{{5Sc-Tkg6=73w%*R^U4*x__tPwdS00E06ru%Rp z6FKRN_&iMSG{uWQ8NQNUX^pa>>1{Z9{vi3UJf=A}U|LwX_-Zhf9aK>hU`^1o`8e@F8z;{Hv*(G9Kgk80;ceiQD0!9?%Z-!qx=L4P=& ziukw~%y_UH3%hn~#OpKxPWxCqU`psaV3;q6Ph< zNx?#JGWki65ge0ZI3UQRn@ozKY`H0pBZu2LqBuxJpgkCTtF8vtX=8cGQhw+!f8Fx! zx6&|8s*MEL;bIs6^s5V6y#_jf&V~~v;zM}Pg8s+ZdxAb9_C(MD6T9H^dDqOpgZI5= z{!waB zs0Z{Adqk%QJYM)d!28i{f5{-n#kx{y$pargilb{sN(fxZXoZ{%BW_fpF!9Rpp)>)o zCzXx75(Gd<;f^{u$P-*5WH+7yZaQU)aGRr#4BJ$Fhyreo+&^;j$fNxF>-eQ%lU_OS z`oM{Nj$e038(=^=C*LT`GmoO<6OzU1|}(vSJSHeH|1zYWb3<442|h<>^*+)ufJ z?!V8!1DiS#-~T@UD64OkPyM04XL2|n1XVQg{;uNvcc6!e?`eEe>`x4=2H`kh5v>r4 zjuo+e(Eyocryk}<@hUBmN&zVr&|5fMiMU%h=sC$QW�e86kFQ_+1!KNl@c42-vvv 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 literal 0 HcmV?d00001 diff --git a/fonts/opensans-bold-webfont.woff b/fonts/opensans-bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ea6007b3b1ed841c815b9bdb0db1c4b930bcbda6 GIT binary patch literal 21112 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%0A?0aU0yo5f~N(n56X3ruf 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 literal 0 HcmV?d00001 diff --git a/fonts/opensans-bolditalic-webfont.svg b/fonts/opensans-bolditalic-webfont.svg new file mode 100644 index 0000000..24661f3 --- /dev/null +++ b/fonts/opensans-bolditalic-webfont.svg @@ -0,0 +1,251 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/opensans-bolditalic-webfont.ttf b/fonts/opensans-bolditalic-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b3eb0d312b91705a3d4ac6592ff11f23a9076244 GIT binary patch literal 36600 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 YG%<#yL~}`xQesgv=DA>jMLxad-ye+dMF0Q* literal 0 HcmV?d00001 diff --git a/fonts/opensans-bolditalic-webfont.woff b/fonts/opensans-bolditalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1712e158f24f13ba6ab84086de4a6d62a0cbb3f5 GIT binary patch literal 23264 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 MIG$G9lV#O^0gfGa-T(jq literal 0 HcmV?d00001 diff --git a/fonts/opensans-extrabold-webfont.svg b/fonts/opensans-extrabold-webfont.svg new file mode 100644 index 0000000..c3d6642 --- /dev/null +++ b/fonts/opensans-extrabold-webfont.svg @@ -0,0 +1,251 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 2011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/opensans-extrabold-webfont.ttf b/fonts/opensans-extrabold-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..bec521d4688d7e6808fa076d151b4da1017d2ba4 GIT binary patch literal 33044 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 z zTm*}VgOr@#yCx-+27Y`m|M!3I-uu2gIm90L6K_d|R8f^K+Num&j^JBe*k&CiQA1ly z;E^?1h9g7P+Q^G1xFUSij9sMIUm`b%LpI1N*_w?jWS!)6zX*`mdz_L2EC*Hf8Blc$ zl#7O%U?4e`KL7tj!(72YjLAW?r8=w+F|hVAFyE1Y{pp!DIXA#9`5sv_jjnnIBz-Ka zF{JVVd5D7O;U3RyQm@KvbR-cS%W)zpkKrjBA)i^J*5q&At-||!Lq=^P6n%6=FY2ic zj=KnxK&b0#Dee#T8sj6^ucKw`A;-B#d>*V4mQ;!!eUGLx4cBO)ATK5!Qm@5c&b=RD zhitPJP?Nex0!?{|BL`dT{B!1NKxNK1@U>dwcm+O^oI4k zu^O*0Tb<~SwWGmxdT+{GYp2Jyqu68_T z7K`XCl;j$EsC_>V>ps;#$zc)Ej~+0K+tkP8lUl*e(;UvAeNk18nZ+VHi#Tg4_u93F zXjos_r{9q;crAm3EyoxcALv5|n5!U(2`Qd{UsTr6kuvtD6}>mJy2Sj~%**V(L!W-YQHO9YB$G_F6~Zrm;0$GHFSWbiED*~jyVSBp1*_Y|KHUlQLEeh&U1 z{zLp<1dIey1g!+~1m_8!5n>P$5b_X85ULScAS@=_Bz!`|Mr4A>BhfXYzr>8hYQ%Pl zT@!Z^?-9Qz;U=*{;+dqC4`Fna+LA{TT+i z)aR+MQs1V2NP|bCPh*zGGL20d`!r5zT+?jO?9rT|xkPh=<{r%xnpZR*Xui=((8|%O z&}z||pfyKph1NUm6zu}-8to44DcTEk_;jRnEOfT%n&>*{X6bIzbHD);^cwUQ=6>2 z*fVPQhJ4xbKD)fPe83?eE#G9Hua<9dz)#D+ne)pzQzl{|mgL+KL~5MM#*{qpMio_s z*}$hbWyp!=NPeZJ^mV2?a3-(x)OQ-Imi9@wHkZo+BlB~fxZu_IT9v7p+4V(5=FGKY z(cuUko?A7QZ%v=zpAs=#yI0=h4)2W!DCm-LdQH+}sO6f^2deeu#^rJr>ybPcWAASZhh`|hDD8m@e2u3oB z(Trg%;~38bCNhc1Okpb1n9dAlGK+GtaG3RM;5vKx#71_qjeQ*Dh(xiK_pD=&SS6Yr z62oS0@lkB-=NP~F#UBoFg8SU%B(s^rE-H9PCHHv1Qy%e{Cwz`v?*-3z&MB(+$!=cr zl2=somG5k!h9Gm9M=f<6pq>THCqx5b7P5%Nd|?U8Sjuu%vVzMTWHqZ;!&<&YK5&IM zoaPKydCLdhNvy<)UE(D{5+zBJB}GytP0}SpGC9jRE^>+6oaX{}*u-(k;s!UlCfSlB zxonp_$(I5tlpOh_O@62cj|LBxQ_C10mwh9#OSk%2X-E2se~)nHr_ZL}9C)8JbM~~Qi%{>x|GV*Nojq^$tlHeS{)q1nF}CcT zikZ`9{Jdd}pRtYeaJ{Gk6>%L*Xy3-e_};!^{<2jyZ}c0A?{6?BpPaXF`m{gK{0*M9 z`A)q5)A`d@Et1@lg|V$uale1TwD~hTRA0Xy-|6}rixw_j*4gX+im`3zkMz}|B{LWO zdwRws#&&?9d=!2|)M9ULUf7jB&rh)H#%GKyt~l^5iE6+Gbm!l2RKz#qmv%Oql{00) zpy8ug%JkJs=CQ2VOJ?523g%5)wt)2!_p&Qj#Bb?$|EF_)+sq{km=!nD+vr`yejGL2 zs*0ygVQ;WE_)fkb|NSIQlxk4El>RE!$bM;}yc6X&%2%c&Q<79;c37-@r=^dj%yI|H z4xAlAq3>muZ!LFN7g!fq4p|rAX+K^44^OK>G5(u~r?>v6d}aJ+cF6S9tN+pS+WcqT zZ7IW(QKT=;4(k!?5z*%TX!m|r02(Z2?NN$ZCsu;e1Em-9vH@3W*q|#L*M*up#C*{ z(i<9>2~V`6Wa0cel+N6aw>98x4dQJj=v5CC<4p~CQv=@AfHyVZO$~Tc1Kt#Ua)WsC zM|j>v6t8$gJbvv$X^*>#(a(}AO?YAx>xu8Z@Yx$>JR0kLTQh4 zqLe|UYEY>fRH_D*szIe{P^p@IjQc)C8H8UhX4m%c>d3aX89g6>_YA_lgHeW{3_~fy zlSZP9!uiek9F5Og@Hqxl7>CdCXvGASTTv#WOu~5lw|VkE(cloC`xxGT80B%4BPdUx z97TB&*N&k)g>oF_X_PvY6DTK9o<%u@avJ4%wCN1W3n(w5yo5FpEnmg82DI%1l;|kN zf-+5@OasQg0W>9!YXU8sK+7i3vI(?o0xgNt8ZhplH-1rrUsU54)e&x~!T2{}mNa38 zG+~A`fsPHJV*}{e06I2+j!mFr6X@6kIyQigO`u}~=-320Hi3@x<{G@Y25+vxn``jq z8oap%eA5IjZo=$p!t83o>}ta7YQpSlg3M_E%^Eaevj)(t0W@m>%^E89uSU-=SPY0a`(QF$!l>@C-ZX9M4>=J?p@-ST^g3 zr{uAGpnwqT1_aO@2%rb+iS@rXczOUE2-!FUkoE>Pf|aq6c+bu37B-fRXA{`1Y$BV? z*0T-lE(V@to7g>UGuy(pvU}M!wv+8;v{8digQ?gnf!0FtmR-TGAHZwHNp< zPvE=m0+A7>Beacw|MiS6%8JZ)fF{gTZ6ZXTVSLK(vRMT3u4)*UnDk94^WTcfj`YK> z*WcV9t?~b?m}lv;7_%&ulKhN6Lf4cbrjz*a3!DhtOxPZ4A+?>cCVZo_G_>v=e0ITo z3efl+>re5w^Jn_A{Q3SK{sI0e{$>8-fy_(nXGQeM&ko`j zqgsE#{7!$8KP~c$l52j!uUxtE#g&(@+<#^9l^b=|^0$_zOK)C!_0ri(^_O0_box^G z(vz1CUb_3zl1n{5JLhjSHAY%XE&gBs<7R@z#y>8|_luw6qO6$A7OTx36C3Amj zZ$hFkDLExIExlcaKad%0-ytizW2ftKay#eccj;OX>Q-1(+`Xhn&tARz^ew%#z zgNF?oFGw?%IFgu|tQCJpRO!PaQk{ z^fNW8R(qoEMZ$Nl&1*~@l6^zef( zpL=65W3R6fkI{kL1PAPA{h=@}XO);ATiJHD3;cf(+`a+dsxbqO;9Ld2pJ!FUIFf`~_m8@YZD;u|E=9X!GI_aJo2#inNBEF3>zR?TqM(doZ&H!E#lk5Mz z=uT{|KVP-vOdRL;4-O8RHrGGSKcn1u1l<=$Z^o(Vvk3b;B1T!FLl z(3T8ob;~Bx$)AQ*C|}_{63e;rg_MKm$3V^%v#=!_7&|rQ~RC zo}-zW%dN#aOG=pLkX>#ywxmnf;%-Hug2V)`IhdK{aeG2M=cWOjyN~GCwRm0Es^95< zA2EDfanXRu-T5jWlD_@`H0*v(ysz0x}%yxb8ncy=nw;3X`a2%3cxl2byVP}qY0 zg{?8MIIm+o(Gr(aE4k9r+xtQqvo+RweMzb9eFb$UMO>1d?K0?+$=X z6E($IE2UUGIkmDln(2s#)!LG+I7xKY+7n}NlHjb3ONhm7PH}XJqnD0!k5Al#UnPlO z;hCw?=cGlSlTP>4wu|1GLG^N}QuIe-%M zP@zkG<9OOk{p&d1dF|Fp-nD9DCAY@v7t48ioPNG?>pK12s*S5vohnqqHMvkJoLian>cG(FXpqox(8 z!F<&d(qeGQfx+mUuUao?8Sci~v<#~?M@w}!hSO5%{xpXBGn`r{jE2wCSR3c-gzFd= zH8x+1!|>!8!_(*X=4=`jMNM<6vGr=IQ;n-P;Y+QO22!8yv%mQmuE*4x=&PwVhCXmt zts^xK#P!he>P=oznLzLJs>HO8$v zY1S6D;~`HTFYH#-mxnBgK1)`%+>T=!$MaxumdBgu^EkMt4{pfH9y03rq2*gEKX@s7 zWA-Dr_g!5zc~8~nuXWwn^_6!A4jnyj#qQhg=r>ZW?$nvT+I7#8N9tW_IJR_CUzUVI`iJK^VjrM`t1ApmEpH_8{Ic+Ucb4e(gbm_@62su7Oc5FV~4x9Pezeb3&f*YlX!N7ljcO!KQB z=$v7C8}q9ZD}=#3oJ2HEBN}Jg8^cy2Z^#Zp770RTcp7Q)U@g(E!|8QStqV?LJdIj0 zRhd95IW3cD)x}k7?U3bc?<2;lbtNVxvt>H`j;=4YF8~CJYr&x8^^E0YKnD;|4FPvnZ_yA4b~ zYoh1M%;l6rZkJQy2l=;)H|+dQ{~4{ymMZxzoa^7|&+1RLyrega_BKd!m38J+vc`Fe zO&*`z!P3EFk$E<6DCSE$9be0jSoM{4M;7b)GG3@0tA38}2Nc=LpEho$wopRY`P{n>hl?pT)W}{LNw%O^L z&4SzPHafLKU=ziNQiTB*P)kC<6?9ecjobL$`kHOh*8BJ-ef52MCEq|i&96M9|A5>7 z&di_@+E6Dsn2kV-MU0%1#9}aVR`EpHT^#b6Ei>*I@w|2M!}wjh4}4$$6a6UW?@J}p zQp}J5JwxMhja1qUKk!Io__6F+ii7;KO#c3X1Nbel6A!Xx&>)Xp1x>BkN$^3Pft?~B zk^Z#>OgSS5=MO=4$Eqah;`W6<~%!Gdw|jcS}z^MYH<@r_z4KD}5;G&^A^EfFf7YNna# zbhi)r!9GebQ!*x^yU>aGC#^ori_b5vU&$|W2XFjQ@1%dI zC;iN>{PXy!FZuCrc>h1^(=aYPA8p?#cy}du7p+s}kb&@|kYHowf+nLuU}FMEefBh0 z(+cbWdQ~l0du698)t~OOqS-+UFKTN|3LsJ#$Eyn?(@d7UJRX+lBhOgtxFY6Sm##LD zEa-6;V=3eL{Olh6_2aMhOfMK8R;qa#Waj%^DypL?iE=zVq(s2t<2v}k> z_`9?AOv_(2`^H`4N4@l^`u0uFp4SgbN78rk!;`jEuDxOUvXTKytF;Fgo_$OXER^>sYsz;m4=P6;AY}L4ZeqKtU1Oox_VEg|F>^*^)q@fTVQ(|7h9tvg-1(SN>G{>RYw9-*@}p5C6XB;jV>`Y}v4Bd(FKQ=N0u^v7=`1Lm&EW zz9%X-o?k+kAAU_`xamd6G!JY{VHbJ5#<4B;#5uq{ zClrev)(!!eB0MC;V2F9VSlWw8%di9icsm4&+@7F=Tb$`~zgK_vmq6o?!^8BR{Hwgu z(N@=VA0Kf*{{7`n59nb&;(m1a!!vctz-y0B|Qm+}e2UXvaQw0vJRcG}5?o%&DO*`9}I z)gQlOS6otwbo>E*sPEnIqyJn@a!Deef-(U-nF6kyYoG|N9eM;ObdF|^ZLD*(PcorL zF6t4u5-VYkL(Aq+mMpwRW-VPard}vWtj?^je)#QhHldp;R<0N@j*JMW+$rU(*!OMq)JI_gm$QzL8>eW?<@nqLi*I^ zdi`agW};bn23O_=Mdb z3?H~}>cVHHJ-ugLec6bbrE6ZA^_q6u9lO^|-`lI#6VlRAw-@!=K5N2UTBIdbj=twa z_+>f^CyN;Xpuk9B#zGr*5ec#&&g|`yU0@u0gmL0p87D}L;}!BuBTYz67TS#6Wd{2& zEsNO366ZCGehZctQx&XH7zN*U4u(f(4O~vxvi$BvZ~ati4?Q;h$y4k8yrb^*MbGM& zkKeO%{=DILj_K|c%SCkpR0Ew`?Uqt~BU3KvhBZ@CyjlEFk5=N9-vn$pLaVNeIXo=WXlF-RmvaQHA z=ov0E&f>l3-Y={xgr5yVODk?TST=at&DUSW5Ax$T-dZtqQX%oMBrp=S4|W@N9aH1; z>+F$MxoOF$i#1F;9(p}3Zn9xtiQpxG_UQW}(5WW!wkU*BUX8wuv(>CwaVj6;UCdL+ z6Kp(TwY;w-LHb(SU(P?@=)95XZ*+b7R(Z1c7MD58Gn1NNSl~s$8P_ z-F5K^iD^MG1%t4Mh22Twa=hz5En}@y5z9!h4=;|YIhF%ugL4aM2(u@?G3tp^8xB^E z^KD9;x%AGmyxCJn?(Qd$;m}FlOgDuJj#jVz-Ktv`SFC^H)*0hw^qDj>1kV@xx$w#$ z^LA4mbi3=>drS@GYk9UtGT+69v=VEh>Mv0HRUSb;6(ldH}SLI@6(%3)asueK6Pr}-4E+KlG-<4&Rc$WW7RjDUC=jusWRownu@oY6m{H1OL?8=qNMQ4s0NqZ1xB?U>diAv}CD;D&Yyr z0htG6693EPjG9x&N_6MH_)xwjm}KfOmQ)P>uAVQ==Q*`cjl&WsBgBp=VYJ=Be9*LZ zL|PvYjYC+W{aE#reAId^iI%beEOZyN7+}$~7!MXL1&9WVzkvD=H9sd14a-ygc`9>4 z$o;7Lof^kSJdB$0e)J=Wj`9h{<9$hf!9E0WnC6Q|H)2ZEB$!$)n1g<|s-RO?dHpVL z1g9#&4uNa2Yz`j-@vxGsH3yci)!(_I|HSPlUfy$WQ^pSuo!+<|N|ml|dpX_ie|W(k z{&D}KyOyopvGb0K*^n`aRxsHiJCfjS6;@=;3Ee9kA+c$8_wi^9!kG}HM)@R2&w4hmUl9Gwl zn##5fd|BUo@%hh6_GF~t4K&X&7A7yo!UhaIDT1L{h>({t7#;#AfX(q2G!q&Yh0S}K zAB@u7Bj|1i!C@%_-Q`+n?4DL$@z~J|Q-rLrEJ#WqJ5wkpRVbKS>p5-GN8erlF{fKYoqOCFTwHdk<$YwNC0roWsj@L{? zO0vmDSma1^W3;J?XsQiOgK(^7R8PB6ODcZ3Or&9Uf;xS8zx9 zEe7;T^rgb`sV0TALj7YQYYhELol>m*w3ymSk3ymQe8iO~PGzM=% z5dK3@KIMZX)hxlig} zSHFF#dhS&Hvj9I|{lR(t2mNEo$>aEQ|LT~#KeOPxzWehRKg#B3_P3PWzyndbo0bZ` zIB3v4Y_Z0WcZS3X6nZmxXJlBx40$M{x8x8q>}oN<#WCOptWpwAB`4u<%>o?*)?Tt# zlBpxgO9z|ZCUTtav$y$xpi7LiR*sRda#>;Dx0*<3(h*OrwNt%ZD=Sw8CykqMf54a! zkTG({Hb}cS9@XD^VtdQH`%NcXc1jC=8YZbNWzl{~u*pJV_KomUTf2v_Cz9O*_Kj%s zslkGs9%z1s+9z`obmFy`XT?YLH-*WEas8}bC7vD2#xvo?g60g1An+R4Gfrs#R&?AE zdEUd#FVgcIAc|_PCoS0qCNN{FaB+;NjL>z-IAC$XlF_9rFNSD>5jMo7K&F1yJaG7o z4aq!oOh12<{^T5e|1NLkD$}u_2Iw2nU1NTm$affr*b-xYTjC<4s7YAcxau$@B>D_H zRAk7s_(&TMG{0&Lj&Tx$duru43_@)z9m6*{G;+d>|1?Z_{o-qvKc+rN0$*%^T_6tD#{n!HL%K1HLI%P}qKOUbBMc}GADo>| zTFW&HWIY)o6JYw$6uh5lNwAb?40tGF$t@6zHsrL>cgfjA`mVIMp5OHQfAS<%Kh3=-=>e2oGxcAHx5C5=P>cFkMu1Wt5!0UJV*hZei-LHT43ZHlJ_0QhYX{{8vLqXtx zu-z>+D6ZNgj0?TNYNvH@Kp%4QW2+AJAre&a!VHZBpPwB60T3fyGgG*a?JZ3ui!kHiRpIXjJ@19gE z7aDZd51WY2E_fJkG`x#iLX^&FL}&Q)(PB5~?1u6ep)>sVS~8Z%S|!F|7b;2u0Z_Ye z{{4dB%mQ|{{8ysue{JIKkH2s^Ba}bTyzxIMU#q8#(5v|{$sqq5e9p&F@<-^u8T5}~ z32bYG<1JV;4bkDn2-suA2mlFkHBs~@vQ$EQ5E$I|YrKz@4?qEnpO@q=9Wes-SZ{>W z;a326nXLq72^+vEO8C%BX1lB4+i8MU(Pm&I!ilB)MSaINr{BH2?%>h0?`+zCWENoJ zPW|xAmetbKmV;9Bu}4fqSypEEaArEJT2_LRAOS5ej-NA}%mGh`1oz zSOBv?DCDuwxb3p=*_vIow)jLua+Ih6x7rRZmD{7GSltp`wLI1npeyli)s1=5Y8TBW z6WLX$V@~k4_J@b1Slht1hFyHNl0mAQ}R z92>pwt`RFo-g0c&^IvD>?aj*Fy0YlT0lmk~SY7sBBdot9^G^c<*J0c&Y#0lh(Kkf7 z7-L3OZpA`YZbb0F@N2>XgD{r}9Hx!(&!0! z5)&Y{$x(FX*fg)wdnW(vrOV%8tS(P}b?c6o9D9EctyqscWPwUvm@FB03DPDrA)9|k_Kyo^F_70)dbLu!FU@s zIg+3=ikA7j7SS{`(Kvx{$tuKZ$%CIA%aH8m@%{OpaE^`JZs^W8oNTE$*41iG9-`;g zp!I9Vl^^`>UU}-}!w>Ac`O zWQkPl)SMuEX$WcnrjmJuG+O`h_<8>P>6Z6-V%__ChHRB;THcmIEyG%vw63KpI&R7< zc!w47m0=6f%p5YtTpfP4%bG zzVeSms?{u3WOvbp=qN*JAZ3fZf%GZRt)L`^7v7sQ$7*@w*_LgKkFNv*ghBH1?3OR2 z4&bpX-+~^y&{i9KxDgB+T^VF@1KaaSG|M)4erfT4Yk)4 zXQ;h#LPJ+f5s3_Z3G z{h}S;sNbcZxzF_3wjW_t#(XT4Iw_LrENsjXdJ2dRk3X5pV6}uYNaTDu^v#sWD;1z~ z2TaR9Dv+X57*Wo68Ds)JqI^O^K!&kk%zSRY|4qHK)QL>a($_5sVhjhM&)cpH0X=<& zwc3st0WwvS$r{W-tqxeuw3Bn%>1Xb}mwqGlRX&tAnFE*u9T+qSicN^4*aYxCDHB-0 z!0MQ2F!UR`S79j8mk`XuFHhuDRP^3tj!n4diR9j=vG$V>WvVg}5mJ6+34~)YqG2Zv zS|VIXHuxaI3J&B@62>YE{Rq z76-ZANOFsCCSRyHBnOLynKc>`VBpB|`+nLpaYx6P{#A_|Q>?Y;wjMY>`Ic$*s}|i_ zQ9FxIeuvL^Yx<<)Kc9N{Pv?#0A$Vf&9%&{J?Npt{U+$>qH@epPQ zEv;CXpm0w<>}aUCi*3@0soM^0{#W(1T^;O$HZ7bp{i%xZ)Yq4rz0LZS<0}@Pd-`4d z;n!!4JH{t0Sbm7-9R1=&{SxsY#zQ`a@n~ZgL$yC7P9)h_6b*afjp0!l^f zg5Ai>B7B-=!!B4vX6wwX^4;9@*yauStEMk{ZqCN#L+#LAINA%++(y^@X&HKQ-Hz*um*xuv1n?sSfXN` z#E4o9{A)pc#w8G!xP9%2#IdfhBObvHVx2OB)a_sja%MSw+MG8z1eGA1=dWdm_aGf_((nfbqd9)Ft8?n1lq^ zqyr?@D)EVeYb-9!Eucz##8_*0X$-bn%?~b$c$y>ed`ci_G4P8p)@DW24)PuD`F{5U z)q0(NKA@~!*LUOe$veiL`u&@+Uw$`q#OxD%(-}U#e&fozx63x{YTspNZtx?1c;({4 zL4Y&-N~-RaH<&Xplb>Yab_|0gp+`QNzbSCjmrXa@@Gv5{!R3yh;LK_ zAqY7HC#veFcu3$k%|qruSBVy{7&aaYP*A2J&;C|ZRLMRduvAr6P=Qfh`;RuFcGbA)}CBxO;|M2Q8286bGN z6O{Z;M&NZ3PE7>E(hP4_Z*W2a>JpqP@&{a6KF;&=wID)D1{zDn4>RkR859jU)q;90 zL-|n+)Ytkm0x*T(c-0ibYBT%=Awk8~GXa6q2vx!zSmX*@vvO&|hlV)^y&&8aTxR5oyWj=%Eemw)iymegnJOJ7(EM|4WH zIuh@?^VD0r_TD(5YFVEl4rlTmvip|tqj!xNHD)AC){uogfuA}-_M{^N%)n3XbZDh9 zG+0T*x&}Z_$Gl88!sQ*neQrl0aGzME@-=d=M(x)T|B%D5aN#Gw@U?O}83h$_G>M~G z94+FQMn||D+Zqck2poZr7LvAv42REh$&yCtO*D4&kN-!F7)KAqO2Z89c4;vH;}|l+ z!Bz(i5Y`ok+`6t<%z%{`1#&D}WLi+%fncZ9n90x1*s$G~J@@Et_pb3zt_#dGdq-72 zQZsAq+#PrIsW__#q`JwScCYyS5j~rDA8R5G8K%#GEgWRo1x1Z$InWQthWsKH2dD^N zAf(&1#$#0>hPEX&Y7zfnpMA}b5hn}u1`90OZiz)V(0Hg|ZG8DX|9bSVi%Ub}`sa4+ zrVRP{lxb@B!4ryycV&{U>s~1x?TH1}S&rDksDv_6gUlhACp9NEC@EhxUC_)hyT?&0 z+-L>tCIZPm)_k#5LMZ~fTc9om0p7HrD^ZGq8OgL3HBCneS@&HYbwP=+A=FUT60H*hKs(=C^qhWOFX7H{KKW`UApO11btqs*aGB4V@+xm>=mFVAEU>H8(<3 zKn$QbGNt)Z3r{a8#4d;cuPX^6K%kL)5f>KC$B4Kp9csX-X4c~;Y6jRRBa_&Nj)I~D z1x2ii<_{2?XG4}ENZ$^NsOoiV4iTc6joDi#EA|+dfebyN$o%z1;i1Kt#lR{wjm3

LP$4sf6dITl{d{4UGLa; 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 literal 0 HcmV?d00001 diff --git a/fonts/opensans-italic-webfont.svg b/fonts/opensans-italic-webfont.svg new file mode 100644 index 0000000..537d20c --- /dev/null +++ b/fonts/opensans-italic-webfont.svg @@ -0,0 +1,251 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/opensans-italic-webfont.ttf b/fonts/opensans-italic-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ddc75c6ca97a5c97be1daeb734b4f1a62adc7ab5 GIT binary patch literal 37252 zcmc${349bq`afLN-E$_H%#}<|GLwM>Oh_O@62cj|LBxOv0Xf4J5K!*R7y%Ih5#%;1 zA|fJU?4ATfL=7Hzu;C?~_w#>0pI5A&p6Z_N zs;8cM>ib+3#u;O792DjsGGy%a)B0`*F~)OIn>KXN;2}Jf$@o4D-~B_&Zn$x2{$Ibr z_e#cW7l+<>(}10Q)?LR~3|+_7{DK2-Z1>>%K75}xW5M*>o}Dxw=P^IvH!U-kFZJ7B zjXTO%Tr;kh&c1EVg82IUCm3_!ebTHs)0ftsUvHXOqDj+G0RuB?4yz;JwjgE9HU{99*C|MRTh;8~mQ z#QQ&8Fn#51l3TJcwq+Xb_b;5jU{UDp`*jDsM`s%jDvu^wM zjEu>QZSRKbWAGcI7JGBkt=;MK+(f%>e8$M)iUZ%0sD?@6``>U>#5d!Yb~ct(FlEq? zkz-lPj8%*0v#dFbXWhaI=1*U`ko6PyvdfpnZ|Qgcr*nSGti=nN6*p45=v|Wdv@&j0 z#nYy;H`p6|2j7Q(KS`6MT9hxPze=^TUz#NEK>3aGl_|-TB-NT77AxOj>1Qdk+<~$k zX9rQ}dzs~1%N^E*)`gaX)`fW5PgnloX|*WEze#v{+dt(iv1jM%jq+2%dKk_dbSa9zuB>U+PoW$`c^ai2D6gXKH9ViQuIwan$K@K9g>nr_ z0ZI>)Vzi;>nA|v6=P8_dfXSi?R;q>rw7P*?@94%0`q&aP2jekMa9Y zQR2iCqiv10a})0TZ|!;o*NCEPBEOAC&s`{);0|ihe|z)CxaU)pvQVpn7!#>7+pP~%GFBh?^dw6AJ+uDqt55jwf;NGDq!%#+`l;KIE zQO4l>MtqLN=S}z=2P%}~a{^j15#?r-Nhp&s9{+8g{7*DEi03|r_a8!e9OW>|6DUVe zp2W4IC{LjrLwOpd9_2X736y71PNJMbc^+*#jq(D@izqLlO+?FAajg+;`v4_6im{+f zGbq!Dv2O%TiQ}3<%VyBB8MJH$Et^40;&6p+4m?6!W zAEyP6?$8bPy0(5w+OYXr?2L9<5CtPwP81kD;jvqsRY z5j1NA%^E?oM$oJgG;0LS8bPy0(5x9WYX;4lL9=Gis~PlaM6YYm>l&;q45NKD2W|Kd zt{;RuhoB5a8HO?fPq-pcYa$Z0CL&R5g+!frw7P*?@94%0`s`nOkbm^BVNL z20gDq&uh^08uYvdJ+DE}YtZu=^t=W=uR+gi(DNGfyaqk5LC4??+czD#0En)4r9aFb!-$XW25n&8`({4Je$BKvYXi?HifNY z>)BllJjph)d)OwnnQdYBvaM_f+r@UXJ!~)A$FQEVhuFhxKXAYSc91>B4zb7CVfF+& z%AR7!*wZY`>e+F20_*oF_7ZDgXV@!P(;L|b>_hbOWA+LA6g^;Q|46i?J-+KG@Lit3 zcRd9nBTPqV8~^>+GrA}%GT#oGFjI|*5P632DZk5R6UdWd%x2l7NRo1qN#B$*|IMiE zOh4>??TrJ`8voCVdA2^AG0PGu$$dGB2{9711X@ zJAhw|Y5N89JN-%iw8$??uKESPeEIShmtVeo|K&xOuh&`Y-&&h5zIpN0i)Su2Tzui; zsf*!@PhLE5@$QR@FZTZItiQ?B6lpCQ_`m;;n+=Fezun~>;BN=`{lOYe~34`c>AcFM}`+~t~_+^%`~-MSZqdK4BF_blnvyHDSK{Y$SM zFc8|(&|$+zTsQLiQ8$#09&_W^o5qbVpD^*}Nt35coi?4_y>Zi)o%{AbcJR>Q$Dere zsiVi9ex_E{>Wz3rC2ET6~jsbNbPo3lc+bN=n@$f>n6 z7SfjmRTnGPZP@$DtBoIga^b^|o?~ZT`|{6!{Nk@)vsE8|%I>;%{npLfw%>o>?mcYR zLk~ar^4T{QG4}du@faP*O>n?IHjvF_%UBiW#}>AY?F9c{0JpElw;IfV!#G#L@8?)G zJAzqpKW4;Awnt^TD)U^YUQ<@Cj#xfHWx@VQsyU~;?*vg-HNpP}m3#7%x~e?a|B)J- z(^ZvnuNzZ7I5;8DRh4t+Ci&IUvhsjhI-#qo^paQce7`Dp8XokkO4bOKm6dOvwRyUqPI{&W0uxd?%0x}eI& zl&ht<6aAyts-?5&3x4AeZ|ZwKDG*5YpWh5J;77yJwwoetL;G-Bd~VQxKJxOQzx=uz zQv)iWP`()*8Xnvn^lu)%IXInKLfxSwwZ}=r;Knexr~`E3{zV7S(BP2Z^m)_T_k@1x z$wenNZzQsfm>Jw`QT=7*eNr26#hY8lN_pvk0sOj?PBw#yBi(!xTJGOGraV}I)&~cq z;)4eVfbykd%EOQ)17{2fb3ez2>Yt$|&q@<5#S;^9Rn&qmICLdWmbRQ#0|LdRctaY&?f`>mG6iT%7{0 zz!`aXYlgI{b))ITPa`Uoukap;lsh9#O2gUuC(-yzL3UjO?6&hQffzELA^;4mt<#$47y~pHr1JJ_BdSP zDtIvEyq4^2Qj?upBHESUYEmu!^DfPblLV(0gSuE}lNR7PYEjCmz7zj;mL=raPxZa~ z@0007O>x#qDHcyooh*)KI^tnO99`n*r6b+r6Zha( zN#a*{W@_{~Y0>AT(>-+^qIYIcytnXo>Nzvo@&cc z(+bpJzUm2SF}UQwV06t_t>?82cT-(jhSi#*r8=9!X{mI78pHh=POS?@!{=$Li}Q8C zb&QJ|o3F)Tc=C+l>34feHjRp+ra9Hv1~t{G#xB?X2zI*W_4K6htTe`8o zO&?Z#@BD*rC4T;yL%zLmLJHp$v-Hy8xZ5WcjrC*PP0)9mO_xmPFz?;K@OePDwJaP@ zGd7n*U3Y6!*hzC%A$gZ(ZK{t=$W`JnkT~sFEKWOGAq9Jrpwc{;fgY!pf-$$ytaNIA zoaW;+->DViG~3mr^}(kHVoZ%MQBz#uST{sUiB_2Ja)+I{9f?BOF4YAA7uy}O&YzEp z1ecawh?_bVqsGHfljhPaDJ3AIr-$2+ev*Te{No`mdqV~Nq{1G-Ob6#zUoOU_BDZnr z$gE?Fm#!?Gb?^8EtNSbc_Wu0J$Xj}h?VmM&z`RmvqPW<9*4A+gSKmAD+RDKT2F~p- zPdd!I@7um%zTT=<>}f3|K`u`_cEj#BhaSG;$JX9-b=!tpbX}XVd(YGBc+8x`Yhie% z`85D^&M>`=`PGFLvc)W%L^Mt#8fV&@!d4=0$PPjl2|{LgnrQN1Ezz#Q=`~KR8%|?9 zOC`srt38C2wuFT{!#&?*kJvJj`)Y0dID-8^LHE~h6b zjro>aAO8O5#p`!+C%;iDlR*T2)B)t_p8NpBJDZItFIYt5-- zjq?MQCGFVgj;yihq>^BmtE)YrNn&ezxW;Cu8V zt2j)DoK1X5KOCdF!Hw@w*Nm_`d!p`ccf^ zmrA51m>~grhQ{NXsI(h?;E~AiW7)G52l;22{Qdp=@mpXg9%L<`K_0sTn%c0F;DdSt zJ4HSs{c8`Haz+o$DJ&b5S2W`Kn|l-vo!Z0b+d)j)D#4d52$3>H?pk~4J(};pH>)l; zD}ykXV_7mAz`}N##Lk!|Lq;aXpz$e!1>@qI)HtW+1-F>vo3vDXda;sdcEV6vB2+xp zOf%Ez?ilieeUxCPWK2YNp%e2@T6KuWJn2EvmezGCEEZGrB$1OY| zV2RD(@6O&mJ%8n#>vv8V^U|m4+c!LWPCpSR)zT5wP==Z%3buWBm^ZJe3YVVyizi7bn?X`Oz z`p|FlJyEsc++xD~@M|g~O)o;Gd0=A-+lVu)p|FGw)w39zgw7~H<{DF)G>g3e@XnjB z+RkfMh|+`zuN-K3$KVy4Q+0q>tWH&FsIyoVfHE79v&{ie7E4Fst9=5w#nIO=j_tW8 z&H?T@u2}4_b_lo>;UOsoL(JpF(q2qjh9wBV+bK}w_5>Z=;!KwZy!yMp1e%5&8lm^* zU*(mKwYp~b_^AEz?=N+EKo9d#4}8K4c*pdO=_Ds%H5V3#G+J|0X$)nIA*orKOKGb2 z+EN|pwwG4k^nBHPUh{#~Iz%*wHGeOr&Z>?)2Z2)j;zx(-ZeUuR8;!4v13;vv8_X}K z8Y683(2FIYMZ&?7jZb2NKwQ@yKW zk_kO>QIEitSP4_TO~A`AT%ex@TWXnTYuJ?(M*za>((ERhQ5`$D+;z<1g_>2O*dN!E?|U28mEw0gi-;wqz$Wn%R8LBb$D>l>_6zA4YKxr zW?oJGhA(%Xc>VCI+otRsePO`u)4K5^@A8LuaHsG7ME$(}(G62xJNBsludQGGcGLd( zFE09_WGc9o#;z9px(ag43_ppG4Pt@Uq*!##k}p;kO*WQcXxSXfl7-jGthIZFye73( zF$H#S`RRSEEYm=_oMEn!p8uR4Iu~&<-^-NRb&n{V54xN^~R54=07{L#CYOj~>p zpSbITk%RY6yY-prPw!saP&TS|$?BJ8zoy-C$F9{g_VnrVgtTPL?L~dI&7L@q7HNr9 zpzk>mewo3-$zlcoC@@l(vCxKHM1m}cGkb?*7Z}GLVVt-&#t9PRc!fOENE1?%g*Ib% znZZ6x%ObY1#Cgr4--6}ER0V4kM!~n8gW=Ix1D6xFthoEOw|*+MhaQ{pK|c%SCpB+0Ew`?UqJy7UG?!1BZ@CyjXhuQ5=N9dvn$pLaVNeIXo=WXlF-Rm zvaQHA=ov0E&f>l3-Y={xgr5yVOB-%DP&Rb>jn`hm5Ax&J-&{F-av|}sBrp=S4|W@N z4O8Ru>+O+NxoOF$k2Op?9(p}3Zn9xtiQpxG_UQW}(5W`^wkU*BUX8wuvsJ7`aVj6; zUCdL+6Kp(TmAtn#LHb(SSHVBqt#8#&?j@R4^V5n?{u5?~1#(27E^vgBubF7@=)95XZ*+b7R(Z27zeF58Gn1NNSl~ zs$8P_-SzPaiD^MG1%t4Mh22Twa=hz5En{s{5z9!hA1{uoIhOrpLvssh2(u@>G3JSr z>km|w`!*)dT5@Mu-kfQpcMTB8aQNgNrW-;9M{3smZspC3D%U-6^UU&@{U*-}!SjWF z-g=eiVsYi|&S|;QIu=2K#VYYh*?HYUf_!-)OzrL^s>`KzE7AIuK%QAy zyw}mukY@m?fda*y;3c7-9VE#gk#0T*_mRd8X~%+@ij?4Ffr8=$bI{K?P5D%g3Gd*y zw(>BKdx?ARU--uA*PPZ{>zAy*=O6c8TUA~UZYdnM7U;FIii~3o8 zujCwX?ejMc>0dhKz$E?yH}Nyy@70@+*Xf@gI(c&M-4E;AlRCCs%3F4KQ}s8To!2*h zski8jRownu@oY6m{H1OL?7=qNMQ4s0NqZ1xB?U>diAv}CD; zD&Yyr0htG6693EOjM|e&OLXVI_;9{Cm}KfSo>UC}uAVO~;5l_qm17B%**LU4jJ7+N z51Q8YNbBRFaR@84AFFo-11y>rQyaEHsnXT0FQ?o6 z4=?<~Kkk2Y=h8LXcid4q2QmiH3MM;bM-sfP!iuapp&JNGlubcLlf?{*^EHvdiD}M6 zG|y9_0)R1#n}meYPQfy;3%HY5vN(b8G~4qYpZWP~dk1gcTC4x<(N~_@^6a>W7Pjck zQZlhxbJ^CxFYB8wJpWnA?u<0Nf#x~J!sNwR*npuYM=&%C5%Mwy!$aT%usQy`Wi~@Vf{`C> zdEaQIQ{@e47il5wn^s3HixRbcvqYmhw7#)Wa{61pS&tUAe*Hk}{X{R~w^87?ba@HV z<~^x@UGw(Int9Xo&jS2h%?Ic7AM}qUCy(RL{i}2CzRZGi`mWDk{3x5B-q%`k9S=n5 zZdxMv;($T-u*Dif-Wd`nQ0UF%osnS$GvuL+-jYMeu&c!Y7sr4buu4fdm7IjbH4AhM zSbND{Nv4h{FCA!ko5*ph-=3EJf-W)6Iypwd%4LOp-)bVENk=@f&QA4movd6DoHTC2 z{Q+Y_K*q=&T`%q0a72IWiEXX(?>C)j-61XfX@sP~_i;v;R`-}0(4IL1i~?x~aGFbH+AbPVHo1ycZRF-?x8_R~>d2#KTlF{tNc zi-{33DODMz0&D-Ti5a_r-tnwRttk#YcJ_+>-tNtALp~4UEdmx_Cp?r zewYwXWAp=570if*nXBe}m7gbO(=3seKhpB1;3n==Sp!&5l^bM@$)Z`2(G$|=C`364 zou9s}A$q~pWj=}bc-i;HdtkS4c;R3)gpLHh=W2o=nHNTPZ-lk6e8SBr*d#(SES!)! znBh*<)o`le_8&S@b4u+28_#TEoT_C)Nj$s_z5#C)A#$ANxPRQ{d8_o=Qo5xFIxFnk$S5-!(i)QsgxG2!@uO1$F zhXXvA!eK{+T?I&39?J{=x!We~r#{?TCt+@WD zznh%HejahD%HTcF(sTs0j7NkATXN27bfu|H+e7 z{bb{1rvLH8t4H?zzR}wAKU`jv|q(SQs~`o(2F{51228 zj&dEh#k4~sB%vZ`ghC8hASinbo0fq_Mx6gdKdHa6aZSyJ4=!%m{+Q{+3#Wh3e`-A= zy?a8fy49ewe#k_0cEQ7Vz2RNd5~6fYBRa#Uj~2T@XE&6;2%X`-*OIYB)+sR#yHHUQ z2!Ps!^Y0e~XBM!t<-ZbL|7$aMfBc2Z8KM0CmJR4*`q z$9f~24!;7p%WNeuOV|KTQNo92GTU7N-%b;>igp7d5l$@SFY4RBIrZ+PwFi!zd1vFk z!?OVscj$*^wXTw;wH}aKjy|$#f$oM}%!Z992;7s!X0vcdVIcyX6{tw!>Xz*<%a6d<#@T0xA6<(!q_a6JoKEBC z4H?a_xv3cj1V=OIeKHv}(_v9T@QREN?$pSxXGgRN06HiQN||7_Bn{RE=Zk80s|lbR zgYhWQkPl)SMuEX$WcnrjmK3G*d1JRhF;Z}EH!qO^^9Pf<fgodt|A`%(+66PBVEIAe@hbuhe3;pFU{Q7D0Pg{Qq+$ZP{?OQ6tJB1!1 z6a6KFXH}5Hh-=U|jD28Fn*5TxDXNDwgufSdf~X#XSrWWZ^I?slg;0Oyp+BV4TVU-L zP9Kb|2-K1b(5q+|fFz_^J8?#U(~h~Qn)66yv!wn=##K2LeFKCf53Vrrpl`*Pki`MM zC*yNo`b7u6LBC5seV^&Gtv|x5jQLn7bx|bK8Q7R3^b`;s9)B{G!Dh5VCXk=ufkBGFCmzRUmnk?tn9nd9Gh^@6Ulu~VeKa$$~0vXBBcDt5(vj) zM8i%Tv_!a&Z16#Z6&%Q+B#duBG9Jt*Sa&E+BnHBVTyL=+LNZQcg^Y29;~WTIg6v6$ z)vBIZEe>+Kk>nQPOukTYNDdYYGix*?z`&6e_x-ec()P|V1FM@hq*&|DZrOiq%1zT7 zR^E1VW!-E(Kb<|Yc+qpb%kDX=kLe!~hU|@gl$gC(moizoG24uO zBuDz8Wl$Fo;b-Vu#3=-JV<8yGDoy%coyqK^2qQHeA~b_KkdcfIm}5O&ljs1zxLLAX zOH{0an)3)y4^sk!fJ3HBjY`k^ujHfEY(_UX@_O|Gkk1fCT z?9=b+55GRU{3xHeaM?kgbL5K`^^3%V7!Ua<#-p8G5FHR>V51I1?1E_Z6}zA}Vi&|Z z2`Ck@3-%y0i|}ch4ZC0wnXNOkDt2+xW1H6Jubgq)b8|N=8+K>E+xNYEo^N=Ak9i@q zHS~b~&4D}b>zul+|9SA5!5^=Hu0Y4^78Z^p{y;`E;vS>Tf`^u?nF16hfHe@LibYe4 z#S#_kBu3O?;9m>kGcJL+#O>=qB#w239q|Zu5bKl~r0xJSQ9ZMm;~njI#TQY2im^VS z?a>G~p>$Mi|JS~k$JAsqSU!SewzjT zv6fFrusH}7+8eY~&yQ*%RC|-N&TN7%UuUBu#i#mG6Y({LjxvLXATgDsEPvL7;NL`F ziugt~5Q30HaH6VyiiZS#(>!Djbd{)aE)gjz8z?T=UlJ}XgJI*b00m_l^6WpAb{jb+ z`HM4gzxCxkcA)c8f6mCh<0q#l|2_F{f5|?0*WjM|iNxEND7^*_nEA%zKF8Yh>1*d# z|9s+wS`x2JzPwiVDkCw@o#DM2$o|E`Swxpy8{%+?E~RFuWd&h}I7irLNm3TYOq5ta zm_dT4yFkhBVgz0n;nYMhEY0v{^#vy+pf16wB7eZ8<>Nd*Ukf6nWU#SR{4lFwsX@_@ zQ!QxFGL#?HKtr8BBLGtfj#o?}tS-Y}5E4{uI};E%jZh`rfkm#cH7l1UOuoC$k&;x3 zX`&?ranXa>lavdT$4rRADN#${!hrl$OWj&b0>LaxBs{X1G?F4ng+M$vLh7>#MrUyH z-z$MgxC6QG^AUt4{96$RzJph9e{|IBoBNl2b86H`z9jj*E={i#r*9e1f7$WJ^iTAE zzx>I;b$6b4d;YpdR^G`ICzjo?ynp$aQOmAF%-eofzsfUuK&qe8W!Li0AJMak_pv78kYV}^*up`UT~O4BmIM8OY{)NS zae#{O1wy)AZ9G;LVrW}ZlNRw0_S@U?7;&;dZ?M3k?Uq<{1C56Y*3OsT`>#j-x~Mc% zJ}|d)4`tZTCr#6O4xLy$vOAM>UH3}qXiqG#&N9RnMkSPq8e|T^JgGUUK}q?l>AYr! z**%V0;YKT9HxWqov6c&M5=s%+-2$zVv{xS0VPS-(Ktv~4AF_wE##j;RL%Jh}v>~-j zh!%b&072LL_NvO^*2DVu9UtB}xJVjT8X7k+H>(Gv%iGGwKPNu*NP?v}(hWaR}go7ho6UFvdvq==&gF7B?-zgDgDQR44 z!@+0-FX1DP==EmrkIlrNVt$)9LN@2J2IDz(xC>NYGwm|qGo`7GBSyM z=qM;kP*B9GX#N1Pc{XGzg7odMh^k(<<`5y8*_gfcvSN>M8OYERip*bM6dqcPSq!W~ z(^$;t$a(L-Zb`I{e|plQ)iKs_FVCpnlDPfoF7MzGqxY}KxCOJj-*sajSrwS23_G`? za^2FZRrjnLw6JxWR9|^bzw4I1zeA6ui9Tf1%FiCwNzXLoBCf>H!spO~?YLD z(e=*#N3Q&IKlPU|>9F8gFY+7eK7Z}B9zOBM=htuBfneq(+ji+c zqJ5X&2j(bICJJ3-p@EqRM;ZfS#;A}k0*y8bEE6E<0Gr#52{n~G!lapSz-B-tRdRup ziL}6{yX(wx$;ci9iG_xu*qkm|@F6@@uLD|74;AL{?}$s%u1f9%kq%+Itu8I{*lf|y4KARDS2I<&wOz1 z>L>Na$lQ@D(HA$e%f;Lgqols@HR{Y3FiRkw>X#wLUt>_fw z7G-uRqJfEZoF@ISDRcS{4s|T-aq}Z9(ry8VPrb3LV^7@5_t)uI4XHg==zoLIo{nrL z3kONjqPT2JA8Z6%m_JaA?UU^$NI97^}^}OzV(?gYUR=$kKK5-+KJ1M~ZsRS}}Rv z&|7broOj{PC%f;zCU54bl2L<(-#cydlOr}I=ZqiRv#fugaWnco^bGZ9;^k)PW78m( zLXk;c>W>5cp%q`VLMKTn2%D^wp@Y&mPkc5w#f}n$B1RYo@ z=z#pqHW74SwI!#}f=Dt5S)=d^l5SyjX--5bt0u$#;ps1hEEJ$@7>$hp8ANy$dnP)b ztEwpG?iBr-weDU$iYH{w>`=AAT`j*keAJhhs#_0@@7l9Z%36na#qt4TrHSazBG?(G zD8rzi&t+j3`eOv|KsOJ|=oA*LsE!U3c%5uvkf~$=hcTQ4trIfW88L=qDo`?+LZOj1 zOl(}lnV~|;Hp26C4rVHjTD&O`jf~$go(O$-QR0rHd)zzAkFLv{dHBy;5@RdEQ}5jI zL%)U6q1NFO3bw7hxSxOg^T~5n6$lZ7Z2_3Yjkd>NHwWPn3`3aycboTwA8!_<|KHlY z-3cMLuz6P;DNQn0_uzxWt=gB$u+|54y^?#TNt1p)DR_JteAQ;OBZJL{o=T`R0eP?p z(~L)k54FhVAycm5IO9;NqUP@mP%op+{7t?z2Xrk00A&6qOr8wT+5sc)fZG5hh4~xl zWFo6Vm~64~M2z3*9B0UxBI|cw9z-rjaM>ZZ)to<(XP@X~mdt4jd2CoWzwK9S1vly! z-a$@4_Z-wm<_$w8p`pt{3P6+~=LkfM5g$kO#umTn>)slFrC zD|MadBaAOslzXU($cTY}24j}6Qk!Cmb0qq(=LLx_@gILSS{JPms45w;_?p`#78 z-T-);yEHtBU`sI~YCQcA32MniZ~#`B1gQ1#T!^^@*vlZ|`tj@@ss82L5(i~I@l+sV z?j-Nfv^y&Iy7S|Q-{{-A*Bj`BZBDk0ujE4x+_`o5c>Sd{t6iop$9fNYuUF^7?)S}I z(PP4n#}c*?Z%gUAQ#vB_0z_m+Z2e>xr()QuIa2GS^ewv6s{H$sF&|*xJ|o8F9FPLJ z*xVQ!#0fKu4d!x($k^D=lU1cIKFyBN@WE>k9foBsHe(o|Q^YlBL3l2b8xZDHr^rP8 zSUM8*Q-aC(nn_0lImERier!n?)kORhsOz#48<(7t8I01I|3{6Wd>Y*GA`%pq$&AP; z>F#!{E3l{-gfA`xiTiU}K7+_^iV{B*8IBx2Vo9ZESn?gW>~R-3M$br#+5SL6cjvo- z7560$?0EQi+=*f1`G93rZu2!y_8#{BwV8$8x6WPOZSu#1a<*+BSE-*mL;)$BEs@HU zkUSPz^Gg(EuQ4YQnXuxh)`GeOTfCM0hLrk)3@38=CV`I)9{$^^mtPPf+?%Ib^C;*? ziTM$sIqC^>V?7oz4RIyNJAiDVC@@bUVN;*L9ELd>UUHy~B^CD%UOKLH zdO`8p(9G?_?ihC6#BN1vrLw&X(mSQ54Jh8TFyQwm4FXWKZ_M}oC5-@$e5-U#0@n!+-GXzX@V!_8M zPf3v}*fbzTY#NZ_!=?c+z+$FoW;jleJuc+TGv6hNF@jlDNhQgG?G`L4W(+_w9Z&Up z?z8_blKyCyJBaeU#0v<9y8zy%~i=yxm*l0B1 zkBO$|lkXzpQcn;``g#2G>aXq(uIaR*B7ea`_zLb?4qw5V$GTMI+_g;_^&Ic_dcx5o zw$AtUAeTYaj?QV@`YC4{Gk*t`g}iUVI#^?R6VXQxN8~+nA5~spq$R=9UT-opJBVn3 ztD82#(3AvX-n4wu(*$AeX%SmmI4Eb++Mp%E8toE+>6rS0UFk^MRk#x=zAS-`utv*P zS0a&uCbk8^=?UUngg8u#C$Cr>%|}eCa=dGFuWxe+KK{76Ar%Aw+r*%NO^PQ zb$rIjeFJvf^?t!%zBonSnx9x+CcPinn)ls>YCeTeAGhI#zv-u{Kd#u6Qv(2Vga;I- zvJrjHW=oN!N{e$RfL(XKmO=+fGW zYbzHSmvCG4v=z%L8s}`Idki}*Jo8F3WPBoaH3}yXo8=F@^@@oqDSrg%A}~GD$Rm>};XJL*byjZz76Bx=i%d8y$*}7Pj;Q_4Ev5<#?s5Y~ zD8d|polK+z*^paKZ@iFOI8uEg)yu~vIo|1sFahL}=B11Uayi+74OKA_iV=Sdr=$R4 zLRApEkyNMi%z%C&{qwgTtK(lk-}?2R)AYsw?~<;6SW?rI+czB8AWOpKuj5qz9?A9i zPinVLT41{tF*x#hgc)lR@T*yTMADFW$Q48sEUlSaw(#Uj|5h%^)=NKthk9TBLH?6D z9cZa1c8Ch+kmdwh!T+auiHAyr9@rJsa&?UZwbDSXu1<{-_=2eH91L9{VP}xA7cRIA z6;Vs;3gyBcpF^SmPOEN3_zb!0yvZ3^#8jPK@YX};hsa6?|Vwl7&4_a_l`k*Ztj(`hDrQQlS6JYy@xo$1bBNW^vQ!*ZwCuu*B?hg zkvY$QxMJeNb*^ z;{&VFmdnlNc~~#vq16;&uP@X^vb{3~WLcYbcWYjy2 zJ(jR)(5@mma4;cr3VdI0HMU#oiP|KL$g{CtB`w&gTMvu|)AD*?jF8vV*>&8S+7!vS{8&)|NN;l($~v6W@7Lf8y=m z=?7n#Tg$s1tL4``Q>!1QwN2jBUP z{={3q;}g$5t-tqlE$^a6ep&m@zxu2!tgU(S(VW$3-fw80u%oh9jy1gw+e-(sb`YTu zd9J0wC+E!95@-V|v7Xj@4B{Z_0P!;Qc7q~Brx+vfAR&$VZO48qamiq6rX>KPU>_gw zb()Ku%5kx{ffhV+GDhPMgbGbA<%pBZeDuaWQx|8OJB`~i$)R+fIsEz=iz@og8C+5_ zsY?F7x^c~%iLG;%^&GKs^_B|lhJJ&8OMNJ3i{vBni-;M)ZkWhS^l^_R@=?w=j?x>C zEjq++9jE_k{~{?Toi87ah|;R=d}{{ZJy_qVAM6gSA@;kFlMy4{Et;_i6^S$yrqK2l z2og6UP(UK8RcJR0*nYsUw3`L?v5@!OD{Z;2^&8r~;{Rsf3FRt2?uuFT=&x~duEEK< zmZorS9(k;Pm0$+z9HZqyw?y_!$c5Aep_Be6Fu#^VdKSMizmK}tko ze6Z$m65?$!J`{BMML_lh&EC825_6+bo+1x{Jc)&-Kg`(uMN346CG=z`Ij2P&NPV0! zG2)yWl5FcOPBsr4ijYys15vNpWtt1L7Y9Pjgu+NuUTni8f+-T)68IaBueh3&#{LZqa%WfF63|1TPJFWFTh58cDrbmz+;+04ni~)|r znXMqKm;{?Tu>XolY_Mg*RxBcF-f^B5z&Naf)&lsq=(adm{!}}B@Nx7F+pN$QZDcG1 zdxwlP-76cmQ0!E6)0VAUrS)6+J^IS6V%rwZrb#2D?}0gelxyK2UDF0p0s}w-Q4C&1 zM8diNwa9QJvY->8%t2VdYzys#a_h`7(6z~WB(R~UxKkvLHoD^mka55=&Bv`v#&_=5 zd*a;QRl65d&AtBu-;ykinD^z(8RPQ{OUowCS~O+GqgBr@#n`XF4lw^jn=%gc=#i8|R1zGX9WWg_IMpQbd14bW=r&psS_>ZlqF>>OqQw}NO-}YLn_<*a zo#Y3 zDrr&mu%nx988fs`-$9ehZ<|_Qq_}1ezH29j9lF04F=wX`b7n&%1xQ-2L}JcF_8KLc z+rfE|&SED$WA7kd2%}L50WKEF+jC2&7qfnIo$_I7pJ#g|bpK5e=?LT}nFpIt3VSEA zFPAV7TVP8i-xW4P2!-%2XkJ`}Wfu^BymipV;JY{^xI_N(+|rcm5oO}g+YLmQk6(<7~$N4qYTr^ zDNHjKIwf8r(+pJ^`X z6gOA&AO2#l`f)wBfcgn+fPEv6O4sp&@@@y*C?+~#A#v+D_SKnW1@TiJlJxa7yz-E za2{A16kLoXFU|kQXF{d^FWdhhQBUmrbM+QLfQOx6dxs+mEX^hETYhTeZOMV;f|VwA z;?ge3F=ush&JB=}M`W+m1YARL4v^gf00PYj*-bQoBm(0557~`9Hic+1WVhHyN#K~M z>=pr1#s&>W*d&FBn2(IzJ7d`$+P*~_7mXY|W!a|L%an{|uPwa$j+ef@Z|zMtY1Edo7L0y2_$&p>U%g_Noj!sPNI?uzMmJ1X=>i@)DFzKuli0VA znGGKc#YmC3a`1@#6c#=t|6@uBogiX1Qto=>uF;7b6K5`&`pBGVWxEFSn|OBokrR?q zHZZ@4HrpC?`#UQt7R+3CX!3Q{yC%;LWvuPi;|Zl^=+saMTX11FW|6CJS>n!uhmYto z@bU+IOr-w$cJ<5I&bIoV?ds360d4i$EctEEd4o-BtIx4?YO9~u?tZ65YO8-8Jh2w^ z-UMIF1D)mLg)!-T1gV#Tvnsb#bw7$YU2BWf&2ywpffD*=p!N$X#H7AeLNX ze^7jS0yZA-1XsvM<)eYtb2BApTI)AUq@qA;Muk2?|Cn#&OLmSGsn~m3zuIT)HXx}6 z@9xpPlyeUnAP5k&Uo8a%PmcqpnI~wBJ-)@5{7keoa?IP+FK1Q1T7Q-eX;)9QH=Yxr zJv~PnEuNEWJYRn+dcQoNUH$UQ_q5e_ZdZTyazR^t7csWRbLL$x#ox`5AY+WYeaiJi zG!o_dk!i(&L?@A$<3k1)<1sRsVUoxUgE5UuHLPv4okAq>PncM0TGZ(9`SK{-)q!#I zA)Fo`7%wRnCKZet8C)FLAJ*Z+*Hk*n&q^}3yTZ1w#^4#l2fdomC*~Mgr9z&7Ut|z= z;n-w1n)pu!w_*N0Tia#->jyRth;9IID<2WiPe_~gNPF9M0+2T8c4ISu)=PWD7>b-t zF(0}M8oz@I(+tY9(F3X5&rXX6$dHbOj7f!wu$Gq3^Ufa&@Rdc!o{SMko!8+a);Wz=7s z#!K~=aR9#pVXwH1nJjkBk{P z5P?ZZCeb*7l5E#;yCu<^Dzem^(2GUlY!JS3jlmp@X!qkhPIjiy=1!^wYJ+h9_2*fI zkyL%y5ur4QJ^Zo}?e3#JPt1~s)CWY#WUtsXkMcifJTh$k;@#Z}C<*k9yVlO?B7Ht_ z2Qom{-+UjEK?ByK_k1lf#a z%*43$jEq~kp!YB9mm^h;#_gB&XW6-S^)x=lbLO#Aq8ku1Ra zhPAsb7ZP(RiZbeB;zS~33}%!JwD^jH>XmkdeU1{5B6{{oB(XEqF}*x2~Td+$GN zI&qo3_&gF-^)noas&_0TjK+Yq%MlyipFJ1Z{~-XF-B;i=c;bqniBLpsX}+3+WL3ay zgt<`kQ4(Nw5+c4xDG8w(oBzZDV_(}Q?VluZAtJ`j4O%bQ=t~;l7&4diqCW7pGl8Mq zFkppK1D#37>gYP|w0ZLlvtr1|Rg?OnxBW}hREiX*RLgV`_Z*u9l?MWZqBoIM7r{XQ zVL;f?AeC&|ahNQUd0Zr3Vl8jm1IY;Yyv+I*TszjslXCv}^1=FRMsHp?HGh8S!;{ZU z4sGnW=$?eU0p)``^_yJQFLUmM6?doUfA4&7-QXhqWa89?rp!0a-d9yJ77mH@+9EzW`%d?VXkTk?>lNX&$GNfe~IWLu(PF;^!Qb!xM6RBUaz zZ_FLpF#{K5ta*3EPQ6b5ZMIUizIe^DLx<)+);VKwigm%`Q&(?3He%4!=U3c({S0-c z^pfL`{~FSN*=auMw|w?*rcXV#lJ_d4mFb+e6j9Ja$&o#zht`~fPm7DltUE8?H-?Xb=UU;ScY`gli zl*1Z%&I=|Qf8#mx{$SK2?wdD@`(GCC9jMO|_ftIhU)$9$zuc{@{?&H%XLXo1@qD`f zjCj8BoO${Oc!5MQUCJVJC#*_&l=DszJX%L^N{+FudK?1(@&%U!z$F1=r->A}^t*vC ztT1F8R~(qPWW!@xPDfbum^H<6)gTv>LTG}+D!Vw**GVitUXkW2hPEv!VdBPbcFlbt zXdXU7Z|V8>0cBH{58+Dbx8rZvrvF%f;_YKg7T$7r_WDH&4vgo^4sh@On{SX@_q;QC z_x$y}ikFQYvSaRsB|mR()o*|Gofqy{ciY9q%Qr9iozZWlOw6~@Vtn2RMCpyVQ$eRO z#<*d}Ib$5Iu3wG->R+orOJ;8|Zr>_};`zpN-Y{ROPZ#ty>gW9h^_aKS>=JBlM{wqV zUWEOC5xH-6L_=FhFLF`XJOX@`^N69Muzl5AK<;P5LY%;R7$#8664#YeSdCj>y|u7+ zpB_DWM?PRgzEA(_s=j@C_3tPB%lI_f(D!n^Ql{+0s@MTu&D)rf1V(EJg}iI2BpTq? z1QtoeDuHOvP8e*8&LY2NBCtr9dl8W@T$UEM2wsJm4LdahY=8{{v|SKG&@g2GaUScC zld{1Oghp&)BMd?#LI6aZnqs52U%?`M7Yy3@&pkWlRnA?}Z_7VxcFe4(a7pH(iuupX zoKiAt&Qnw4%p2z%e3_3@2d%wf?es@q(2u@&Z53~x)K%J$zXKTxw|3perNd)`G)^Mc zPmFiDG2Rat9L5DN;Mm=Dpz0O;*d2jzoPUL(Rn1*U(j0>+6d&8T$Dd4T-O;GHbg36 zSg}`MyZlK+VMyk-JQuOUDt6nm{4zdSJu2EM2e{NCI*tp-WSAsOA7#7uzJz*3V{w?W9@@lTZijyJe5_(8`KE6rwql ziNNnzR>iKGldmrN=`K=^U|`3(o1(SM{t zYa$?@kUkbsf#`kh6l&UZON56PdjzT%Te-J z$R$@ccJX1;Gt#uN5jbt^;!!pw8r!-=@WwIVjR0g;Py98oFk*D^cQZnikAO%#17%=S z!W?`pDyYwQrVuUiNtD374c5-Y`T<-)+i|$y5p@~ zX}7^IKyDa13h6F}jxz5u>=LklZi6lUd)VR~EQf6I*jcX494~UY>TSj!wn4@b-1y|( zAUmsJjz_j%LNF`aY~kDPU*X%|>E>(ZlvLh5ABOo2YqMwEGa$9w))hk#!;p=4TrjVN z{^|pIL25YyXON{J{1#CQW~_nyU?jj_@2SThSczIN-AIeoY{L6QAz<(c2>4Fkr-&gf zYQPjcmwo%xS<6?>n6J<5x}>70Z{MQAe!`E&%ajk5d=nDD z;NPKO8VQPvy`TmGXknTi*%X>Y8Zap0 zhBpi``uJ-RQZO?Q1!O7f^>pqHL;I!XhEiq@c%t)lLkIeELy5|VT>7)cr*S~B|Yo^gTLt@G4xAb zBl;BwBqUly^Uvs<8Vd$=V5%6cF?!YMs?PCS${MNFpf>AlcCny3mTJJV$H+gBN%Or zCK;1xVw;ds<2c5#iSG70=ic3*c}y}XJ3r_AXV1NR@A?08?>XoH&-Y9IJH9fuHSy(; zx%&fmzD7u&WjX1^$dltWi#vEsixG%J$etYV&(Z>0z-l|}%5{Whxbw4f>*&m1Y%b{w zzF+l&GYbS_Bk>tSfhQRSX2KMyC@CPnW zeq+g)J4{#rTmVBc4h_t(SR|rUILdqD&Oju^-ZVDS!E&VL z+zB9CEH|ey2S4D6!sidWeD*5_UVx1r3JRidYkB1oMCrcr8kS$TSLE9)P>(W{c}ESu^M@ zH~dP4y-E{0~`Dj=$;U8fan4RRXhy-|RsWT&&{^_ZYcDQN4v zbJdAwdnQ4XCYgFl(1fMPmazjWv3q<*U?OxIzC{tow>UWQ(W}XZG%KGBm++oYT81uCF^zSRh-v5) zWo+3@twg8z9w?1e*fj&aWEipra}~k%kyH(5L%4kxKVUZ|SAPfWhVXH$qpK^cd9>rM z;yJa6v0ubr{?z{5W0js_PkB}RC*BkdltalK>>hlGqX1-hu!ny6s@oAdmK=HBGID)V z{c48Q@mhCuhh^Dnc^rrB5YB0+0iLNGHGsXhn2wFtbWFg0(;u&4J+T>hw*DCPx>KJ3 zVE-+B@}t&IyaOiXht)_&-<8#gCbi@+)Ke`z{D0A@(h$E=m6ZB*<|itaHSHV4!gJb^mDbwAx!V~zVK7^MF$jM&ov>g^V)CL4ctF+!k3hS;R60n)P&+Wr={LFugSRH zxC~>hAdvQl-bWY3DKtx*;+rQgrgQjituW--q*+^rkO6sR7!(#9cQk9qs z8s&XOZbkVCs#1DswqYmeHQI)K2~|Apz;QBVN;m1Cp@DYEb=0SvrjO)I-!rm8eR7HK ziZ~54Kp!AoERUiBDbrUjHvxaFo$BPBG+yfDX`OTx*Sp@AAd9~BtQ;dXkk>1HN@I~9 zt9(V{S-ITC>igb7zD}~^_y;LSe1>YkH%q-#hw)LX^tSJS)ax5Bbs%~yVrR(>Vv=~m zcL4XlOly1JRpc+q4ZcC7-5|7$m?V9I^EU~{ly2H2?uBphA*yEIv9@Pr7>#~{-{UgD zJf_pK`yM6B%im4y%y<6Djs}cv`U!=l^=McphF4HPd8T{~nB`x;dur;_UcyaLN;Acb3A(ukS zLJx%nh0P7y8+Luf;t^-V()R?H6C^hPW zIo>?Q{Dk>c^Z95ox<2~2CDO9S@>xtv?9A9JaYb=E{O&Nj_<(DsJyAGQx|pWCk6Mf(c-=n zxsWrIb0b&H?aMuydnWfn?ojRxr|dL26P=r#&p3BERcD{`sPl}=;@axk;p%qncO7=U z>0au7)ZLxu%sZ8LK0iFaI{(@4`5ekWpZ`^Xxge)tR>6vb&Vr)_H$0;~vpjY9Jmz`c zbHH<;Fu8DB;mx9uB1=))*xBRQqC0!$Uxp3J0L&U0PjmrfVZ|7UO8na(;Qk^f2cu>S zNXrY@bI_vaMcDcu*YnK6vQ^K^WTl;Y-T*JkLwa7JWIC(okHRbC8mzmGKyYouUav-a z6fw9K;>d;Yj$RJ3(gMsE9s>G73$N9HQijiFg};as5xbn^&~q;A^|4YVYB%9*OK=`U z^+vu0N3;Lze2vI0#Vq?Clr2KeO826CDdu2P`L8WOjRipFU4*^<^}O!#@2G3Nt#%b^ zx8MxgxvjXS9DZH?-|+u}tTDp(sVlV$Znh&4nKR^@P>1EnRpV6;r z3vI&g$v@IA__%JQ_h|!s0%gHK9fCqn&};OeV1$P52CCgPp#FE!pXnI&!>(Tk9oPyD zJb_j`O2_FfSdj+kBn_hV-iELBY5EH^>INx}8p5AE`^{d42G_KGJ->oF50? zqw|G&TBN7`^G(utmwsNCE2O^hf!3Bq3+FFu(7tj{*3tq!EzFlIT3eb}VTFD@lgqU0 zLCUWODZd`1`9v=XzIIjUpy_JR%xYNlw#@07tk|nlRnwg5s(BVN>*ge>ioLd)0MJ&U zO7?Pi=2Y~`r$r#Ufe8f@woYe4z=T%oOfZ;`Vx0*B6YSI(BbcBxol(FT=j)6Cj1sLg zBdiq%Nfykc^Wpe%M3lw=@l3xOGpjWc`YOURYVyOlq$t!gZwj!HV59B{un}M*@mkU= qo4`h-1lVYPwza-*&SpV{=r;ltV!&Ez0&FbUm@Gd#M>Qj^(fS~~XQgVdti8Ls zldh`ns& z;6DLcQhWw$i~1P0DxfmiU0G1yB2NhR3lesLI41A@<+q}AD{=}%1!OfY<_aX{%Gm| z0ALe@M*0#n1E-%@=-wX<cAcZ3X12MO8cK_jiVu7Uq0FdVDQ9%i7JEI?+8tTt^mw&K*Y%-Z_ zW8nUiOa0;}j_^MKBLhR&8rYZs02)8-1AP4GM#<6z0`2XboB;sMpSA&={p38NMMXZc zcQpBl)q42R4gK(n&qf3~KXsrV0LCKh82>9LG@-x#r-T7*4N(!F@yp~)Hej& z?f^@mGsFP_;H`lOenjr;Rez<-wN)@jzL`BHQ`dv_F$6%121`Sz2LaiIdcF zlthYJ&9ThwNdo8+rHy?2gB#&WJjAnY@G%ld;ZCK}(A(|(EDh_abw)K>9p^%B-L5Aa zBkB|c==i3yn2)os-+iAqooWOVp%=^JlC`v+Z7Z_BUmBhksNV}e52*g+eBCSr!&9d) zYU-u%J?&(M$!1y0QJ&?19Z=&oDum?kfRXUc@@&C8hQ4v&hwK|5;0OD`d2!H(>~nP8 zC|wP7CUz#)rs!ctgnE)dkkpN8yo5F&-S~|V_lQi3qRa^!lcwu8i^;*8-V^!^{>E=Z zSP=6ykhNowdkVMV&G(7Ff#MsbkCqjt2+Vv3883T`mdq2ZDT6N06YWs$xGJ;dDf2Y< zAAU?WoCTfwu+6h6vMJyJ70XTXXwSt-w1@Z9Pkj}|Ydhkwrv#HC)TD#z45)1Ys~tBQ2BzHiM!0}OL!3gM0-q}{Eytcup!xqk!oaW!4&y#6pMGpDKZ`ph-_vp2Zpl{^0a^)LxquO30k#YP*z{`ns$SS zZAwZ`l3gQ-lMT`<+JLQUSkBx6aSekhobIT4q!Cp&nIo8Tq8^%!i@coWhBY0tD*)U8 zG88MP`ES1_u9X8CxLfy0+>Q!tQRaK`{9o8MY)94u%h9FyiaF^0*ER z737lG@3TB0Y@%Ulnb}DX7@J=y)vy}EzEB3@ZTDa9SCb3FEpmOrZlEBs@^HF zlXz)dOm4NP=re_vj`yS)uL`&yEV-tScJ#58;pSWU;ZOIND6A2}RcXc@T4dpXq( zypw$BY`0k69(&ch*MF@*f1)AoNs+~2xt59#Q7=tUFZ}ZSqoKQk;|t{=78yv~|x@FY8RPMLp?k?wGTN{-LOQe&tLh zhwdbwu1v2}jx|>R?iTN+N|dFXF~&+TDSf`tl%?XDwZwz==1X_p<};Vz&t8_TiyoiS zVWjWedJYq5(8f<+_CI}{;f=wKk-me&J(NMbWHrsWfxHC0kc*fMaz*Ecx+F~wfZD?X> zMQBH8NoY&xH)vI8I_O(yGH5H*KlltErmx=wrAh$?K=NXU+7Lm3{(-@Pu*zX%k?_Oc z-`{*+ekb39-yz>V-;5tV@6S^~NeHpr>@T~$L0t&0JgX1-he2Qn>-eZVwcUQw|H#gz zO06V-qpe}>ZK9?51EF+)7%0`%*H$^$+gd%`UthpLLxM$whX%<=iwaGQ4-e2#|0Js@ zFDW38`qbx_Qtwz$tFnna}B%J@g+U)YMq8R8akjv)t z{5WG3I=u9PrRACG()mOof0D@>W-yuFTnmfMqBPcL*9BXI1Y9yDmH&)=Uq7QVs5n?N zAQd14umHFKd;o!fC_o$_36Kfc0+a)Se!O%*KA`xgUJGajOaS}fF&%(vk;541aEH$1`?q<<-T;_x>{?9hZG{cvoa9zY7V*W8k zRCK+MgqDrj^RV5gC$7(w zOj1I@L95h^f`g=Cg0dWL^?X$Fyote#c8T-Ttp-&A4hO6ZrBe&I-IJzD8(LuuzDQy> zTlB+;@VxbTY@)%dvFgzJzzOjRD&2X*PV`YgZHr&bh-CU6Zxa&aO5a_e@dj(z+J_%Gq2aNyEG`r@}^ zw!h~zT?SRQVF=djSR3ll|CugoP{5;E|A6bMyNd#@Pd7_e%?=rQ>2b z9fpC3hQ2ekma^dQx^^b-eP`}h!Xyt}OWwAfrg@Ite!w!oRDu=y+cF(1c8+mKI-E$l zd(eu6sJ|~p7KbQ*o1Q5p-94c0{*{$Sps1H1f*D@RjzC2rUj65%TegzGtLey5m(Ph9)+!L=wXfu16_ zy(4{nAm}PVwNGTuJD6MeFSQkw(b>cBbbb^glZV#m{*(6I0+(^gP=0(9t#>gL?>rA%q@74fAq*Ln zPQCZ1Dp6|g>Z|dEVK^;_Z0$1k`^f$1DOlTtvlTJAS?)v6?k8TmHOQxWeauSvGZHbp z6mUBzvT@zaAR}!|0f+XCfpBUW>3_2o>=*nDHJIX874}@*|LOS`d^_vD%^dgoy(s#@ zNMup@84+!%{$5VRcY$jXw)@LhMQnlo>TP4xrAC%l zlRl1oLh%}P0R1~-`)O{KMSyGXgW?b)iaV@ytQn1Vm$TnJYvATaN zd}njdm;^-9tz=HZ@-3FeIJ5Vh*@5%NdG(7GBroNC!lanDX2ldfYC|BAJMOj%T;w$2 zLM5YC=9q?#if#fEa}A>g-t<8ujh^gE#;Q1Ggj|r$IH*Vtt*~{rJ_BX?MZSMWGi4Si zE?8lynS&qlY#UE|rZ!zxFPki3cOq`s!li?HL;>n{d=i)O!!MxVDxj|tb!j%M%V*55Ta9t|VYLvl^4gYj+FK7K*mg$HvU5Yufv zFO%a{RP9`8)(o^=zN7V~hPSEGyB(n%-uqfpE>j+>&#vS_RQjIGBdPMhSF6>fXPze{ zL_p6pac^`g1Qtvk?RV(wbCnNT)`SFCWX1EeGt5E2%&vf-DHaew=rWSEK<&7UsOcDOeUCwX$|9D;2aQ8g;aOEdwiNEGu~d4Ln|Uh>Sy|yr{OGQ&Agv_6)aM z`wyKhp)^!o{otv<`r%7`&hLYp$)o%0<;s_V(1GJ@i&Va= zUc=Yl&pRt&#?iKwbX``9nMKAIv2v!LmJ@w$&#ckvs;uo%ot}LYJCTm(d00kT>Bq|{ zJ3@s(Dy-J-Ly3JPH60 zyo|U8PCjbF>VEA^d_eRrzSELqqcx&3Ma?$RYK$}4!f_w!6}t12OB9ABkb|X8b=W_1 zfhwFbff4BV;UOZki*$GH%(N2k_GikTbmMkP;L-g`QCSe|Jqo@?*6T;lD@6Pk@+-=y zhj-YQ!K)pa;FTuIyVZ_>xDH1TnP35$g;IZAKna#BZNF9tRgmgD3PSp|0Q?w31)hcy z-*tqCCkkBdw5;+HV7;zMof3_pJZ@aQQFl(T>kHxCa5voY!M{3TN*+OFCMdc&7)X_W zBHjW;o+B+Xn}~=+8+agmk&Kw2xLblk6Jab2OV$R4NN5v3Zc@Te9*%H5VfZJ;oE};K zyY3eP@&t_WSJE9{|6W-EGddkd)5%Bv-@^kXL$ZVl$Lx=~O!t_!+nEB2Zep<>3Xm^i zxu7-_zR9kD@hiu8oz$#RFx21?@KVhC6jHi?NXDjKF0JQ$ux4OSG#7m@4gq5=*}rsF zP5COpbG4x>c?JhAm!Sy>ESwB}MCg8{&9OJmH7?f%fW%kaCu9bbK{?s$>s8<9la+5uUCh^c z4-Y;sN*gW?b_=7+vyBpZIC$-PHBawqsINigvi)rw z()v&`Z4%~C4(T4w>AZ5JG%wEnPKp7YI{eF|ylRZ!~v{U9@Dt73zblY0W?_z2O?6P5M)*6pG zf_g5UYQ^T1;wrUzURMiHj?q%+Hyo{3PzcPhxo4KyGWDWU^#Ga(E1lqu|MasB1i_PG zeFz5?tE`x6YccWT+XiotB+O_)-H#ulLZuS%m9wS({Q0c9{#9>?DEw{Z>{VM zNG=n=kxd=QFYPwcu_>&d8s-Z-(5mx6y^@e;#3O4i+U?Rkf)yxA0+{uZ?8M~68{pp1 zHRKwGkV;kPaA?>4tY&9C+GpTMaCaP7&*-_~jDS@a_kQ`%+Tz@xAvI4UmeggGmtS8R-t2c zAaS9ydP3+Grc>eAGL-_zuX-%BGEmm!y=cJlpL+REN<%7>K5e09VHjY@5*a2-dodTu zmbHQCB`8}}d8T!J*0vZC)AJ-Rs*QPkDcyWwv?G7VcP;I+6x3#!v=Zwi=db^%DK~9PhNm za{hid-Olu^)!J=aMOA~A&=hlG##0W|fNonL;pE!pg6kSiAvH2C){AEvh8kR>*#C7t zz#M6O9NkqdX}rb0o*vHO1BK&7GiO78a6x`y<4H}E9t~2K0x8G3_3+*74M|oWyMlCg zOhgw?1{voP>#NE2>DR$atF00!klNLiO5oi~l7;$-xE4Pbd>e%uyp zT3O-4PV2B5Y#I3}?o%AV`GV^Memkk*LIakTl5>H9;`CVbEbA3O5tr8|J>}gVFhFUk z8N>&3fxy|>L0+)fXgA5&ElLZ7oWZW``r+PxLCAI7H@klHexAj8m50ev=fD3kB+bnE zTgRw_$=J7JtZZf?mNaLq%DWvv5H}p7wD*fzH}ahujT29aj}|)b<#$1jXzCy7U-UNw z17?h|#CuGLE0VTu8Bw31TS_9l8&r15au=fMO9@JyVejd~zKLjyV90&%WHAZD4U4U4 zx%?ObelDBY=S!RTj+P7L^)+=0YZ(qXDvHL0Hx9qn#52M+w%RjbiKJY|x2dW9-fg|d zF2P=z89642Z)g@Qud#?7i8{&y7Cvbnt#@aVamQBYR%{|w4H;Q*{ zD2DRx*hoddWZX@GhDCziNdr-oCqQC`8B**?E{*DSB-;i!)0JRT5X`!F2O>pXBGswK zbM{r?Zyo@ZBXK@|Vh-&+p$~S`Dr%moFcy(zrvC*-Z)riiTIit1RBLUJ5Ix7ujDHSq2J(7b+Y0@9{Iau&L!ui2AG;1^|#xUZGtbJymrx;+Jb*wW>=lKW+n z^ejiJ79N`UWzq6+d0YE25=0?;;d>(CA!S&yd=AEnAad4CF-hj}speSW?P0wIS|1*r z=}d7dq&^e)+kqf(^$vIy8P8q!mrCK-Z2%^n?fe;*ArLG|d?Wfp0p!IJr93*SYGY-) zA~MMM@Q)MY9~a~A*AL$57;O$}-D?RuY)&Ib?8&MKQyn}o;=!?ZDF3-)k2NBT3pL)~ zGb(=EFs&;o ztaS7k6)TkpRcl^81|%8vWlHte>sb4(t5xUcwY` zBW3zbrmQEuDNixkHOP~QOgW6u?~Ok4ppA;qRO;{L%arWN@>J$4JZ-ed$l0 zBv3P(fkSy=9H-z&K9 zdo;f~imPJkZ#~Z9(c6tpeVYg_5y7b8QXZyBGFNuzTn?T}T@wyT=p6$jq7$cvilkfc zdr+*(VA-;8y&`C>KF6dvtHKave!W|765WG!DsJr6YyGReWZ(mKQrX0}_IJ<=9)%7WU~2sr7wED+oRIv zSHbuOWPA&YY1P@U)q*?IK&UnzDsJ=eLH~mzl6)8zVa_a7;2RF1tmWcm6=zMl8_djI zMpDi`BXQukzO-QcpoKrFi&u=Tm*Gs9y*T7?fz2LhHcA05Ud~{JI>xsQ{z66V=iFsq zg-3!euXyvD#|L&#y6kmsSIV!<^c+3U1^0JouPCa7=y!KLoNil5D(<@5w?kGvGh`)C zjbL^Z+mu_ZQl;7#Y7R}ZcF?8mh`64#+0*vJrMw3(kUWTrdzR$AL*((`wTd*G^}u`f z632yex-r;iwS%2QFb^k1&T8kpk{C&dN76LvbX|x?kJ!rT?n4eFu}%iDc50cUzdb($ zZdO6q-Hh=%3jWo4U#(5q+&5dMZ#37z_~H*o+WeVwsS{x_UC{ee^3n#EE5sna4bCo(+S0!bxur-=9L)`zeP#CHo9P)fBQO@dD=!|-bFtFIc zJ;~wm(WqB5mzA1%><72Jt-hi6V1p1$gYMQ2XVkbz1lWeCu$4D1b^8&hOYFPmj&f?j zX6i1F!E}HAckBwH_Wra0yFX+m)^VI#B$2w{CD7{c&~3^KVjz&)!`e2^@!GA6vM8HJ zROtgf7#4G(0uox69!In8Bx#J3_A0JD)ADN7>1w24G`^LVCycCrST3K1#3W*j8 z#&nWQB_zb0Ds$lXr&r=kBImvE7{Xc;V}DuIIRBM~4nrYpl1{s;2$7i2e2-Eo=BnKQ zqk*F~7bI`jLcn^Gi1#Q#Ze+1>PxV_tRyzbka6?=Ra<5F1vBQdz9r4FyO9vk|F2e^g z`j@x`I{u^W%UA{R8k6!KD;yN_#sybyWh^|&;exwxHB6?**Ls(aY^JTfiq}n{?<}_H zX3Mk-gkuZT@g14ju?Qww3Dv1lF-9;iWD?+?o?}S7 zTn?7*)t?`mFW@^np)5)gU1Px1{A2b9tCTH~5 zoS1sh5(-ik@VcYKu>x=c-7m$J;6<6nKH379;RiO>_1qFXOUe{Ztcz-Nrn{DJ(ujQT zy*oJ3FQxm8u;-8cIi(q14-=3KnTNttt=AQRu3z$qh&XRu&*vJB@pg%wpR4RP$`Ia% zC-#C1gOu{6DeNGFSW=&GRgpjl0fQCEg=c|(ht7QKMuIrR_yCTu7z{uS0x}1xMxPz7 zn4QJYQjFw;n^BkGIDEuqfbLU(m#C-C_i;Nkj@NGUF1q-2NlEP>XG69DBV%Yi1Ul<5 zD0!NVJg7~GLnm2VfwP1-i}^JAlHZJr-H$q}>*OvV0`{J~BB7nK8WdY^c%UZ(a{7cn z)8$(%f5Pwm?<(KxJb7vI&dT}?%y`)00n(i=Q?$zHH+kX}WZ$-EfY*Z!{NzIs6H=oC?=jYcz$&Oyn@hpvh#Ku7o^M-1@(#BIml~}eW zhhFo5q@$hp#5>^mfT1B(o}%0jXj)NiD`s9PUoqmZ9*ClpLprys{}3hocI6^Trq3pZ6H)urHHAxoT&-%sTF0d;Ff*0_zqR z^XdNO3P=b#z(dlwkr11}gWwN@WTcH!OpwD0)11?Oq$p1vPG6_6LFyABS>&UnFHXO@UhWT?SVt z>5uf;`?9*%w0bsPdU;z^v$t5vM`Bd#jDx$_kSf>E*rb>GH6|ZR5ejW=0kRY)xXi>b zP9`!6g9Ev|0&Om!xVCEkfp|M*Bnz3|J!uCGD>dJco$b7@Q^T}ott9|=q$B3gxupTz z`P4OOO1Rf!Sjq*4RR`?TIg8z62h2t31`!62kcNMVG3Qud(6a&a{8TY);ymg?)E_MC zA{etV5+EGQ>L?bo_@+k=XD5sLk(@5E+OkOMQhVb)2*w z6YxE4<`LV@8B;&cZdKm>ypuCLKX;8fu0KA2s-KPxHnkA!aPBJ3RYNxcF8z9iEK>?i zt)9Rjj#w@Om=PlP!#EC{ydaZ7hSagj4MqnQVlzo z{o57tAVlJS&pI)^{2I zUPx1rj|cxc3;^bnbM@(7vlp`KdDQQDH2=6cG%G}~ORIydhM5B1i|(qP0n8p#iy>kI z)Nb9LT!E>5sHkrPAaZfA`UPY_f19X-r59gMzaSCxk?U=lbkq=|*5qs>VvRpzDa*W0 z$wMW)LY2p)1&YM%dyv-qth5++0F-_#5-((C8Rtf7$KO z)*3%FbIopD+j$DaU<$_$76{ri*}RD{@Ea0NZKJ{7cT%&Sj?=v-YD-WF>(8uBJ$Jv?hd6VhNzzq2#S6OzR z^dSY4D|u2{fAC}}6d0;X3nMa9Z7SXnHH0L~OQ?rD6bl0My|6TJOt*w71TmICT|Bwg zsG&^p2!(}hMpwk#c_sSeW;lMDJ1`m0wAx-2UhNQ}%u;cVo9yQN8cQzSe0(KBj?Jc< z^PSFurs}4foQpep+6Ql%gY8>k!HTT*$J@ofp(tH)4=%GBPU>%bMri_dG5K@Qj^v?e zH6FkASW*`}MR2-4jK>yvMZ}AA0Y-gg?9Z-R(O>$Z38~=JEFc*137{~u>3Fg+j^J{@ znyM@Rf*DpyqWYM$1qqlg>~jk{4CZgkaGq^mR<U(aS2TdW$VRbPka zOkifZ?G=3QE;aV!;-O$H;p0CB^t#FYVwoU6c4Gm2dwYSkfN_wZIv%=IL;?)wat8r1 zZ|zz$8oE*w6|K{LD78z%bThWf`g1SRF`lr#xl+96CtN@Qw>$0EgCd?mf{m^M)j*6wO}%!aXS%1GeH zrebVAr5zmiQN#w4cFNLzdmX_zjUGrD52vl*>&cqxd_Ec4JZ6Y>dxyNZ(rdr{dKEI! z(MBn9LRYF%<+ObFsoYKdj*HS>2i{{clK(#F%N+-fO(Azb``=+*e~cSd$z`bW)m&-bSBz-0{Js_Kx;O{}$>CxanFJi^NmD|c&MzTC zL5Zg@(YBg|5dG>}5lat@;7DagS_CoxSFAn`NXjW8)x{X70yj`EcT8VG$TES5>^vDT z;M;uvOq@YVphk_M;Zwad!i!ezY+r3ke01@l@mCG%@+!KH{B4scxs0i%WHp`yRiRi^Y`vOX?Gk;ZZs&N703$} zg0mdOz?Lm8Htn@;Hw6$E&un@KX(iJOVP2%LaIv~GD6N6 zNAnDnqYD+Gx=jz|1uM#(p}W`^4f9|kGKgKu+2HVc#Fcbtusv+l_Xijx$#JnPWU#g^ z62-#US^~J0E#(IIzc3npankmV84R6I{F za*AF1oUK+aIkj-BYx-zD)ypK^yVAVxQE{l8dggo&8MK0PHZe#^ZUzo`X6#8ROx^qH^UtZ}p9og+x} zJ9{{Ii!^k#*3iH3ujb$p=+NHfYdiA&qDrY2)`9#pML1fFVZ1PYp<=RNK7@^k-e7?i z8OUI0PK%u$i(G#?0$fDSk0ex)7+(rmLN%$Oa7E(|hUuad&nc}wEodU$pW|FNi0$&B zJ_#e=2_<2qdKd}PneVR7NhcnPSBBuauW{bt$UEk9n88!fG|i^&#FIhlW@TkORdEfHqWx`G`mb+Dl3ZK^?N9d608bDU_y<^r%iOv|}+4Ow*~KN1f8nbwnA# zkzoVF;sieXdK_%7tB-O@w!!t=Ikt|qm&NT_yyzp17FIzM*V2(yKgI5nplMgJFihAzrZAPC)F=>`qvo<*d zh$UEf;9N#{U{U}eE>228-eiAr?nXE_)tL8?ik+?bvqFLe$>X_BHKP;K#fk&X8BirZ}WIKHJk8P(cq_BoK)w`vpmj~#D-FIw>kPQN!O)I zf8mw!GT&zxsQJn8UXxd6J`dmPWfN+|&1gJkdTVTPX|q21g;j7uwvlQ82+vO82#{oQ zeQ+gF`2s|5Rb=NTi&J-*6GIg23i_wZ@zteA!=$sfdWdJsN{@NuXyv*vNWI6iwkkVl ze(`$qvr%&@Aoz~)r^->7f}`At+}Bg77Og$6v3MzL4RMM*N^oG-G*d#+aBetkQ&M^u zr4A*6qwM=!&RIs0py(mx&z+h3y4s@Ro;f<1H?GSBEfEU82|!RUZ}XX-g4|Dkp1qbt zdQEBbT3qdhJj+tQJZv3%UYsj37zx`C_U6UO`W&i> z+x2J&Ajdm_lNh^{fKV;j(>k$9+yz>jr@TcF^JB{$_<*$04ul2lA&-EbPc*5J4QA8$ z>DgUxH5^=3Vx2a@J#k)b<&ku^>#n%kYq#k7B%N1HA5M&q)S~F~MD-G6Pa**IIKZRS z=XJ`jAbH;-mS9$v##Mu4jh|w6Ap@;YMXLIL(w6qwQ#}3yYW82m)HfQ4vM*_k&jh;V z-d>RAW8I5!$F(c?x*>jUCh54?7$NW=v>MLqVQHUI$)kF<>ria>=UHWYxayz!#k(WM z+vd9GZKFaGL+hVaJFvNLxP9@Bc;Eeg#`CQ*xp4)(YQf!hX0MUoTuo61*(9NNE3}TE za%j1iIxgGk8aVC_x9USXNfML>;Y+;-qXsbgYbcnwM^O7^xD_ks+0yM)_#-t+}U z+QOkVVX1_H=p??ZJ9+>Y;x?X>9pr+G)8n(0-e4`WQ8$nj)gDa-?)uS$auGa6lCAzk zZMv3=uno(ytcX1_(!2Ie1TM>tE$RBg&J^u^_nBa1yo$x){%IfO9ZqYAcL#eXys=_K z5FgJCoV^3#T$44vgGzo7OKgsV!<8-@d1ysi;l25KjrA?i4IU*BB`lqgj0ufjX0}aW zb__lpxGUmtuk{HLTTO-Kdve22Aaq_KE69tGw&?vGA5d?e!|`e0E4X2Xb{AJL@=2AG z_0-_~K!h>*E>+19=z#;cEUUBcvaI9yq&NCUY}P@AoPA`FVPXf}!CGa*eJ)=9Z+My4 z^V6AjTq)8!^4=;vP5bCNTG7TF^gZbcVuvH&6o69Kpf}k@YKkE#>qUAnDUh`<&3q7+ zl*v&m(nV;Z+mj5smt10*ZKl_V=@k$$xL&3(3Eafv5=WepWasCR0yzWz{^4 zI)fGd*b9~;wf?JXX~-*H%B>9;a1tCdYlC(L{M-Os#huBrMY$)kz8Vd8xIQU|!QT)< z4AE|5yVXveYl+F3hgKyYQzVkBWI$a`c#6C{LR}&|TPJx)XdyRbBK&(_!NhCG(Cw7B zAkSq$f8G>Vkh4Yo_`q7pDIkR)RLlO(KdZ-4eF{PC=rQe8UUi$JUNt_& zV+6c)X1BW%K!vXu`p5Sy$7#+-Mr3-5((uAFh3Sd7RM=!}n}ZprwAqTM{!duWN{~>C z<3nK`EyRwc+erQyHg5qNOstR63&sAk3VJds$(av1kOZ$tw zfgMD(^VvOCP3!6&=Jf1#ryO&)T91AfRpg9SO!?U}bM2WZzBQkZ8VDWB@Z65(x@sLS zP8EHuO=w(GxiSE|+SeTE+{|WM*Z81H&@dO+&H-~`Nvr@9wiL$X4&Hk-drhu$~?m8ZPve=jf%g52jqpEM+(;&& zAkh~xAxQ>|QbJMkN4G;a;z;OVkKK*u6#F<_r7^r7{b{!QN~G3%O6+8L-QV5T9bB#L z2{1ZM=z(pylM8;cx6aI3WVC>t>ib6*;Z5{-)$|ai87d6SriYxz)=-x~s`LHZFKa=s zM{tI@#$L~A0YLj`*vypYeFlYH>nQG+4+~aCunJdda&Fp}ubr#YV@M`FWSs#HbiOEquk#l2E+8O&Et%PyRHFK3as{>=- zO19(Vt7xN?O;3JNe9~!=s+~<0BGrn_$CHPr(_mpzBX09~>+X3_jeuso&U$(LpOvZd zqjr=1ty&Md+vKJKhsj&$oKuvmu}jx`^;H4P8>_6B+f{C8yAO!kGoH#^LB85b0`qt= z-WjX=TJ~{Uv_R5Cdo|wbbN2RF1=ioriaJN6DTwF<2SyRt#4V&=$w~l%uST9H#+3ZL zJ8`}!#|jcR3oX>^96s{2Z4cS?6E)@>#mX_oz31z7FOG~#pZ_>MNQbMcFQ5d+&buND z=7|`nGu_ZM%kgK=anqbMzfED8Spb$Cs@EBbO!xG|a@c4~@ZWGK$v85BGU$nxg>kC* zck-@z+cJ!3@*^Xiq$IgV6SwQKMb<~yb2>atRNGL@b^O@zHZjV!pPKJezt>&B?;~82typ{#9bIm6J@mM-6kXD3 zzRsuK`fjI@MK*5x?~}K?vA(}M^*OsaSqTW8-&U_{PS*)gW_s~_?*A~yP?mbQpT2tD zswp{}pW90A)rvTKN{&&)*eT4GJEvCr#;4^u75rO*8-b1J+G>H&no4BgL)hWRtH}!l z6QtK!g30k)6&Q$;y7 ztr_7^XueJ}hi)_aD5&(Tq4n(!YF?GJPgI!6nP=!MSgl5?#Hh@V&PVR-Vh=M%wGM1r zMgOgv9T!G$I9VjvAA3W(J1mHTPYf|Sta+w2yk8itSGa>}Ylc`$6>wD@-8cl{$^WEl zB^4cTj#H%(<5*)+x%kB6TJy5=khRM->hOD2 zL~chiv4c8sQw0Ocg|-*VWeU_fNo_S*MObfiUM!<)lXPXP_XrI&KmQD<$7H~64X+Rv zr}qJH>AZO%eR$9Hi(K{s85QNy5P#fDZMTItA+H8??Tds1B1f1iW{_J^zBz;?=7A6K zzDkep{vf%dd-HWWKf+ErjrHF*^`4Da>-#bAnlj=2@Y?~;~<3jE{C*?NjPBM)V>9Rd}qIe&34GQu|T?Xh2B&~tk?R_5O=d@D8MrKrbx(wzu)S*w>Ut8<%33rTAewqKxDX;;p`k@f{^tPE zXJ>G*JT3Y>pvKi%HzuL1PQtVlMSwblfk zmIQ8^npN?QW<*nJuINuFh7t%XT5}xGpugY%ZO%hw1uuzus0RjxvOrg_(t3FzcyKLo zt9methj2q7F^OhPF&r!P1pdzUGW>i!rfMgP94M!^#j8v`F0=EJLdFO@w&x10g^|~& zacYW5Z5EHWg{B&s5Dwa~tQWadC3Mx&4tv>NURYLp23BdK1?pF_bSXtn>o8{l=|S-n zuq#?$DuvvCj-q>=k&Me5q=C1zLCA~9VBGja_f()CrSO}SI}TlJ`OIq6m;u)lRv3Da zGWK$5&5ijpm1AW0Ihx+zUT7rg2#fVmXQ8-j_SYwUKJRduEMMJJGDDpFp?qA-~;gdXHgN!jyU;iv&&xPXd$qpU9M2EB>b$IFiqDj&VG z%d^{a!w00BOk+B}UX&F&WYAyUtiNj2!WISes z;lW<2-q4>vHxElEkXi~ey^m;U=fk9z4Wn9{x2%xjC2ZMb%7c79)jWGq*+s8c{|>gV zAf-7E<-Udf{X^nz#EOEhvND@5fG)Ygh(yz~YTYnLMK&k5B%lJu+#@;Z$42y>Ik(@s0}XlI z(|mmgYn{C+DSssTf1Hzz z0ItD2han2CK{P{kw^q?ax6<9nm86*N=3_V}s=K2#{&Bh+eYi2Zr`N2N_AFYzXn4is znhmpR_>7vj7Cf}}^&d9X-*uO?<``fu9`co;FJ==l{j1^TlwK3$@><4EN>y5 zNZ!#!5qbGrq?%L}$_p~&g@ecdvc6EhElu8}@P9SKXhv(Ag@`U&cLU&_3jx$oiiW@)6l*h(5>-@3tcbKWM@qAr&YndX|?lW#7uh8M#YvmM)y;+1;|>#fa}_HsWZYuguB zb?tR{4aDp4E$5DKN4#z4XRtzQ+xJV@VsBeUl{`%Tgsdk^wvVBu*t^>PyhrIaz>13O z?vkBYa}PAgp)^><`sg^V)uuG=tn%a<((D)G%b40*j-O=)#f@jj##P$W+TBs~{auyk z%jcrwTzTC1vKtS_#yiK2pS@8S8_%Q9SH|bwC?lhRk3-bDeawg0tDNj%XvJz)3v)c? zNE1U$9EM>M)gD-u6eZe*?G)6)KgzzntzwjY9?Cu|@XaGg9Q*)&v1Sg_lkf^AH=Jno zz^^HILo#=5jH~{a_pdLHHUL;i zhI!>?cEc`qcdQctyFoT8%>df2@1i`UHJfxl^icK#(NmhyI!H>(LWTBB^)1Jj5$`R> z&oUpzjr(Kc?=WA-jc3Qk=Q8gBPY83E^x%Hrmop~pg=ioi1JM~sE;(F9%E*A}@+ne2 zO@38wnM(Tc&gpUsDVr*vl3$xj%H%U}QTl2wzj0L4tmSEEj6Ae;6dw6tYhsbMl!iO6 z3&xhDL{CIa6sYRMU`9R$3pk8^duYee<3^p^nc`K>{UsAEob_Q1E#t;?65DZN;e_No zsw=_t(Br@UB&SF%)i3d2hbP*@FIVSqdI*gcJJg(#+~phQVkzSEr-rOsyt8{D7Ja1M(1OI`;Gu~pN>ANO%R#5^XKU9_krkJUoLUW#>6FlQ=VCP!qjc=+$Lb zngp0EuGsc*S=7{VOyvvQs!H`p(f$uVl~2g08wORazx=01w;s|o-(X&Q<*gdf{8kb2`E{j8J&^< z3U``EFV+bBK3A$6@x8ISfdU0R&tr6cN9b0^KJG}inlZRSeAgU^`(m*b`_M5ugET)R9Tue=kDbX zrOMxRKJY+Av3$ZkWq~H^owJ*2gVXPsbLYTbsQ`TU#KQI@F3}Q@ieRj) zuqo*1jA5N1O2`1EwNnBOQ2GG5W+^x}OUZ{9oDpdt2BDGFeCHx$b(SsEDZ3Ew`q`Ux z7N-mNNp+D;N=ZdsRDk(Osn|I3I0I@bJ0Ek~i-LgR^g5x?baOJ8?iWQA^dwQ&9@C3Y z;LC2Wt=QPQY4qA$(}4LIt1m9!E{EmcU$_40UxWJBoFWr{LuUPY+LWW)4n4fI`F!QN?cTty+ecP5UVL!c*&~aW zyhOU}n!V};`HypJ7WONDRpEo|#9AKU!ypB3klHAJxF*WyA&Oi)5aS#7ecbr6qW1B# zapPy-Zy$e^@?ROB`#WX44{Z2v>HE*9cw=UCoc&we__7<_W8-hejh~f&6B|EGpRbJ1 zl|P~}pa~#fi?rEbl?t%t9W!{OP9Q1yN?Ub{pNM`cC0>vcuhMBE*&d;7p7mav{Wx!h zecPEvu=6`%V@7?lt^<=x&>__1mVj62_GDA@L!*1`4H;=x-Gueu@0jzLPdjv&+}i8A z^2#a81`)pOhjAk}%U7G4FC1O6;NC;C)-9U9e;ldVPn`Si8Ohonet*)=dFy(Y)QlOl zZBE0IYg^jnHE+KE>e>hH`+V`TN0z*&tedZ-`)!o6-@H+J|G=kFI*nHNw)@uO%YwHa zKl^?AcoBWRGX9P>?){YB%J|&BD*LvMxz2m|!!(aS3Ht$?{gT!iO>IHH$Yxa%eECaa zLLsK@i_StkevWp!>lyQ4Z0)yo+@7{K7Z&yD+p}jM^#|LyX_Nf7mHqnm?qACOOZlnH zq2G-rzLMV#RxzE)W$t5?A~3WeG{ZQ>sGMs87I8ahwr94QorUL`ZtPx1oGw%W*|xCG zPLo$ft36~uumNJ=#dblAGl;tXI5Atf#9S4P7)((#qEv(_hnloe+byt2zxe~V|8v*2 zxz%%)mp1-$@3t9LRW?>zTs7~-8IyxEXFos1qFq1xz!@?^99TcHe%jNo%12(it(JT{ zu^Zdq-zLlQ!fu-hduWUg`9X92DF4PQ{C-T~)6L_{s&74h))+TV`KpZ1W$3t)%SGQm zQF+eiN?%2qKdf~E?(4+-no-IaQ=(;zUYa^AdIB^T6gHX189{&^hdMIUy2hMX(Znb5 z$@!jW#lb5|#es)bz|gB3uOYa0s5=n(Ak#!xH^(a(Ga=nG`1bOx}^ zOWB&zp?;Dh0akl+{iIfRbAGaTcuHr7VWv2x5mS__cH|oMwpsBR%6di~d|+U0wUVVg zYkc((IXON_IZ9hfbA`5ZABxo+kPG~foBAlR|4ARE_zD=c?gfiou*fCWb48b@xd3~_ zi@vmtX1fAfcSKu1Yt{B{N=PW3k2D!A4z#RBK2}5N_Rwm{4!$(@-bocO#+j0Q6~Fs| zI@RER8Lb9dEn>jT2Qa4CcGzIFSW{BtTrirDk4Qh(EiTTX)<*k^lsMYEitM?IeJg`{ z*BNa;EMbc>+a`~wRNISG6#q1QNqc({kLqbVmChm#euG=>E27|yqkuQOptE{0yO~fj z(mNx7^$22ip@K3D0VyATEiP>G3&}_ezfn2f23lkRBLG)mI}V#YBHEPmZL?E3&hDHA z>&oDRG-pwpsvKe)oXLKrzEtwiGAn1YlGiBM!i*<13z0>>A8d}fvo_Ffb22@uP<8+n znQ>nrV7L7OPVBn=II-S+`-SC=PslIH@8|KW>u-Ow^1wm4b!^3j)h9kWx@LiNZuye= z$EI=#_P_j4c3bW1WY=jjLi%Fji6&Py6E=0aMZpJV#J$=FyFiA@znDK$M_`@eQcg>WU%T`RExvaWR zuRvK@phvmp*yNhUQ>HInQdQiqUvW_>Jx3#z{Kvds^C|4F3I>yD&~Y&qhIOnR+ZUr= zEL2^wCYLfb7p*{UGAUE@U{aV1zv#z8lfsBSP?*jluLGI}3wfSo=`LDShWn*7|^by)xjDcwzA|0#L%lP+?@Dr!Gqty~>l zYeMYVrhvL&QBMzdqUSd1N(04oqFi*vKLl%1Uo}o{imiMt$sX zs^6@k5enQw2eoj7^N{dZ4dk8V9$HF0BTI>k=;H@?&qF|9%_V`KFUo*3A8 zJGJOGDj(ySMQk^kf8l20WXsSlTOE@z*z7d2W|SO~*Y%)(?_rjZZ|S*6dhG8tvA>rz z46Sd#R@u?RlFmmL{Z(G^7P*7e7RtA$%GisX$VoupAC&LZ@R9)h&iC7U?M$?{3Cm1m zhq0e&PBBJCU^c3}0#`E~j4 zeo^6aPT-s~*#T`AYR;XjA>~6mEdsjr2TXfe^DdKv9ow>q5{^`oI|UxY%~?jJpD1I% zDxW5;h7Py_k`@LWg|zclHvQ6;6bvyHQY)X5jdc+;>pCq<=^DV;#EhUMVAiIUW^G!O zP;zmJuOwh+Vm$*eXOCV4r9pER+mG~5*{gT&PI+n?sd(@1O@k^IEqnB<+sTn+d9cew zYSZN1ByV*?-R|E-$dz@KV;YEwB}PAwQ(IItwwlo}#qg5+WguHd^8o($drjEoM1rRL@wTXWH~2 zTcD@cQUYL>u+i+m98cAS!vk7k3#4Gipsi;KwnX!}UAC1h|Mp7qU{mwyBP)6BTT@@v z@|qFV)or`kIq>gd+Xrm7Ha|Hww0qmfY;MmAc{IvA{KpUG2cxXoyf!aC_}a_(kNPAj zfSV6!GlU#wD8(GZ*>$mTF>ZV$jx!vN>$+((n10M{%y6=X36&zoyDbm`pq(#v3t)MQ zSXe0b%e-V0{Xj1GwR<6LN)b-X>0gFlfmfmjXq;CJL=#IoG^h}>0@SXj7Gl+D1;!wU zS+V41|$K5DFbMIhhHqcB;5v*bekac2NIUSZ8`qI;KYbD;@15wdeQzCCrekg zrc;<*B#22ZQWqG_Zo!ifN$GrfQ~QfvLfF&G4L3zXII$=RZn_BJoG#h$hCICDcrhJ^ zcb4>`Eizux7~M(PU5a|est(EorB_B`Qg%*Wm!jg5UbpzBJn6Pl5QNmMOzI5xr#qpn z9JWZsFx(=xVX!I6F>EFd{y(?U@Rr=Vvqq8fcCYdq=~%`8by@d8<8RS+gZwG6+kpdl zi#l~1-tX>-sfphwfA^RCrym$x+^=}x*yJze45cZD)*m?>xsP@M$SIba-Ah+@?9egq ztwZjF`m|YVuvf<$hg}=pOV;qcY1zGj^v|!=XZPgSW~q{)La`SS@K(Qgp4#%4dD?He$vo9<)(H~!8t=mb_9Ty%`;70B@H^trl9c~to{$c{ zQHwH)M>gSSq-0XEfxOf(Xl!zMO1Eo&V1nZb_ZyQMmx|{5lc*-i z`b7+y9W3yD$?hW`wyQII6jktzo6E8y)h&9 zoa>i@BUi_kX`*32U|#36_ZqYJaL(^Vp72aLvdXgzOT(XpQ)si_jbP7bOeZkrE$Xa~ zg_kIkX*Ysrw&<^;zTbh{^qcC1`p{40Lm%`iedEJz^eog>^`f813wp-BA?o`bIHF#t z5B>Cf_zyi__~*lIw2u``sTcjI7wVa+uHjBmbr;OneU*Y&QZotatbGRzECV-c%0Tdy zO!dGewL*Q)IlT{mv8NaxXkt9bqD(5$effkksn`X-YaQAyj-=L_8O)PdFw}jhYtzFl z_niv6yd#eR+2%h;24X($<9`4rMg9o@c-muNU@(Bf9EMqpB}@g(D$E<0FEIaM(O~gm z>0{Zza*pK>D;H}N>ju_;Y-wx{*ge?yu|MKS;JCo4z!}Dw#o5ETfJ=ny3AYpXJ{}I9 zIG%Mp-*~fluko?)`SESyd&b|x|4zU~V38n;V3FVhAt|9cp##Di!coE-gdd1ViFk-~ zh#V5RBg!P2CVEOtNUTomg?N!ffW!w$3(0v>JW>u)Po!g{*T~4oG|8NjHIkhr`$Nt~ zZk60Ec{BMG1qlTsg)53tK-j0Gr?f^{MR|?#Ih72RWvULUi&T%OzER^++oE<(T}nMe zeUpZmhJ(g6O({(k%^1x!n%}gfw6wIWw7j%-X&uwLq;*g0mDV?HChZFC7VQbzbF^1z zZ_z%WeMb9+_7j~nog$q&oi3eeI*WAH>0HqD(~Z(i(=F1i)9upzrnfS&&&05H^`D zGuvji&0NlWn)x3KE(Y`S6N9PKnL{-35qKhO7=54{(v~iL=jzk08ilF zOJtoBcoGkg>Yg4^2*Vrdy{@YF13WXKNTKusq>2m|s4C0w17-Fe7pZXYxP;;8@guMN zc>F|-v&X+Ix$w@MsbZzr5i^!@OsG5*8jVmG@2PA#Q)-lnSzZN?8Fj>gLo2fl1LKCq>xG) z>12>e7TM&GOCI?YP)HHQl+cXkw4f!eXiXd1(vJ3Ypd+2=Oc%P+4GkRw7j8V1;-!po zy3>Q6^rAO?=!*|O{iq;7kp2u{AcGjp5QZ|0;f!D;qZrK?#xjoaOkg6Dn9LNWGL7lX zpi*ocW<49Y&R#yTk)3Q~A4fSNF>K{M>)0c9iDiexv6)+Z6bJh`#xENA%^^;3pSzr7 zCbQT@6%VQA9uIiRBOddF&*t@B@Qml2qK2RB<~1*QMJ-?X&K719W)Ab1OC1NOX94qx z&_I-hEMhTVSi&-vvYeHy;4%kU%_`QgmT%?(MWYwWkapiGJ=*KrPpl_ zL?iW0ML_Kn^jrIw8cLVa{TD|ipsxS`c-lqIy%NDt5Cz~|iTwGE+@LW+XKv#KbdpBF zs1l>`5?UIS;aN5+;{o)Zh=X-?`|X}NvsdvvkT@ALau-=4#5pfgTV(@k<};E@4EwxC zwpwQfS#d(9no>1RF4m)@KBxfIJ;Mho8N&no3Bv`vF~b4eA;Sio4WkU$5u+9-H^UOr zVApZ1gKMZeyC`tlY`#{2$w@9S4IXz-kzzyA~O zCo^XGyx-sPM3$KE$Xa`6j{-PaY*`=*V(cVcR%p10vWeSUPt^x1P<`?D~gg)K0iZ)eP!IOEKi zIKX1Dg3s^AcL-YS?CR;cbbV_?)RoBfU*Z!R-o;Z5P=TJjf>(S~AO^jUVkK-GQ+oFu zSi%y=FPL=?OS@~<#JgGEJ!9ugW8KBGtf4`Cm%jJEyXSXLoHdP^@gTKMeGv+B)Nr#Z zS{u#IurquE--5rZ(nx79%2(3grM1~i;xMJTtS z6yyD1TuX2rg6mL}VJOSc&T^E8P)bo&psYlB3hnL0v(KQ-T`13@RG>VEvK!@jd{&9_ z0?Hng7g4HE_M;p?If!xy3~vz zF?4FEW`%gRGfG#K)u`Wu@;FKv%4U=;DCH;MEx7MQV-S4fEJ@r z(s17nC5uO)w`%lOEqW`&tU9CUJyoNpYV=f%o~qGPHF~N>PmL{CiwclLs^FVLnwz)Ucs1-puCE56y-IHiD3CUKC8yqK0;|+#TY=Q7LcjN+E)Xn zgmJZiWi4P?3s}|ymbHK-VOllT9q`6C_Tn4m_(pjITlQl8Yq3jeu|sOHLuvuXYQV7? zaI6L#s{zMaz_Au^tOXpa0moXvu^Mo!1srPuN9uVmdftnk_oC;$=y@-C-V3~`1s2z0 zchzEd)na$mVt3VIch!RCR0C$!fLS$QRt=a{17_8LSv6o*4VYB}X4QaMHDFc^m{kL2 z)qq(wU{(#7RRd<#fLSeIRtuQb0%o;5no1EnK6h9{|61Sw zX-x=r3MfM@C_^oFZ!LCjEsI7wL@)H(|K2mT0#6&C#`m!sqfkCZxr9PBi%_!&HH%QQ zh(XFii_Ni&FpIE?u;Tw_^=YM(_3=T#i$b{`{x)y@{|lU>UOz@(mryL2o!)aJS0Fk} zpAv>&o6WGmS! zwwkSBYuUr>5w?MCVvn;jwwY~V;HPXG+s>YZ9I%7!WY4f&>{(X9o@15l1-6I1$il3O z?PmwTzYnw5*fDmTodi#>W*@POnB^zzQ}!8VK=R!{jHJ1I*ILMT*+RbSBqTDD=}6kf zzdyaB4`ro0e+)2ThH^s?LS3?cmCv(D{0FmiP0RS4N&oE0e0ShGZRm@+Hw?a!q2f2i zI7yqtm}#~Y?_>Na`b_C>IDm^!$cd2aNwxgR_)4}bpr=Q}=M z@%gOJyIel?S+(!)hQCL~OymB)_{WVT9_#-&$;wok|HFr}VlbM_mZ<0$t1Z^Ov}ee)jvSGt8Z$#Fd=+~4tr|HpNo8oplo@I*s(Ln6nKPXZUtvy`YRU91rcPc* z`#x1py(!>Rm9zmWD;lyYy#V>1 z#lym&DL0Pq73MyUi|QM%CQNK0Mv4|anJQ`l7rbNY0L&IsAi zku4`=wb$%(+M;b4p_rtakR9DxZ4PJ1EEN{AP_*Q5s@B5xxdk19d2t@MF_4nx3d(^k z_jT^kv#>*N=egjNsjK?->e;V{VbgDyh#Hm36_Og6XJ^cl#-jwQWE(Rp8LBZ)%u~J) zlF+?m51G)RQJ-hXgx*42LC5WLsTYn?dbrIL-v4mh$%X^<-%IxT@2L-P6XieXBZ(#R z+gLabunc<=5`)2D*o6MV=I9vQS1}%EvS#d)94%V5_69@DQnSzPjZbRr&8spf;v?DK zDw#eq;3K0Y%8HM;3fxS*5K3^@s0sS`%297gRyttXCZJucLG z?4fAX#<*)jex9LrOg!BE#J^@UPe#JYDPdZlN);u(A^UVIB}CN;LxqOqNp^vu3ijZY?1z1-6- z$uvp3gP=gXAo~yh)Sih1`h363j}nwc@%UvwO29>>03QW;a&PTBo>9EEnCF(PDB))9 zaxb2wy;HKfL_1%+s%R(A>7||LdzbQ(CA>--NM(t(XQ@`q_fkPU?F|_+_i81^=){gn zhbBChsjYI{+#ZxAQ=iq0jOz(b}zfqZbWZ74m+2IUtt}9NCsHx8GS;)~dY!aG2l> zY;r@bG0X54WJC{3fP|RKwnnfwK;)pUxhCu-{FU(%a+zwXEU^LEnh~;?YpPo2h})KC zP>MW3DS&Efj6LMQWR18r+Cz!BO~Y-PJ(P)CA8;{ES2V3>ds?*Wdw*cc34%W%+Cg3n$QS zZ%Ob9SZl&j1gc;ZfYMO_#TQ$%&*UTe*3Mg_`s|_h0Du*@R(r^eTMy8q0N3_FkNr_{ z%RrhPQ&+7HHMLN6JHrldBBrl;9BKl<6AU<<2bjs~O-#$eV*)A#*%I(ahmDDM=LJE@ z!~^Lz4%#RHRlsc$VN!SC8Np@d+NvSlTEG6%&cmy=^Ua-Z^zQDxnrHm&<#|7qRsUnp z6H9-1O1pXb$jqhvZ(lm`uA#%H^F_R+-u3xY;$6{k0lCKW%iVdrD(H?7^Vv0TdK z7XI$u<67QN-)kLTt>mZlHoKwCn8r4V3y3zO!Qig78H{r}?2;+a(V5>t>b7%Z`4by< ztlhNUaNs2^SNj!zop&GMZ+-t0e^+mFsI)|}8Qo-waupclpq%P8xJ=PJ-8Hl&Uzu?t zgFl+6E&pYYx2+W_U#rcXejr&pJc>`%%0}_NnCoQrrP5Y8i9J{hnGiBiFvLM` z40&M5L~mG>B_qrk-Ex^oPfU&)bwQQ#LKf^SB`<7=qR%WQJQii4+bCuMis?|w_dCF= zJ$^^PF`1XI<=eCgYo$ll@I6||8f_@A)aTJd`-R^N+qxMv0Arw)vJkVjh?O#c4@Ut- z4Km17AhcJPUIw;TNSI(o8@(h1vrTt)W6o zA7VVCN=~Bo8<#)d?1UZ--P`Ji%60K|yBn}Dkm^ekkNiL`rSacg9PVA-9)Ji@0y zisWi`Kk3R(SAPEIe`^1;yKGm*`~xNVK~Bo6atB(b5!L*NcPr*VlWh0A(6$Ti!9(R0Y`@d8#f){%OBop z_H_6_S<5r74p5xS=6JIAh;}DHc1Z;fNPwQLOMzacCLBv-%o2@RC+4We3n6E0P1tE9 z>~bam(Tq;IH4>sF0ntKU2Grn%s>QL-7-Mw`(iF_+-9ed%;+WEP`UJw{@p~rlrRt1M zk3Mk6GZTirdF9=|Z@Zu!lfGZamxs4JHhAvpu7hUoem}fc`~E%cw7Fc2aTL}j31duW zlUO*FRt79N98Y6(MAuYBSyJP{m7-{o+KAz%V3BOT3yx3{#%)WY!P+bsY+D+vk}B9Q z1{>ljpsi8PP^)BI9geWulO&jpV9Y*;-5?P)%oa@14t$J`ER5t(V~Jhl11Ex>0Pq6hdxwI6(Uc(_ zP%mD5s{XR%k21G!$BWj>m+IPX)>K}!nSZ^mYD{GB!1k-O1pXzm`*ofdQm{L$v<7yi zrpjeWuwo5dgfdB>Vi!Vj*c~l&$+O3)t2>0iY0b8&F~%M+vzVg4X~e!IpqMa;$R<}!oqIC ztR!HTkax^cHDLoy&lpovW%Ij9@UdAiy;MO5yg;$&Jk@ItwF1B$*nerbw(>%LG0QeL zF>!|@WHit`{7I-~F^*`RXWN-S&+Ea)kmO)wV+2xIfg8eL1leqSE+54E&%EK5@!$Ot z6Ep3~8(;qV!I#>9DvLIbdUX4S^&`rLOEdXP`~}y#1nm>;)SfGEf2IA#hrE1Oc=EpI z*H;W!rtcfzRtDD02s#`#G|~*hC$5@uLT1_xoG?TdxZ(Hk03Rj4Q(t*V8f9p?we;#a zLrap2Atx$r1)Neql^Y?I2mqN0K$#@&%tQ!5%n^5 z_r{sOuhc-KAQn})oy z=)lEy4&Arn;e}&2_v`tDw07h}y}GU$IdT4dfS<&sV%|g>z2J+OwcrUh%sQG7C8m)% z1VDm3NfyEn4?!YLKq4M%}=Arm%3Iwl+sXosR>Ky8v8`z$uuBjz0-{4g=6 zBQk4-2|GPN@$~#S@DNwvnlMK~TsA~I<5br0-s6|=tDiilchRKp{~aAuaA4MnuXa7Q z@s6#-iZ_m4w@tqM6*q6yJ~>@i;a-;rA`~2a`?r62?AZYi_MH-*bcp6Bc&#!4JkJAL z@HK7=N>gQvDC}`G8xWlc&sFV>cOfs(hcs~=g=t|N(8~2CYGT0i7By$X@+&J>3T`Yh zk~vDzw;6P=BU9};Rnd_i1(~a|3KqXlwpt)NlL$g|jYJS=)6k-RCx*yP{Wb|CZRexo z&fS{HCCcBZ@4m!KC$k?Rb6v!~V4#Q|3-?TxkJtSu+oUby_~pm7N3=tm2|n}raV0_i z5<9>Ix+f%=D9BtnAp@1rwyGo+TU^R=91ZSiYHA^&HZh)ZjB*WNNhnvU*jP|74)Va@FS?foKUu?AU3yMPAzIs0?4=Ayx*58aqob!r?Pw1LFj1I6LO0+V=;(0vw8SW` zg!Jt8!7gG)Wd}jkatl?LLv3Fe>edNrlbPA$VgniYSWR%KEok0$XKR0+;`F%1{z})k zS$+q|ZE;@N;}+CdY8^;XBoEQ-0*^7^V;uEKoWz<17x=@RRr5gyhwYd=bY8T%&Eu2G zp07RDdrzN)(mQ5t(*7B`tR2|Pd-0sVd~xh2?Qw0olz;M^)7F2;vW-#~uJE$U`?Y-+ zAFW+B`L^4~y`#R(8WQ5Pc5&hAJ^Q(Rzjhz*p`hkz+vP9*7ASPOmD znyPrO=r*2YFuz!|STM-=JT=xHa$%986Km8Kf-6AXNnl9VlGLP;-f)#ySs-T}b{ zXz6pR1`HO8iw~R~wh+bPRsyMhojJl3ByKYJS-u?%OIg2`-=h6|Mcc!3L;DWC0?9eP z>Phw3Rl|Y3N0vRG5LKvs^7^OiS8rJL;A1maEV>taaxv)oIl<4mMYw(}WL|}MnT6oy z6%&X_tO>;v_+s4{pOd89Ff%!5<0wwmNC?!y**cg9s}0zpUTYug9`j*SutJ>4jr7w8Np^^=^YOiTE+F!NP1&dcz-Fy&S^d}0_L}!=VLkPJ zuk~p*2%IugpHbK86Y6lJLs&Y&imr9o)Ezpjui0FW06OXm>u%^KXzYCv88#MBZd~^& zmpefr8J7BM(TDgM8RGe}A*rse{_TwGX(T$8TA$z07}@^Zk=rmG-Fg z$yfaL{X@%2v=iFb+DF>E0e)gjePL=EU#qX5Az7?n4;_xMBnVKd23WhJ1SI8as~3&c zlk(JPJ4u09MkbIt{j&4brr$10=P+sA=8!Z1g8Hf3tNH z)*T>+40(r&ZsDO8&sGXWo7SOWjJAZsfDZdk77H7(?bPU;kVJ!nna6M;WX3ni%oCEL z0ZeP5YSbB(3DQutcVjvu=3n~Bzk9X+2U)|u#4jeteOv(W?# zCD@3*IsgQ!3K=*ggY*y%1s!Bl3~&>MENwnpUnPw@Q~%voKp;cwAW)Mu9+pL0A?$L^c3x;9U@SN|=h)u@|m>MJleoenw0JQ4H{!;5{C; z!hb*;5G%-4w`k7=&Cey)Oe`4jeHV$8u@H$=vtyrRu#zaPnsg~s7w^MzG}I}bqJjbF zR)GMf7>M&Y&cY>h&dlG}Ds5&hX0>KF+YASO8=$}{BJbUK^y-fWd-(@-qZ-F(Ae){G zwkk13)fyRL^z}w)g+%hl5r!liMrw5eQnXu0`v9UuAkTwo>1p{LXoVw#J0`a@OD4lp zC+hxcwqsnKTWpxs!)cTI!HDtJM^$z3Vu60rzCQX#+YNTM(BnLWR}tF{m{6nb2K2MZ zc0)@avE49iVv-D)U*DfT*4%cpn2+JTVU!uCZP#8Wl3rE+!@qv{`Nx0NOPBdXzFM21 z?a-dqrt!6Wl2)y~!}EAM*l}{Tcf>deFA480ka>GG+chC4IH+0Qqh{iu5Q!obHY$%O zz76Y4c1p;=EaZ&Brcm_d^no+_0|^nukyzp)skL@q`+CpTFWx+Q7ED_k`q@A0k4xt_ zJiY!=0V{2nfK@Cc&A}{eB}{WQ!Ybh!>9Ye?c3tK&0#>BH#ld&KPmZ=)2*a=?qX0CQ z6@#&mNEP}$=*S4oVfnr;_`Z0TkNjEtvf$4^_>J~(FRhFZkgf+v!7stT53m^MEq6pP z(X3-)$OBMDTLrm`CQu6fB@_d4XU%J*|M2KV2GAzeD~f0YZHO#uxGK!DApy2_`U-{Ys1@u2q2G?Q?`&ZbDN9 zIy1y}aGZVI=JwHNsBi%vgw={9%ge+b?P7 zKR%eh|Am!XI+d>e`bF*A@3p#|U1^z1XAc}(+~KA(Pd|UAXw$$scMTkSTW;}@vg03# z`8a{YBLI67;)cS;<~9YGq=YjS=0XF=oFIClG^Z z;uN%z4_BT!@$#8U?JwFd_`58b<-K)%55E4!A^A|DrJy`$4Zo`4PTc0W4`Lw(TW@WSq zj$|5$HWLuxBz#4xBPf~;bF&RzVPRp#X6M*%T;J)j*l#ealF#;d!}qO)<&4?Ygkv^5 zp-RUNpLp*d-Ko}~I$%(99}8LHel@6OeDKUa&f|k<)otJBbUWa4bA>UKYhMsp791@<_>#P!ZpjlTAAMUc1%G+}hEwi` zw%1WG7Gz(hlmSlB(0_H_f*nnUP{I9T5EwwtB^_tRGmXNz3aON7i={`Y=<(gQ;nKWh??*9k5xFMW0+9M%W-# zi6*b#w9j==UmPfVm@nrm4r%dwAP}TVpVTd_KP%;+?P83dc%uatvIzff)G3766_rE& zcos5Y1~8R}6v9_hB48(n1nA*@PqA#Sx67~9{UWz2Rkmy`{cTD_HmHE_YC76B(Voyn za=3`#STT^fRm5pjnS@V;{6g4#jqhZ5iO?zS30)4a;AgeV+^-!mUVY@M|51$F3#I_# z1w?1GoZe@oK!q-;aTe5$@H){iWOE@V%jg%DLl)`RbOD>5EJJYTki(Z_UvgOB?TrJ# zv-$wM&;lUm`h)2FG6qp_z;OAIUtxfuwa=B(m4M;6u*cAbvLMneJf2_LqCKpIrF6Nx zZjw|}kC8%ltkd3*%NzP*yk4ft7>cEaitsJz(xwShRpck@#vNMDIA5(?l$}OD)&;&T zlL$nyl5YzN1xM5e3`=5JBwA>W<>2{25KB)W8()3PH!E}64Pj%9r_cWUgbv4uf5I0b zzoU%6er}BzudtJ_$V+6ZB@BaB*z-fCsG2G%#Y^UJ$)rc8pKxfa6TgRJB_X6e;Q=u=&Sg*$?ilM*p2n6n$R1VXwTH6TbL8URvv2;nF~I|7Fg%5);seh7su)Dp(?DkE61 z0ZdI7#oZ>dkn3KwwDf=p=H5mdxlye!o9BG>(LaBl={u-%jYlrxm_Abjalh=+OHZFZ1(+)ZXvc!z)*Q1MiE9>p$^hG2w1247Lg z149+G6@eB;2TYlS7SRrDO&h52YRz>AjLPfn8p4;k>7%dgLZ8$Nya`+BS4 z<=vqdhi#w!FYOyu>h|!mhqg;ehz;nZ{qWh8<7WnJO7rtJe8KkTbU!FLI&8*dBF zVHd50jTXQG10~B-9iVN7ny`c_P!8Sx>&a1F!V&MpT3GYKP8Z?7(++{hCH$ipSw zi-n@Vci}oGG1ElrkY4}?mTovQVh%zUYWX@Jd;8psF}+8P;9jk!LOy+S*KT}Mpfq{$ zntn^`3gy$-i!w{s+)6HB-449q2IO^x5u%(ycGR$g#@oXPEXfPUihG3VfKYAZ6-8DVKP=7d_|(#am`hSl-n z@my~=Je~^8?ncyf%D}wjLbVl%&uTa40Y_qNN}KE+B$Vbl1o6tx23<=fJBZ&wk0!!D z-U{!!I6{^dxb|?WgqmI$o%KMdM#~R{t*+To_VD?$)3%(R`M`)} zbN=<8xepwkup!Y^l;3qkyTI;ys!qD6#0O^fd-|BU(~zu;vULYu+p%f*$i;UL?{kmb zb@sg4pG8Wl3O?T(do~VH@l86gg~08^rnQKLw+I@jQ<6AA0}&`ElsH8G5M2qykrZGk zRBeE>P(GbR{tZI;BohSap6+di#37Ix8cBsYDtG^!vu7(7E_?AQ5V^KPIt{wxwfE}t zr8ibQbmW4tNl1vcXMB?3GNZWv!U##bP{krJyb32|yR8 z(Evl6d38vh-r1>Lo6eovwCiN6Fw88>@7SqRLBZA2O8?);kJPYUbBjJ>5U&I4VH9Dc z1ZWtk-#8edsow-L?O>mT%;?jFJ}ElPY(SrOxOO4hkz^gyWb2WME|DdG++fW^yr%cL z@z$Fvv=2vj;f}^pX;tnaw<~qO*|j4Hrrwe9VY`6d(|WbUdf7>KBF+V8Lzvc!s}4bl zh!ulLQ9y^iaS;%RCT>B0uJIwr`np|6-DNgs4CCY8(0cPv&T5Mn8{KuM3ubp4&*y7d z^=l>LJ(MM&_sxf*Zy6Z?qVFd27(hTF8`sQZYK77LYpwnr<6GF@0X9$nPRM~k(lBD{ zGr}f!m*fa3r0rr4 zMq!;*Cs9-uO2%CG$%-Yak?nd3`F!FPo9-G4Uc<$eN#>jvrgA>WoO^iw;g>6B%zI)} z#f8nQOB(YTgJ!%z$4IY|#>I^o{o*fwAZNtUruxk!q zz^HWoH4bAU{B4Z=!9aBW1zs$W7bCKQX~Ek@{tVI-2!YA|5sCdF^GH+d54aN<;o%gO zn&=gX)NCGez!5@3VW{LUBfy0fyI`)s1e29^ zU8{IV2uhUQA?p%?Bd7zgN#u(2B=BZJmq}geHTSHX_VGV-u`8y$^3kWC-mtq&(%i-l zWh08T3);VQv1`mwt()Tb=JeNZzVpf9w}hO4xfEb7B=0~@XcBb@ubbo@@Yg?xJkZG0 z1ZXlqY}mp~E}?>u6d47PCk%;)WMvZq4#bQ|=GkYo+H@gOn4i4Cf{4-cn#iMz%|?hk ze_ddSUtSSyo&C)RSN3cztJq@L_Ph{ztUSB2?AN#XhZFif2RokS+{N(wsb9Ydkw?d4 zIURGcBX3HVh?}-(D25C>P1J?fLMJbTxe9>)`j35tgh7R`jIB05F&LP7*DhlEJc zDNe9!5uF0rdcSOr^(3Vbhju!`MmqvjFrp+6hK4GbkeSg*vA8PaT(W@)PVfga5?S9V zbeU{TY3bS0)Q^vbp6~MPih1{CO?dR=>aX7Yc;TS^CCeV3{`A(RJ>)Jcp6I`__kbHR zyLZa(_t?E#EBchT$sRnU=N;V(hukAzncPq-?KboUPhF_XUZEJw$pEZ?Ly+XJutCH( z84&I+WH2G7d*FitN{|oALy{MTK&r8MVUGxl@<8|^bV1SrsfomNWtt>u^)bL3*fvO_ zjD=99D}Jv09z5tFSxlh2kSTo$Jb8QHF}%BWVpMTvaeUI0cG?N~bWxwbYfI}Fjh|{Y zEwMWIAn9RD18cJja&V#2AGB?lzEg#I7&1n-t4aneowhYMU||k0N?Sp}tvQ0iktr(_ zOMZK3c{Yrf8&N?aQCJQV4jNfwqpr7WHz?av6|3Vd!Tod2oQ4hfo%%w^1RqL0e>=4C zwxLHcHvCg0j4hgMTNJ<&262HZfgv>-R~T78$*}AoD^IYalMDjk+9I1ddhXj{ZOjR! zfBn3#dgt?7r1oI^!ggQ`pv@M@aSFE}Ty!OYS5d=ZZgB|fR!c#ZIJ93_w@A!DC?O#w zS+`C|-^Vptw_51dE&15tZmf2vZrw_Pbt~~$$e8@AFmB1;dxUX|BW;1pu8hOoR$dt= zYK-=MCL?kR!l=Jy=nA9m+B>%2oG8gglQ4Q^^MhvOpIv(&2|ja>)yoBtvlc`wg&Gc^ z2NOAwMHsvYFx@!;1|D>T&&dJr-l@~g=IC7K*{at&xy?%8wLRMTH_o~`nA@~EcMJy3 z@mkT%h5PfRx%F%J%xzsJefrxWX?d6PFO9BW0vM)aUCd%#n%li-X~^r-xHNxa_e$sM zwdJqEQ|Nm|Th7-X(caPClyW7nHj?kEuc<%Bk7&Ko2WYcmL?0gVw^>O#BU!a+{UM=I zEDyX8m^?NF17`(IrFl0^mlP}!jnson;qM2z%+|qQhqbnSUs*M9Ktb;pZt4cySa)G` z@Mr1%-+V7W;V6wcwoU8_*u~^~(YK|^M`nVqqR`%!$!zlnyO=7I<_b=J%_=4htv@Gy zQy-S@oGA}oy0q@_Qrc@2&wU)WsTQO;i!d~VtCFzk5$uqHsJRN{OSk|EK}d&ay|6Pu zsAzE@4d%RXf)LgdNJ|4kV$DGxLXHINJMIwMO|?KKN3a$kk&lQN2ZUme(F5V!1E~lR z^&aRd5YM|UoAc3uuP#}NkTi6D$}l%MMa9Q$Lw6#v_j=4&JOffW;N zDQt0(X>-a~kuRuGd7(ti*O3=alxe<+m~U84q+1!;DER6{zB%~m}0RE1UnFK@l z<)R=TKpoSkTacfI38v?h6iD+;q1hWv{in~FHuLhAhu)vJY~cg%96cnBoW$GCy3dWN z^7P}zwR^W}?~c7gdSv)y?fljkBr*QUQjyYDE`inVHAaCc%pMF|3ok-NaeBxI@B;47mIhzbgiVO6>9 z$U^AK??{#(!T*HO2P}{x@+MDstoO{}y~gEqe6ZuBNBcj}|K<_x^Os0Po9}Ir)*`8A zVc9gl&li6qw%rzOE$ot`;AgNQIt@XDJX&CM;nchAnJo_plcDH}`Q zIxhW<*LB~sOZIyo4!(c!gSWbD>}Wjmu8%u_M%ecVM^<_p&SD?TjOa&62#@A@BGZu6 zhbn`SMI&$;=!7&np(p9v!jwnJSY#cE7uzBpdtqPCc(FOed}N{o*cqXCvdt%9Hg+&x zGCf6;qC`XqmNJBd6CsFLZ#-mJrywQ2r^h>|PJMp#Xl1wZb&D4B3T?y<1LUMfa(=qJ zgm(?UQ&M`%{SRvwmt2~#Dzh9ou!tKKud)OFp>!NLMONTsj5ZZXaY6=|wM!SeLjz$2f#TtuR=yOgyW3#*C=$TZ+nOgP7 z6$BwmMl))$ECpVgLxI->tBT2MN++4llwJV$dx2LMFofM<(WsS^mo1sJdi06`1D20m zJ!#2`2`fjI4j53dea@UGcg&izUFkit^ro9iN3NMUZ~0yLs2}cE-F^SUyZ<(0$5S(B z?c9YmgJ(~n*oHVZ2eeDb9^}}OgH?8ijbW`U!H^xZkIt)ddqjmH7;>Y+1T=&h#dbk; z=83E+P;IM?aL-BsN5;Ic%}VcVFuK8A#9m1KHi}Ec z-9bSX@E8*ff8-IKQ1`P^C!6bjp>=QgMJ_Y8M6O*ooTCtKPf`;?9o1yWsAL}Po}hTb}$C4lTy7cT?QqX(z-6I}pd)tPG zx5|BPnbOTwG=1=lLKiYr0Q-D{O`dFcAM&dg@uTFg^N1|oAUsd8c|<^Q*BI!5hy`*- zk=ekh2YP@;Ha8-rHnVT>{QT6kf`YWve7-n8HMO81H8tNbvqN@vhrHa}yvP+}kl6&} zOZEocj0Hw_WQ%p{TF8y8Y2h4eVXUd@kR3xYWF0IuRe?6*Hb7ENf#C6W=pyj?zy}n{ zz;Ys_mDCb5(v4|_<6%ra7~@u21X9~~5F0tW1C|X@tZf|o&GvYf)}9tO;6!R5QGAL$ zq~KLa38b`!mBFb5^IB8pnuu66G^0^E(IpWsZNeEo>P;#qw4Lvr({`SmvX^J=*~{C# zv{$QE_i7hj;_geYJvguPJw=OGE?GWwar=pVPrN66BEF{WeDe*h;=OZx#Hkmx_g~!0 z+p3MSddm8ZVAb+N;a@Nv2JcK zdCD}`y+vL)!;nrvo^NcaSU8OkJL&+)9J+&pl8kf@SrR;CF$jhbkq_`@BRLu$*-2w+ zNs(e96A^G&q~Qh;Ua}4ydmwH;fRqtp-LPm8xq~4&m?s=$eaoMnyJ^DYj9I12R^J&v z^5N0b7tJ_3ZpK~mrAbqaN_uH}d0FS@^I>zfzw+d{QcM!x+(+A~J>8FQZn_0dAr_sRAs2a(joV(2Fek}e z&-{b7E8@r~zYEFTir`ubF)2Nc3{e!IW(Zwhjg-FnA9yLX_y0WiPWc^vX92^vT@S-E zbqvo$a&Trg5#LN7h;Q4xs+5ib-&3xO?^(e2>^wEg9?Ao%r{Z1e@3B8G3$}?EcMBz> z4PkkzLv4qMV5iy^%bT4EBNgoc@li+0js4$m0PgUU$G~WE1>|NCQvU}VU=}x2JTc^! zE4$CFyr*dF&^xF8Z_&SggS=>2Y0;uq+FZnjJ;^T(?s@YKSYG(6uNt9y88?D@z+X+d zknmTdH1k)>`F@WPdFSpWOMq8P@G2wz6^Af&I%%)|FH( zFP=4XaLLSB;yj(nj8lH9GmWxb?Iu>kSp6K}S*|oddIWM>o#Mu{*W zC$Kh>H5gA}K-aPcVLH|Ik3VJ&!Zr5CtU+TeJY>1G!;AezJvxl*(`)&bhc*u1xR)Q2 z22B2X=-7cBd-qAp8#aB}jH1nJo}Ev7z%Lbwu?29jr^sL@(7`?$l5wH54I(-)o+H#W z8Js)Wt|pSlD`J94#Hg)8Te6z5*I+D40=LrG%{)t$^Z*AhH4YI5iTPUO{zcz1L+bUH<%sukJ` zPgSf+jGF$pvSCqCWKXWo*RH<$fv{pdH?m}hc1-LkHdCup#z23L#c5Tvo-+7~Sx`r_ zm1KG|a;A|%Eqq;3uo7akSnO30wM}3;$gzf5(1;NmuybIMBK67MF-WwO{)jWo{PSIgyPI`{A^nR~C zeR}Ch%ABRbmj**GY~;C-Y*4#!rxD5`EEFgUG!@BER`||D45o$b%A}vTfPcayTM&$( zTY73%mqE7;x_8*Lf~u`$!7Od1xlPB6d`Eb8`pU6WEttzdX#oE#Wb-KOWD#3Lv2G%z z*d*P8sj>yG_V78o1`1>7!j8>8l#n1FYx=L;P)h-A+}Y}Ef1X0 za687**dr0R;PH(SxX@^tB5?mZd%Br`)|_zl+P!(pvoDlwcz)p>lc$UvIpwaA%JgUU zAK1C;;Nhni;@_fq^9V;IoL)9p-rdNb3oN{Vdw8mTDdNf6U+3~++HM}#bbEx4=A-&( zQ?>(Gs9+kTc%p@0%Ps2<;`3~Lj=Xa5IrLNd{0sVg zj(n^5JdrV@-*6W7bAM&xsmp?vel6cye-L90YV+k1*aKSPys-rg5fULJoFk90${)%6 zYAc97%=oHP5B`pZW*7|{M|5lCfwj`}z*^;0wH9~c+>usL0>j2wGVfzU!f1eNv@`7S zrwN0<2PubYLZO<5xE4!1s>lW3T|(klH(0?zu5374fzYYIOUT+NJL5JO=sCRO#2c31 zJa$31gohRum5uB2#;Kzp@e#cWU~}lwJ+<$Y;l(+_=G>VaT)3%Z>dm)wnlLl1{mLbse763G)Uy6dsdv4zUi%V3 z<45N;Wy?nO z6Sc3mGgW(!7h@0P03Z4T{xP5*$S@Nrw${eDbOfm5^rUE5r^u{>)u(iT-7mB!Vd{swverPAs$X|uLktEBWPX|)z56-hno>&kTa8+w=s4{?$;;D3f~ z6!R6i2?7pzI^5WT>(5XIOc&dV;53Kb3t5c;fsOZdYQIEuhED z1@cGgi3M0yI)RVnrnaZ|^dH_{f zB--yP+Mma8q9bsh@vlc9M#zTn zWo|<}Tzj1CsgH<`H4C*s0(2IPl;mB>6a_S{$mSr!o5=TYmc4yMJAWbN~9?`tYO^ z`;Oe*V_W~~!PAzE>C^A#o~xEcATaJa|y>Q8;ttln^`shu;g zHm`rbsebAg&FYP%P4%aLMm_c^`ZZEaKwF&Y+6CG{IGwmxN-&g#)k=bql-!9RoH)`# zErhKsFWm+nM#@k=gygPb8QZ}_+fE!$Mb?)Dyvxs{l$4HGGcPcU0s!05mX3Ct zZRtqf#qPx>N3OZr5vQjl;W!o2hQxU)ER>awg-b3}^AJ>kgmhYH1Qkd^9w9fAYNNDY zY<1l{gPk2wm*9QUgFBr__0=8m&CavZgPK$IV9!y%Xou#^f9#nK15S@y_H5hy7p5Ki zw*E#l?|jGhlA(`J)GjZ+?Tr-(cD{Sq`rS_+-i4#}O%L9;U@gyidY@SjdTyJ4`{-d~ zw10ngw>CGBj;PR@B~vRmj~@Pf`FNB0-YeU7SBN#7t+^>~7_jdv_U=NQ-b8CFVqXO= z^w)7=zrI#~teSap)t}PvYY=Fs7WK)ZeZ8Hj+Cg+6F`Q?nj6-}pojZVtMmo(*#FxPnLBakO z=zlnO049eP*vAoP1Rd={M-8OFNsio7hl@^fLqZZ|mV{E1fh2Jh@ChG~9?|IPfN&^p zL0Y;24uBNzy-piaO_D$X$kmda?__@OfVO|qy2DG&B#%GwoSS+@Z6SXwcmdJ zOsm=b($1HjuG*%(#6S6(8y6@SUVGx6((bu)Z@PKS-SgLJv;L-iqy2?jKYi}t*<%|k zb(m7@UvII$05j~XMSk*ynwY{Kb6u_ zu}>$nI@nTnGvw_xnXhyt5VVRGejO6HDTf=IdY86~quE!~sP&)+=jnTn15W+}!IDcU2 zmR~7muFu?Cmv7({SeKX2ys>rXkG)r&RN z+nMUu>qTsZXn&+=e;z%LeI?@dV55k`**l2T5}9m?fUXD?MgV8nk}OmNgbE8IOF(Qg z7+FG+7d$=AE&}2yyNx)jFs=w;ss)0!ER@a*KOc_EsWQU85(#w!w~xdP>(P7Z0dFLF z&!-0wn-FDc?o~Op`uiU)e6}z~F_%~JoK4$dd(zxyre${+F?^8r0g}%F(Wg^x>7nIQ z{2o`U&mTHVuoQC@Fwo~Zm1rH=k?=AUcBE#x;NUn-kqwyBAA?=>$YFvL7v9MIsipKg9&E<=q81hP`oTLPV!TWqZ)MAfd}Fz4m_oROwt9&OQ;+0 z;3X77X(D`qVY?I7QNS&Rhuq zFiK7Zc;IZ(O~-nE%b##!rss`7(t?rGptItc-1yz{ce-~z_Q2UI_336lVARvM58EET zU*`8e`SARu4?mjv zYhi~QdIf{M4C=Vq^GA-GKX+WGZrwVeV7_LI<$J>=XvsYoInZRtbkTV@bIc7{I~H#k zL^|Er=EacUP$Q#o41A0u#ic98^8dou^#9*?i#hN`qO&O@{+MQGQyQAM zY_U9297u`0PJYq!fAGAi@%aS#IPVQz&P3-lim)0}r-h4Jb+IOUc5P?MKv!i6jL&JDrklkxNQTVj#i>zck5+!q(H|gKp+}7Hz>UF17r9 zTL|omGw+^@?Gv-I$uC_p4xvma|49a|`HFd*BScP+{ zq7ZpHPCwI(NVG8+iiYlqI5H+Ib__>z3Zq8kbW1pg|MYZAI<`TaZb@<6e{#AdV#`wf zh-0!LY0+qM^DS!^FJ7-*KI@amjdq0?XpX(d}wtUX{rxDxg^-rWGMdLRG@q#yA@iZbuUD zyouzGqCmN@?4=5dF{4l+QlGpO=WT{P9zt^f>*uG?Th&(xZzbMc3p;b=2h;Nl5O?Mz zH41TbJ~{#2AE*xozu) z7yhAL&gNH_w#zJ9$_>2r*tJ8odzTjW%vxIYMz69P=u``_ZmH7W3^N zXCL{Me!sm;MeI@Jhbk?M$=IVeiM=^c!tcId?^1nCv-&~T>W%NCKAqhpK|a3rJp*#8 z_3s%^Ugz^c^tl)dPbhDwyY=ko*slL($z~cH{XXKq8n4;{&2HLjnvM5n>^1!#=3ts# z|1DMF^F=259iZR;yP~{9zx{yi2_;!_qXgI%IiHmpG7v$vku5itvB`#NHj}@>N~KY( z7^Rysl?}ybllgyG4{0MSk^Jz1e$U*fn}l)%Wdh15lnj(ol(~`jlX0CYp7*mJxTp8? z=_*fWNv2%3z+i{vKZl()#IVJN57}8|Hi{qb-^crJ*jcFyN}yqkQiJ+*b{0JDtkI0p z1wSHrK63pLpG{y>m3x^J-#e_F1RN%_B;`r`%EKb8(0a^cC#%4iy|@OIp)6M}Yp7G6 zgkOC&#{CV>5;(_Z<2qYe%w}T^TPovNI{H*4Bikt%8`dj9aj%+YGL`C;55@B;eMjz% z@1Ms!-)G7AY!|}YaT*TuD!FI_s{?wA!yN#}$@~hg{b~M0=tZUF{EyOy|T0{!g!GN+)>e6tU?hU6|PUAYzS~M>h zS1G-xwP;?%T6~E%uL6Dq_v;knB{oW|1=fMqqOo8NKE_%+k1L{HL8;$hGn>|d)}nbK zJfrut7R?LRg4UYW1Z}CNacr?^HQFH@IRahcNyz#~xLL-Yr=8V!mGC+L6s4P9*cDtC zV-rom{-OO4DGFo7*r~;QBufy5_RBjgfr{J>MPj>*eX%z9t~4DuIh_^pC~X0?jkR(s zhgc6|K1-!_1s%eefQKlixu7$kH29Yp`J2Nk_!>!)w#n_~Tjl%Yo$~9*@yJ#NDhriW z%0~vpFx2q6G2Xb$_@gP#w81Qydzv3GZ!lMx&saKJK8uQrdLZglv^lzW^up+GV|+2) zW2VLIj5%XXv`(@fw$|B3*{Wh#I6I*5PZ(nHNZNK2a;o^?Ij**Tzj&063=l#wT zt~A#w*Qf4n?pjZpXO1WAc{{Fa+={plyq&!}yr0JRjjxRVB_So@^@RTVcC)fI-d{w@;{cgX{Kh6J9ij*=c<;j${ z14^JxU`C)UP!Tu~IMupm>w&F@wVu#=M(g`quWJ2i>u+2Cl*&?LQsYxwr?yY+n%X~g zcWPDYvDCLyKT7>7_4_nu+M2Yow4G^t(+;PdO1Gy!nO>QGApJ!8yXhaNf8AzFn_X?x zHb>f=Y4bsw&)d|t`K7JWc2?U5+OBC^)^=yxy=@P-W9{a&d$8TZ?Y6Yr)lSVw%t*`V zopB^{SmuPxvdmAjQvT$xXV$2!`?EG>?aBHet1er~?w|c=_Ll5Z*%$Cvmy?o{hrc!L zZS7~ZU(D-Ho`P zg>Ta&eMPVA?82(8Ry2Ifo}W}=n^&=W0;MR zHMZ@;FLjL-HFu%UX(;+gbtsI(ynPtKWHFn*k@3)vO3%cveoeu%v+&71V&2WaP4msh zYDM^T!zA=~y?_5iC9S!P--$CIw$ubhjuSSab;Kaf)CQdmIqFzz7wlyoV1^g#n1E9^ zU?>KCXayUZ5BYs5@YXi}#lLp2jbvh7=$HR!jr3pt%SVLgAAbR`2kVJbC3<0<^xp#P zCw>iZ0KAd|*)8~mfI&^a3RuF1;K#CWN2X)rZv) zzZCc=TZ>;kdmcKJjNc3Vm@S8IT;T@x2sh$41W&ThxCy@%SjqnVzj``%-=?7`4xd{> z^J*Xu=UE<6N3u8}B``3wsx!a`dXZys{FQ0v5ANd?5y1tJej(Pvs!QcTSqOWmAJ4UVDqVnGIOqb=2e3WA94cqqHqjhD!J?ytfn|j>;9=uDrrMg>naJ`9u>w4XKJux$PqTf3{ zef0cnaH`P`bX>L7j5z(?S?{2u@w6v{cu+qc)Q<;kJ~ivtTysPAyRn0vMPqJu7u%jE zotwtc&N{@0h3-}iysydG%D?@Z?fe|2eM)USSgu$pygbD-ESsb(Ul^#X7_&X?vb{3bZR84lxfFS|M6s zz7}E*w6=$s1#{m+%z)YJA*R7hHpG&D>#MwLAKbc3gVL)ID`2@7;wV_zF~F{C)&2uS CKI# + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/opensans-regular-webfont.ttf b/fonts/opensans-regular-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..99a1ece2625e325c18f4cdc563bc1f84895b71d0 GIT binary patch literal 32384 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 SjRi2@(KrScCI;AY%+fvL%FqM= literal 0 HcmV?d00001 diff --git a/fonts/opensans-regular-webfont.woff b/fonts/opensans-regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..826d6434cff9400ff8f34f6f47cd4e66a272eb3e GIT binary patch literal 20948 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 literal 0 HcmV?d00001 diff --git a/images/bg-ramp.jpg b/images/bg-ramp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..77385632b95e7bf89e88cab976df23028724d8ac GIT binary patch literal 2161 zcmex=1UGFf3qFV`da& zU=n0x7G(T?gh2}ER4`zG17;RRCRUFBw-`7;zF=VZTbSt=`~V`&zA9{L|K&S!s+OmU z<}9}Z3Cb?bj0&5&w3bcm0fX?I<(j98ryO7aiA`Cn!NAO&0SpL`;=k9XE(Nj~l5!v> zF)$czgRvxTH9(m>YaO60wlJ6&v(*Er7>gK`!JxDP%3@T7332XifHLLeU@U$;7)$24 z15}o;1j=Br`2u4-fHCZWK_UhU510UiW@G^e2a^DhdsqW1$FYH_0n7x6a)II-0k~mI zZUrcd(E%nzZyK+cm_d%eq5ySMz=8%4pFx9_g@J(y$Or(kK|TZd6f6Uh9dN)5^pb)A z&{fR9h+<-fx<`NS-9;}+Gvnn-SFZX3A3g}TjDRtwD+vIzz zW`COAvm-TJD>Lv^U&yAFQ)_l^U;p8&#HVAsOa+(p%49tDl{)e9`00Q>fnyh4RDT|B8rkMsl?C*b3^3+dj6$q^R6esjxYRJl^ z?ppQ_lij5(E*)ECBlzJOM0UMf=pXfcVUNH{k%)aiV?WHhd9Vvf2+aKTT>MZ~x||l8 z_-dP{KRlnCVG4iv;hEltA*}oJkJ=|`l)wOT!mF<6g=y?$~#K(M+lJiAQ>-yx7@zrQqCAtJ@EDbMrGTi(bq6_)zfdH5@B8e_p;j zbjgLNWphi!|1+EnKDUeWNaK-}hbywP-i58~UC|@5<%#f2b8Ytbj)rxg{Wq6yxKJN@ zb3@jD2J8JI;@QsGt1g7hUf*zrb&g2)zR)SfJ69SPR%f^+I^FDg7{IUccgBIM+tNZV zT&?C)Nm=fChHr6OndY_otT!SmG}~S~R;>2Yy~Yy$$=y0c*fDEn*&6ZroWbjMac|sE zs8w?B!mS;tSu?H9CmfkG@m7DTbY2$Um2fEr%aw-p+g{Cc&=mW#IxD6)sCO5$VTMhJ z+N+I=8u*Xis!zXUK1J_*YxvK*9~fR}F>hUHcr`=I_xgb}rgL1b_w`O$-np`H$KEZ< z1`BhJ6|7*NRCgvJ>yoinM$9uAkCXn~Gi2R)P6V&r$Gze5K_Bxd;iFMb%8p|4>%VuJ zF=qbKY|lDWx!4oz$csn)rEf@w^K=HgN1lxjA}le;OTb zJ!^9A1<8)>T}^i*a&x8F4}6=_s$A)`$w|ISeZ~83c1N*R##6>-o-KyL{61R@X%bf0eTWF^9dj3I-s$}l9xD4|-{S4U>rZ`G`p>|6bx&cfd!BmSw0EwLj$WwWq`%_r zk=GmSkBfJUPN*rg1xm#<)u;IB#Pav4&%NDwZNYb?!mLf=o&Sn=%uN#fyZ+JDi$CT+ zv+lfn_}%{Q(4M3Bl6TD2=D&DTzg+mpJ9Up+GE;kxe7xgdx%@>@l)1162>)vp33Me5 zmCFOOjf5bue1a8E;Cv&<;83stQkO6?+B014IdkUBLt`K~>W)Kw08~A&sIzBKQnqn% NTcHF37J2_~0s!8!lD7Z= literal 0 HcmV?d00001 diff --git a/images/blockquote-gfx-2x.png b/images/blockquote-gfx-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..993efd5a0c035ea1faff2e11d9656176bf9e9d93 GIT binary patch literal 366 zcmV-!0g?WRP)kO1)bA`nC1 zp<+fWZZ4o(0*Hvz3tC(QL^$e=J{K2T)dVLDcQUvlRCSOo0wU0i5q+`S(ID^)5!ZVW z#DtzIhLFI`nuB0t1i1d)T120#%4d1Y!?SOgN24EXyv$1QLgg0kR{C zNU|dL70FeqD?2e9TTqloQKG(2kW*-|o7go{Q6ke?*8c+72O``xkHC)YfB*mh M07*qoM6N<$f;!8LlmGw# literal 0 HcmV?d00001 diff --git a/images/blockquote-gfx.png b/images/blockquote-gfx.png new file mode 100644 index 0000000000000000000000000000000000000000..bd5be35022f1585a4c8d97b4e1cbcdfdf97d031d GIT binary patch literal 224 zcmV<603ZK}P)K zv>_wR+*KWl4j{p=s>PZSX2CWLkdaBxx33vs=D`Uk+wj{#!+PQXg5AJEl9|BNkXbkc z0W>};yD|Scr5hD9G1?6J5g+nS6(53r01*~2=LLVz+e!I>%wvc@sq%k>pTj_vJf)}- aEA|Ge-PhrYDtPb!0000_E)P!3HG%MVKuHQtqBEjv*44lM@)4ZhN1tXs~Ex zlx1L@+4#di;6M8#w;Zmn1(Ou+H2FHRDzZjT5UEi1o}%Qrk~nppguou6{1-oD!M7$f)*eM{7G*TEIzN<=ayNgWY@?85TOZ6d#j|{sC0Z;OXk; Jvd$@?2>`fhABX?| literal 0 HcmV?d00001 diff --git a/images/download-sprite.png b/images/download-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..56eedc46dfff283afaba2836c1db64c2c548c942 GIT binary patch literal 10019 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 h1EO{{xQ$ZyNvr literal 0 HcmV?d00001 diff --git a/images/footer-ramp.jpg b/images/footer-ramp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..99accec91ec30c4918dcf5542b03b740bb7eded1 GIT binary patch literal 1285 zcmex=i3*BJ!6k`h{6EAX z$idLeq{hrB$iO7X$SlbC{|JL5(5WE6fB?*lOe`G#Z!vH{M1M&;K;?KcpbW;D7htT@ z3!p6KId7mWhKDy`vWGXoWP29C#7vbl=|7NWBQ+A;-U}UqOr#dwV&l|xcKk6~tR_ns{kXM@Gz6+bp0d*=Hk4)+Lj_FB*SWwq&e_`_a%=KThu5Z5n^vBhQfBS(k bDk6wn5P%|e0VMA*0^>|*KOPm#P!%@;!onP8 literal 0 HcmV?d00001 diff --git a/images/fork-sprite.png b/images/fork-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce61b22b7fea371991789fefab45453e34a428d GIT binary patch literal 1940 zcma)-X;hL47sqjd29>55NwZw%q1@6I(XnJ2w^traM{%!oFibPeRLo^^&n3wweFAqe z3{f%7)Lbz4LdZrbD)%#y9DX!~! zJ-5*IWK^{DNiZ@Aad~w;1M#&(Q5-&%)#-DkxPdpUvb7Q(L2`6~?bLCbG^)Ja27@7< zfHsz{(LReW`f4Ujccl2f{Vbnw>5C&c1ALIuWcVMo(a*={OjJhC=V%TVSk*sVmf|}k zMxp`ZZy(7sK{DA?#wX z(%yn{xe9*0R@kA3T&_$s+t1{z?hwqqBBiP-@q-(dFr@Bttr+VIt&n7*yVMQ4Tv7aE z%U7^o;h`%*t4>IFEJpA+8%nawc4f)$KPB95Bt#ZFuHGnOxK}(BK!@q;lT1BOqrEqz zXBJbqx`7KcSXWwK90NSV@t19q&=~)*b7B>DYb>S?ky{#dxzbWI(O$#AIqgth*34zo z8F6)wLaxBK5zY$eTzN)KAysM&16nht8Ya z%{OSjf%Esr4|Z>Ow&th$81@p`pjT%_HYB-V(|#+(e{o{+kwUQ}?$_?d3dHM_uj^;C zp~X1k6(;3owbik?UE{NApU}cCG9?*k0}IHACj{ZanmbBmtzLi8Vah_^iZRa&$ks##!b1OiR z726b`SgPv|^=GrGfM_px)b9VpKzm8ZyH}S&%#sc&-IPu!x2nLE*=`9ty*P7 zLQ9%79HY;!TLV?Hv#Yf~JB12N?FE0aLw|=BfMlg|vmBIqONZYdhi=UNyt_6HXK;7M>*c zIF~9GXf!{oUDC}j;6p|y^qG)anXIjPTF~)8C$@YWxdyFw#<~YaD`%TtB9P#9RVg^e ztwrZY;9;H$fA^Hg{aSqVaSAbpCy!R4Vb7dP316>8B()G- zU&Z+~h;;Lz>5vsBlvi++E(dhtXwrUrs_$<;HOKC%p9VUk-rc`F7+}73Gd>LW_aiKZ z%255y?Pbu+7-a@!O1~#+>&xS2;jZ^yCP8#xaplqp z(xZ!ptB>p}Q;Oq!m5mq!L6wnWbTrWfRl8&)%3Xc{+&^ZYsqa0g|J1D%Zi+)uU(5Mt zE+ls7$8mp}%7*G6PLG`2U8vS{SAN>ib!K&da{amRN0T*+QKK_w_ARud^`OH}>E(X$ zr%EC1meuFY_16}8M|+~pUb3fR(Qh~Xn2^s~$)EOXbXB<*dgTJ0s&DpeS;QT&LB0b? z{)pC}MD9J_QMT6;Ub(y$2gY2?cbmE`94qUZuWo63&P-a?%y( zIqmBy*6aOE6mKMtKSu06UY@*=JRd5VwH-%eO6jclsjoM^jfETlT!4QI2cJU|Ze#)m zh-(3Yk)XO!rLP9`ajocpyLIX$Rvi|-b1haASW9fLk+1=E=7 z)9VCNTNM)zA?F08g=lJvp_Ti&NU#A^R;*b7wN&8oeeS6=@R4hGsf+SFK)lm7+GLaL?! literal 0 HcmV?d00001 diff --git a/images/hr-2x.jpg b/images/hr-2x.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a883d5d6076e8837bf424edb714a7a758e7bf8ad GIT binary patch literal 1591 zcmex=1UGFlaF~Ff$4= zFbOg;3o`yc!XO27Br_w(xp2V5%*w*Z@&6VB2SoU{21rVAifI~%mUsXp811~Sl^y^x z7!*K+SYhFA>ueBR3b zd1jym(gHvqFfk+fLX??_g*6~S0UCOYObjn`-D68{TwYpwJr)-#m-CY5T3y?-Qyr(0 z((6@`e=Z+=^H?j~DN6Kww7ht3)yLpRkJrE54|Ini&^yc=j7%&n>@c@5FJfe36%0zb z_=rVFSkcfiP(&%9P&GIqsb%6J0jTSk?HPXQXDsHO@=G@R(x*8y4fFQ8?)obB`bzZr zz`GioQ}wn-eA)BV%>TtTun>|gO_()_F_U)33aNfye$`&h`{uj{?@UbBPx|hC)_&>Q z$iT1_^!XfziDPZ>Fq9mRo~h1tTT*Vw{z0t%de)-R(*3n zktVJLh7F3jE_qX?H%c#6ec<-evqErJP#nu(5)gb_RHr*hf772mGU;daZ`pJGbN}(5 zVSj7A@*j@=?A{MOGi#@-KX_c1S@&?z`T^*$$1l#?AGbUnX+E#;nc=***-~5u(|5!i zkJp`Bd(7^GSILtu4aQ|9JCn&P!N+vb^1VsT z$MN~IqctlG8=qS|+3{KB^E#_zn{Td3ws#XgQNHB4#AjRSSLZi7{wd$RM_Ae_>ZbYc zPZ>{&#!`Gu1dioQx zxdTg-m2XI~f;buCPDZwk3t~A=d}C`IWWpTFo-5CoVk5}MVe?6%_BUsu`u=_L_5zDE zjOzb=$d`;yWV_(Q{8L|w$Lntx-R(%-n>zZZS~Ga=(H z$ah_X(vLVIfqTlN9)6CB1wmd0Hj0l}RTeWJl2aIB9FDXn3;H4^cyn=hi_fckzOV9$ zea$C*+n?Xh{|HGw)BIJ+;_vrgF8%kDI#T~L@SorJ_xqFh{u&p5b#3{52OrdYv#`-& z>Dl-5SHchb{kP=z-@ku~x60Fvy~QTb3rsmInUYZ9e1vOAa)cG|gZ1Wr3jQ;Qzx(`i YfBxV4U7UaZRQ_j}-y*XBzqbE30p9SNfdBvi literal 0 HcmV?d00001 diff --git a/images/octocat-2x.png b/images/octocat-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f995921f90d1487fe4b1c7bea614581c9f4158f0 GIT binary patch literal 1334 zcmV-61WU9a!eM=nkxjix$AH_}p(cz*kTZH+v1-8Ennl3QmKurK@}qW12oE|9SAYxWpgS1#|-Cry;y0Xa}u;194PjN5iu8ES@idY~CwXM*>%i@P}*w@C_E z11*x7$*0mMV|oT#_Q1Pl{GL&;A@WsRTQ)yiIHmFbk;QuZX9$9u7VGm+#v!HAal0#LblFgAU31=M{{Z;G9fW3QefJ7oKaz zXC3@=qWQ%E5!{W4;A2t~m*Y_oNGZDnf$W#8#O-P8^+|lSyKc5X&EqHa_VD-vWm)jAYQ_x&D;8 zG}#A-&39sFM;;XDf>z)91R{L$#7`W!7UgmS8s@1_Fu8f%87I6nsHCgNrRE?aj7J2 zh^jphH&zumKg3umI3t6MB{9(B2jbpa9%7mcURbmtmZf0ves5?6bFnS2Ef11wmv=92 zD2wJn0Tk8Gk`P=Mf?vc|ZyCXH7qJz;+m?Ek&7HY`1gBZsQc#U)*$@ODfRRh$vhGLg zp%g9M=fesFC&7)29S_U39`YyMC0(?~8sSXvGZU=lERn9-j=VM(%!z|*iw5vB@Og2a zR(unJv%+(dqH$#xT+M|N4~qraAbw=I`*{Gr7IrIJ;7J^Hw#7QE#&eVnzpv^m04Y#e zvZgf>)rV`3dG=I2`9xS>^0-}bd1oUEU*q`=v;r*c+=cfw9`5R8(D6a!Jt&E#CS<|9 s)RF@ypbGln$&f6}%Q|In5M)30f32X7`?l2=RR91007*qoM6N<$f)g-gH~;_u literal 0 HcmV?d00001 diff --git a/images/octocat.png b/images/octocat.png new file mode 100644 index 0000000000000000000000000000000000000000..7c55dfc985d53505454fae0a5118c29ce0529523 GIT binary patch literal 654 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 Yq?hfTW3+TSu-stqboFyt=akR{0EmPoYybcN literal 0 HcmV?d00001 diff --git a/images/site-2.png b/images/site-2.png new file mode 100644 index 0000000000000000000000000000000000000000..7c55dfc985d53505454fae0a5118c29ce0529523 GIT binary patch literal 654 zcmV;90&)F`P)+C7E2mMQ0Mq9#?<_s8;9UV%KMD* zH!^4ns^Azze4!qH>#WvH^zY^6hm1`?L5-{YBjNW6$T8!q5J{eGFlYSa!8nv{)3MU+ z<~8&g3tt$&1sjV%S;Qq1*VZr|+v41o|K2VFEfK2eCio}fRrqK*M~r`6JoNc+VWJO= zufs5<+;l zt6J*5?qQ?1rtxdb*Ve_=)bC+Dmo1Q&w&#;vZ9hQDDYr~Z7U43kxfKKBr5_-H9GP9Y zGzPo?LTQ6aDckIh=lTmVa?YeR3jMH1KuL^Z@Yxdlrq=niq%laStNXQhE!ScJ=w}%4 z)cGMs&d3dI0$fLU_C`RGTuO-}Fwb#VxM^23hiR?PsfY$((;B&Q4&x%|`zlvs=bI(l z&3LGc?IlOnrG{DC+e8L-K*{b@=N9bgEJ-tOL%+{i__n#v$P^r;<UtP)DL>TB!tCVOpK@@~0002p zNklww(XG?DpzapuM6XH~nL zXz`Emob7V&?Q-BeTmZQUXMh)H z5>B1d;)i+eJlo{Hb8nMlbPg^U$ptuL#ET!~iji96gb-r@y~ioly;T#v!I9#AD<=HU zWpgn;d5mt8Ke>j6Uw9t`opPkud*!2qUwnKq=i{fJoWmX0TFU;Ylv?kDz&GyGaJ9L4 krH)ndjc;xS=Oed$1IQCY?7(pE!vFvP07*qoM6N<$f?2Yqvj6}9 literal 0 HcmV?d00001 diff --git a/images/small-ribbon-tail-sprite.png b/images/small-ribbon-tail-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..f717af616cb8ea691342888e68b999d009827614 GIT binary patch literal 311 zcmV-70m%M|P))xZ90002O zNkl + + + + + + + + + + + + + + + TTRTS by mdiluz + + + + View on GitHub +

+ +
+ + +

TTRTS

+

The Tiny Terminal RTS where the players write their AIs

+
+ + +
+
+ +
+ + download .ZIPdownload .TGZ + +
+ + + + + +
+

+Introduction

+ +

We aim to create a simple terminal based rts where a user can program an AI to control their army

+ +
+ +

+Gameplay

+ +
    +
  1. ttrts clients are run from the command line
  2. +
  3. ttrts-server is launched from the command line
  4. +
  5. clients will connect to server and confirm initial board state + +
      +
    1. clients output a text file with game data for this turn
    2. +
    3. a player, or program, reads the game data file and outputs an instructions file
    4. +
    5. clients read the instructions file, simulates the turn
    6. +
    7. game state is verified between clients and server
    8. +
    9. repeat until an end state is reached
    10. +
    +
  6. +
  7. once game is finished, host and clients disconnect and a winner is notified
  8. +
+ +

see game for full game rules

+ +
+ +

+Source

+ +

+Targets

+ +
+ttrts
+ +

Main TTRTS executable , runs from the command line and acts as client

+ +
+ttrts-server
+ +

TTRTS server executable, runs from the command line acting as server

+ +
+player
+ +

Custom player AI code, this should contain examples and test code to help newcomers begin their journey

+ +
+ttrts-test
+ +

Test executable, to be compiled and run to test various functionality

+ +

+Libraries

+ +
+game
+ +

Implementation of the RTS rules and simulation. game has full information on it's implementation.

+ +
+net
+ +

Net code for hosting the server and communicating with clients

+ +
+ui
+ +

Wrapper for user interface for the terminal, this only really needs three stages

+ +
    +
  • Initialise the game with settings and connect the clients
  • +
  • Run the game simulation to it's conclusion
  • +
  • Display the game result
  • +
  • ASCII Colour wrapper for separate teams
  • +
+ +
+maths
+ +

simple maths library for 2D calculations and types

+
+ + + +
+ + + + diff --git a/javascripts/headsmart.min.js b/javascripts/headsmart.min.js new file mode 100644 index 0000000..16da97a --- /dev/null +++ b/javascripts/headsmart.min.js @@ -0,0 +1 @@ +(function(a){a.fn.headsmart=function(){var c=a(this);d();function d(){var e=[],g="";if(b("h1")){e.push("h1")}if(b("h2")){e.push("h2")}if(b("h3")){e.push("h3")}if(b("h4")){e.push("h4")}if(b("h5")){e.push("h5")}if(b("h6")){e.push("h6")}for(var f=0;f0)?true:false}}})(jQuery); \ No newline at end of file diff --git a/javascripts/main.js b/javascripts/main.js new file mode 100644 index 0000000..d8135d3 --- /dev/null +++ b/javascripts/main.js @@ -0,0 +1 @@ +console.log('This would be the main JS file.'); diff --git a/javascripts/modernizr.js b/javascripts/modernizr.js new file mode 100644 index 0000000..434b0af --- /dev/null +++ b/javascripts/modernizr.js @@ -0,0 +1,4 @@ +/* Modernizr 2.5.2 (Custom Build) | MIT & BSD + * Build: http://www.modernizr.com/download/#-fontface-borderradius-boxshadow-textshadow-cssgradients-shiv-cssclasses-teststyles-testprop-testallprops-prefixes-domprefixes-load + */ +;window.Modernizr=function(a,b,c){function z(a){j.cssText=a}function A(a,b){return z(m.join(a+";")+(b||""))}function B(a,b){return typeof a===b}function C(a,b){return!!~(""+a).indexOf(b)}function D(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function E(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:B(f,"function")?f.bind(d||b):f}return!1}function F(a,b,c){var d=a.charAt(0).toUpperCase()+a.substr(1),e=(a+" "+o.join(d+" ")+d).split(" ");return B(b,"string")||B(b,"undefined")?D(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),E(e,b,c))}var d="2.5.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k=b.createElement("div"),l=b.body,m=l?l:b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),k.appendChild(j);return f=["­",""].join(""),k.id=h,m.innerHTML+=f,m.appendChild(k),l||g.appendChild(m),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},x={}.hasOwnProperty,y;!B(x,"undefined")&&!B(x.call,"undefined")?y=function(a,b){return x.call(a,b)}:y=function(a,b){return b in a&&B(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e});var G=function(a,c){var d=a.join(""),f=c.length;w(d,function(a,c){var d=b.styleSheets[b.styleSheets.length-1],g=d?d.cssRules&&d.cssRules[0]?d.cssRules[0].cssText:d.cssText||"":"",h=a.childNodes,i={};while(f--)i[h[f].id]=h[f];e.fontface=/src/i.test(g)&&g.indexOf(c.split(" ")[0])===0},f,c)}(['@font-face {font-family:"font";src:url("https://")}'],["fontface"]);q.borderradius=function(){return F("borderRadius")},q.boxshadow=function(){return F("boxShadow")},q.textshadow=function(){return b.createElement("div").style.textShadow===""},q.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return z((a+"-webkit- ".split(" ").join(b+a)+m.join(c+a)).slice(0,-a.length)),C(j.backgroundImage,"gradient")},q.fontface=function(){return e.fontface};for(var H in q)y(q,H)&&(v=H.toLowerCase(),e[v]=q[H](),t.push((e[v]?"":"no-")+v));return z(""),i=k=null,function(a,b){function g(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function h(){var a=k.elements;return typeof a=="string"?a.split(" "):a}function i(a){function m(){var a=j.cloneNode(!1);return k.shivMethods?(i(a),a):a}function n(a){var b=(c[a]||(c[a]=e(a))).cloneNode(!1);return k.shivMethods&&!d.test(a)?j.appendChild(b):b}var b,c={},e=a.createElement,f=a.createDocumentFragment,g=h(),j=f(),l=g.length;while(l--)b=g[l],c[b]=e(b),j.createElement(b);a.createElement=n,a.createDocumentFragment=m}function j(a){var b;return a.documentShived?a:(k.shivCSS&&!e&&(b=!!g(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),k.shivMethods&&!f&&(b=!i(a)),b&&(a.documentShived=b),a)}var c=a.html5||{},d=/^<|^(?:button|iframe|input|script|textarea)$/i,e,f;(function(){var c,d=b.createElement("a"),g=a.getComputedStyle,h=b.documentElement,i=b.body||(c=h.insertBefore(b.createElement("body"),h.firstChild));i.insertBefore(d,i.firstChild),d.hidden=!0,d.innerHTML="",e=(d.currentStyle||g(d,null)).display=="none",f=d.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}(),i.removeChild(d),c&&h.removeChild(c)})();var k={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:j};a.html5=k,j(b)}(this,b),e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.testProp=function(a){return D([a])},e.testAllProps=F,e.testStyles=w,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=!!b.attachEvent,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f .header-level-1:first-child, +#main_content > .header-level-2:first-child, +#main_content > .header-level-3:first-child, +#main_content > .header-level-4:first-child, +#main_content > .header-level-5:first-child, +#main_content > .header-level-6:first-child { + margin-top: 0; +} + +.header-level-1 { + font-size: 1.85em; + border-bottom: .2em double #d3ccc1; + color: #7c334f; + text-align: center; + font-style: italic; + margin: 1.1em 0 .38em; + line-height: 1.2; + padding-bottom: 10px +} + +.header-level-2 { + font-size: 1.58em; + color: #7c334f; + margin: .95em 0 .5em; + border-bottom: .1em solid #D3CCC1; + line-height: 1.2; + padding-bottom: 10px +} + +.header-level-3 { + margin: 20px 0 10px; + font-size: 1.45em; +} + +.header-level-4 { + margin: .6em 0; + font-size: 1.2em; + color: #cd596b; +} + +.header-level-5 { + margin: .7em 0; + font-size: 1em; + color: #8b786f; +} + +.header-level-6 { + margin: .8em 0; + font-size: .85em; + font-style: italic; +} \ No newline at end of file diff --git a/stylesheets/non-screen.css b/stylesheets/non-screen.css new file mode 100644 index 0000000..eea5ecd --- /dev/null +++ b/stylesheets/non-screen.css @@ -0,0 +1,154 @@ +a#forkme_banner { + display: none; +} + +div.shell { + width: 640px; +} + + +header { + max-width:640px; + margin: 0; + top: 51px; +} + +header span.ribbon-inner { + border: 8px solid #7c334f; + padding: 6px; +} + +header span.left-tail, header span.right-tail { + width: 19px; + height: 10px; + background: transparent url(../images/ribbon-tail-sprite-2x.png) 0 0 no-repeat; + bottom: -10px; +} + +header span.left-tail { + left: 0; +} + +header span.right-tail { + background-position: -19px 0; + right: 0; +} + +header h1 { + font-size: 2em; +} + +section#downloads { + height: 171px; + width: 602px; + margin: 51px auto -250px; + background: transparent url(../images/shield.png) center 0 no-repeat; +} + +section#downloads a { + display: none; +} + +span.banner-fix { + background: transparent url(../images/shield-fallback.png) center top no-repeat; + height: 31px; + width: 640px; + top: 20px; +} + +section#main_content { + padding: 20px 40px 0; +} + +footer { + max-width:640px; + background: none; +} + +footer span.left-tail, footer span.right-tail { + width: 23px; + height: 126px; + background: transparent url(../images/small-ribbon-tail-sprite-2x.png) 0 0 no-repeat; + top: -126px; +} + +footer span.left-tail { + left: 0; +} + +footer span.right-tail { + background-position: -23px 0; + right: 0; +} + +footer p { + font-size: .6em; +} + +footer span.ribbon-inner { + border: 8px solid #7c334f; + padding: 6px; +} + +footer span.ribbon-inner p { + font-size: 22px; + height: auto; + line-height: 1.1; + padding: 20px 0px 10px; +} + +footer span.ribbon-inner a { + font-size: 38px; + display: block; + bottom: 0; + padding-bottom: 10px; +} + +footer span.octocat { + background: transparent url(../images/octocat-2x.png) 0 0 no-repeat; + width: 60px; + height: 60px; + margin: 20px auto 0; +} + +body { + font: normal normal 30px/1.5 Georgia, Palatino,” Palatino Linotype”, Times, “Times New Roman”, serif; +} + +ul li { + padding-left: 20px; + background: transparent url(../images/chevron-2x.png) left 15px no-repeat; +} + +table { + border-bottom: 4px solid #bdb6ad; +} + +th { + border-width: 0 4px 4px 0; +} + +td { + border-width: 0 4px 4px 0; +} + +pre { + border-bottom: 4px solid #bdb6ad; +} + +img { + -webkit-box-shadow: 0px 4px 0px #bdb6ad; + -moz-box-shadow: 0px 4px 0px #bdb6ad; + box-shadow: 0px 4px 0px #bdb6ad; + border: 4px solid #fff6e9; + max-width: 556px; +} + +blockquote { + background: transparent url('../images/blockquote-gfx-2x.png') 0 8px no-repeat; +} + +hr { + height: 42px; + background: transparent url('../images/hr-2x.jpg') center center repeat-x; +} \ No newline at end of file diff --git a/stylesheets/print.css b/stylesheets/print.css new file mode 100644 index 0000000..32d9a9d --- /dev/null +++ b/stylesheets/print.css @@ -0,0 +1,34 @@ +* { + background: none !important; + color: #333 !important; +} + +h1,h2,h3,h4,h5,h6 { + color: #7c334f !important; +} + +a { + color: #417090 !important; +} + +#main_content > .header-level-1:first-child, +#main_content > .header-level-2:first-child, +#main_content > .header-level-3:first-child, +#main_content > .header-level-4:first-child, +#main_content > .header-level-5:first-child, +#main_content > .header-level-6:first-child { + margin-top: 10px !important; +} + +#forkme_banner, +#downloads, +.left-tail, +.right-tail +{ +display: none !important; + +} + +.ribbon-inner,.ribbon-outer { + border: 0 !important; +} diff --git a/stylesheets/pygment_trac.css b/stylesheets/pygment_trac.css new file mode 100644 index 0000000..c6a6452 --- /dev/null +++ b/stylesheets/pygment_trac.css @@ -0,0 +1,69 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ + +.type-csharp .highlight .k { color: #0000FF } +.type-csharp .highlight .kt { color: #0000FF } +.type-csharp .highlight .nf { color: #000000; font-weight: normal } +.type-csharp .highlight .nc { color: #2B91AF } +.type-csharp .highlight .nn { color: #000000 } +.type-csharp .highlight .s { color: #A31515 } +.type-csharp .highlight .sc { color: #A31515 } diff --git a/stylesheets/screen.css b/stylesheets/screen.css new file mode 100644 index 0000000..ba63020 --- /dev/null +++ b/stylesheets/screen.css @@ -0,0 +1,569 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on February 9, 2012 */ + + +@font-face { + font-family: 'Open Sans'; + src: url('../fonts/opensans-regular-webfont.eot'); + src: url('../fonts/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/opensans-regular-webfont.woff') format('woff'), + url('../fonts/opensans-regular-webfont.ttf') format('truetype'), + url('../fonts/opensans-regular-webfont.svg#OpenSansRegular') format('svg'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Open Sans'; + src: url('../fonts/opensans-italic-webfont.eot'); + src: url('../fonts/opensans-italic-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/opensans-italic-webfont.woff') format('woff'), + url('../fonts/opensans-italic-webfont.ttf') format('truetype'), + url('../fonts/opensans-italic-webfont.svg#OpenSansItalic') format('svg'); + font-weight: normal; + font-style: italic; +} + +@font-face { + font-family: 'Open Sans'; + src: url('../fonts/opensans-bold-webfont.eot'); + src: url('../fonts/opensans-bold-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/opensans-bold-webfont.woff') format('woff'), + url('../fonts/opensans-bold-webfont.ttf') format('truetype'), + url('../fonts/opensans-bold-webfont.svg#OpenSansBold') format('svg'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: 'Open Sans'; + src: url('../fonts/opensans-bolditalic-webfont.eot'); + src: url('../fonts/opensans-bolditalic-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/opensans-bolditalic-webfont.woff') format('woff'), + url('../fonts/opensans-bolditalic-webfont.ttf') format('truetype'), + url('../fonts/opensans-bolditalic-webfont.svg#OpenSansBoldItalic') format('svg'); + font-weight: bold; + font-style: italic; +} + +@font-face { + font-family: 'Open Sans'; + src: url('../fonts/opensans-extrabold-webfont.eot'); + src: url('../fonts/opensans-extrabold-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/opensans-extrabold-webfont.woff') format('woff'), + url('../fonts/opensans-extrabold-webfont.ttf') format('truetype'), + url('../fonts/opensans-extrabold-webfont.svg#OpenSansExtrabold') format('svg'); + font-weight: 800; + font-style: normal; +} + +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +header, footer, section { + display: block; + position: relative; +} + +/* STYLES */ + +div.shell { + display: block; + width: 670px; + margin: 0 auto; +} + +a#forkme_banner { + position: absolute; + top: 0; + left: 0; + width: 138px; + height: 138px; + display: block; + background: transparent url(../images/fork-sprite.png) 0 0 no-repeat; + text-indent: -9000px; + z-index: 3; +} + +a#forkme_banner:hover { + background-position: 0 -138px; +} + +/* header */ + +header { + position: relative; + z-index: 2; + margin: 0 auto; + max-width: 600px; + top: 38px; +} + +header span.ribbon-inner { + position: relative; + display: block; + background-color: #cd596b; + border: 4px solid #7c334f; + padding: 2px; + z-index: 1; +} + +header span.left-tail, header span.right-tail { + position: relative; + display: block; + width: 56px; + height: 105px; + background: transparent url(../images/ribbon-tail-sprite.png) 0 0 no-repeat; + position: absolute; + bottom: -37px; + z-index: 0; +} + +header span.left-tail { + background-position: 0 0; + left: -31px; +} + +header span.right-tail { + background-position: -56px 0; + right: -31px; +} + +header h1 { + background-color: #7c334f; + font-size: 2.5em; + font-weight: 800; + font-style: normal; + text-transform: uppercase; + color: #ece4d8; + text-align: center; + line-height:1; + padding: 14px 20px 0; +} + +header h2 { + background-color: #7c334f; + font: bold italic .85em/1.5 Georgia, Times, “Times New Roman”, serif; + color: #e69b95; + padding-bottom: 14px; + margin-top: -3px; + text-align: center; +} + +section#downloads, +div#no-downloads { + position: relative; + display: block; + height: 197px; + width: 550px; + padding-bottom: 150px; + margin: -80px auto -150px; + z-index: 1; + background: transparent url(../images/bg-ramp.jpg) center 171px no-repeat; +} + +div#no-downloads span.inner { + display: block; + position: relative; + height: 197px; + width: 550px; + background: transparent url(../images/download-sprite.png) 0 0 no-repeat; +} + +section#downloads a { + display: block; + position: relative; + height: 67px; + width: 275px; + padding-top: 130px; + background: transparent url(../images/download-sprite.png) 0 0 no-repeat; + text-align: center; + line-height: 1; + color: #fff; + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + font-weight: 800; + font-size: 1.3em; +} + +section#downloads a:hover { + text-decoration: none; +} + +section#downloads a em { + font: bold italic 12px/1 Georgia, Times, “Times New Roman”, serif; + color: #83b7da; + display: block; +} + +section#downloads a.zip { + float: left; + background-position: 0 0; +} + +section#downloads a.tgz { + float: right; + background-position: -275px 0; +} + +section#downloads a.zip:hover { + background-position: 0 -197px; +} + +section#downloads a.tgz:hover { + background-position: -275px -197px; +} + +span.banner-fix { + background: transparent url(../images/download-fallback-bg.png) center top no-repeat; + display: block; + height: 19px; + position: absolute; + width: 670px; + top: 19px; +} + +section#main_content { + z-index: 2; + padding: 20px 82px 0; + min-height:185px; +} + +/* footer */ + +footer { + background: transparent url(../images/footer-ramp.jpg) center -1px no-repeat; + padding-top: 104px; + margin: -94px auto 40px; + max-width: 560px; + text-align: center; +} + +footer span.ribbon-outer { + display: block; + position: relative; + border-bottom: 2px solid #bdb6ad; +} + +footer span.ribbon-inner { + position: relative; + display: block; + background-color: #cd596b; + border: 2px solid #7c334f; + padding: 1px; + z-index: 1; +} + +footer p { + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + font-weight: bold; + font-size: .8em; + color: #8b786f; +} + +footer a { + color: #cd596b; +} + +footer span.ribbon-inner p { + background-color: #7c334f; + margin: 0; + color: #e69b95; + font: bold italic 12px/1 Georgia, Times, “Times New Roman”, serif; + padding-bottom:4px; +} + +footer span.ribbon-inner a { + position: relative; + bottom: -1px; + color: #7eb0d2; + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + text-transform: uppercase; + font-style: normal; + font-weight: 800; + font-size: 1.2em; +} + +footer span.ribbon-inner a:hover { + color: #7eb0d2; +} + +footer span.left-tail, footer span.right-tail { + position: relative; + display: block; + width: 18px; + height: 29px; + background: transparent url(../images/small-ribbon-tail-sprite.png) 0 0 no-repeat; + position: absolute; + bottom: 5px; + z-index: 0; +} + +footer span.left-tail { + background-position: 0 0; + left: -11px; +} + +footer span.right-tail { + background-position: -18px 0; + right: -11px; +} + +footer span.octocat { + background: transparent url(../images/octocat.png) 0 0 no-repeat; + display: block; + width: 30px; + height: 30px; + margin: 0 auto; +} + +/* content */ + +body { + background: #ece4d8; + font: normal normal 15px/1.5 Georgia, Palatino,” Palatino Linotype”, Times, “Times New Roman”, serif; + color: #544943; + -webkit-font-smoothing: antialiased; +} + +a, a:hover { + color: #417090; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +h1,h2,h3,h4,h5,h6 { + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + font-weight: bold; +} + +p { + margin: .7em 0; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +ol { + margin: .7em 0; + list-style-type: decimal; + padding-left: 1.35em; +} + +ul { + margin: .7em 0; + padding-left: 1.35em; +} + +ul li { + padding-left: 10px; + background: transparent url(../images/chevron.png) left 6px no-repeat; +} + +blockquote { + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + margin: 20px 0; + color: #8b786f; + padding-left: 1.35em; + background: transparent url('../images/blockquote-gfx.png') 0 4px no-repeat; +} + +img { + -webkit-box-shadow: 0px 2px 0px #bdb6ad; + -moz-box-shadow: 0px 2px 0px #bdb6ad; + box-shadow: 0px 2px 0px #bdb6ad; + border: 2px solid #fff6e9; + max-width: 502px; +} + +hr { + border: none; + outline: none; + height: 18px; + background: transparent url('../images/hr.jpg') center center repeat-x; + margin: 0 0 20px; +} + +code { + background: #fff6e9; + font: normal normal .8em/1.7 "Lucida Sans Typewriter", "Lucida Console", Monaco, "Bitstream Vera Sans Mono", monospace; + padding: 0 5px 1px; +} + +pre { + margin: 10px 0 20px; + padding: .7em; + background: #fff6e9; + border-bottom: 2px solid #bdb6ad; + font: normal normal .9em/1.7 "Lucida Sans Typewriter", "Lucida Console", Monaco, "Bitstream Vera Sans Mono", monospace; + overflow: auto; +} + +pre code { + padding: 0; +} + +table { + background: #fff6e9; + display: table; + width: 100%; + border-collapse: separate; + border-bottom: 2px solid #bdb6ad; + margin: 10px 0; +} + +tr { + display: table-row; +} + +th { + display: table-cell; + padding: 2px 10px; + border: solid #ece4d8; + border-width: 0 2px 2px 0; + color: #cd596b; + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + font-weight: bold; + font-size: .85em; +} + +td { + display: table-cell; + padding: 0 .7em; + border: solid #ece4d8; + border-width: 0 2px 2px 0; +} + +td:last-child, th:last-child { + border-right: none; +} + +tr:last-child td { + border-bottom: none; +} + +dl { + margin: .7em 0 20px; +} + +dt { + font-family: 'Open Sans', Myriad, Calibri, sans-serif; + font-weight: bold; +} + +dd { + padding-left: 1.35em; +} + +dd p:first-child { + margin-top: 0; +} + +/* Content based headers */ + +#main_content > .header-level-1:first-child, +#main_content > .header-level-2:first-child, +#main_content > .header-level-3:first-child, +#main_content > .header-level-4:first-child, +#main_content > .header-level-5:first-child, +#main_content > .header-level-6:first-child { + margin-top: 0; +} + +.header-level-1 { + font-size: 1.85em; + border-bottom: .2em double #d3ccc1; + color: #7c334f; + text-align: center; + font-style: italic; + margin: 1.1em 0 .38em; + line-height: 1.2; + padding-bottom: 10px +} + +.header-level-2 { + font-size: 1.58em; + color: #7c334f; + margin: .95em 0 .5em; + border-bottom: .1em solid #D3CCC1; + line-height: 1.2; + padding-bottom: 10px +} + +.header-level-3 { + margin: 20px 0 10px; + font-size: 1.45em; +} + +.header-level-4 { + margin: .6em 0; + font-size: 1.2em; + color: #cd596b; +} + +.header-level-5 { + margin: .7em 0; + font-size: 1em; + color: #8b786f; +} + +.header-level-6 { + margin: .8em 0; + font-size: .85em; + font-style: italic; +} From 157e6285eb588a0fadcf4b780c240f714e2a3123 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 13:40:54 +0000 Subject: [PATCH 071/190] Move USAGE.txt out to the ttrts target and convert to header appropriately --- source/ttrts/main.cpp | 56 +--------------------------- source/ttrts/usage.h | 56 ++++++++++++++++++++++++++++ USAGE.txt => source/ttrts/usage.md | 59 +++++++++++++++--------------- 3 files changed, 87 insertions(+), 84 deletions(-) create mode 100644 source/ttrts/usage.h rename USAGE.txt => source/ttrts/usage.md (50%) diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index ccf47a0..cacc29f 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -7,60 +7,8 @@ #include "game.h" static const char* sk_usage = -"NAME\n" -"\tttrts - Tiny Terminal RTS\n" -"\n" -"SYNOPSYS\n" -"\tttrts [OPTIONS...] MAPFILE\n" -"\n" -"DESCRIPTION\n" -"\tttrts is a tiny terminal based RTS where that uses text\n" -"\tfiles as order lists to control it's units.\n" -"\n" -"\tThis means that any user, program or cat that can read\n" -"\tand write to text files can play the game.\n" -"\n" -"USAGE\n" -"\tWhen invoked, ttrts will set up a full game and output a\n" -"\tsingle file representing the current gamestate into a \n" -"\tlocal directory called ttrts_{GAME_NAME}.\n" -"\n" -"\tThis file can be read in and interpretted by human, robot\n" -"\tor cat. ttrts will wait for orders files to be placed in \n" -"\tit's current working directory.\n" -"\n" -"\tOnce orders have been set for each player taking part \n" -"\tttrts will calculate the new game state and output a new \n" -"\tgamestate file for the next turn.\n" -"\n" -"\tThis process repeats until a winner is chosen!\n" -"\n" -"OPTIONS\n" -"\tMAPFILE:\n" -"\t\tFile to read in the initial game state from\n" -"\n" -"GAMESTATE FILE FORMAT\n" -"\tName\n" -"\t Turn_{TURN_NUMBER}.txt\n" -"\tContents\n" -"\t ===== {GAME_NAME} =====\n" -"\t SIZE:[{X},{Y}]\n" -"\t TURN:{TURN_NUMBER}\n" -"\t ~~~~\n" -"\t UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]\n" -"\t ...\n" -"\n" -"ORDER FILE FORMAT\n" -"\tName\n" -"\t Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" -"\tContents\n" -"\t ORDER:{ORDER_CHAR} id:{UNIT_ID}\n" -"\t ...\n" -"\n" -"ORDERS\n" -"\tF - Move unit forward one space\n" -"\tL/R - Rotate unit left or right\n" -"\tA - Attack space in front of unit\n"; +#include "usage.h" +; // time for waiting between file stats static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100); diff --git a/source/ttrts/usage.h b/source/ttrts/usage.h new file mode 100644 index 0000000..3a94c8d --- /dev/null +++ b/source/ttrts/usage.h @@ -0,0 +1,56 @@ +"NAME\n" +"\tttrts - Tiny Terminal RTS\n" +"\n" +"SYNOPSYS\n" +"\tttrts [OPTIONS...] MAPFILE\n" +"\n" +"DESCRIPTION\n" +"\tttrts is a tiny terminal based RTS where that uses text\n" +"\tfiles as order lists to control it's units.\n" +"\n" +"\tThis means that any user, program or cat that can read\n" +"\tand write to text files can play the game.\n" +"\n" +"USAGE\n" +"\tWhen invoked, ttrts will set up a full game and output a\n" +"\tsingle file representing the current gamestate into a \n" +"\tlocal directory called `ttrts_{GAME_NAME}`.\n" +"\n" +"\tThis file can be read in and interpretted by human, robot\n" +"\tor cat. ttrts will wait for orders files to be placed in \n" +"\tit's current working directory.\n" +"\n" +"\tOnce orders have been set for each player taking part \n" +"\tttrts will calculate the new game state and output a new \n" +"\tgamestate file for the next turn.\n" +"\n" +"\tThis process repeats until a winner is chosen!\n" +"\n" +"OPTIONS\n" +"\tMAPFILE - File to read in the initial game state from\n" +"\n" +"-------------------------------------------------------------------------------\n" +"\n" +"GAMESTATE FILE FORMAT\n" +"Name\n" +"\tTurn_{TURN_NUMBER}.txt\n" +"Contents\n" +"\t===== {GAME_NAME} =====\n" +"\tSIZE:[{X},{Y}]\n" +"\tTURN:{TURN_NUMBER}\n" +"\t~~~~\n" +"\tUNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]\n" +"\t...\n" +"\n" +"ORDER FILE FORMAT\n" +"Name\n" +"\tTurn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" +"Contents\n" +"\tORDER:{ORDER_CHAR} id:{UNIT_ID}\n" +"\t...\n" +"\n" +"Orders\n" +"\tF - Move unit forward one space\n" +"\tL/R - Rotate unit left or right\n" +"\tA - Attack row in front of unit\n" +"\t\n" diff --git a/USAGE.txt b/source/ttrts/usage.md similarity index 50% rename from USAGE.txt rename to source/ttrts/usage.md index c3bd574..007cac2 100644 --- a/USAGE.txt +++ b/source/ttrts/usage.md @@ -1,23 +1,20 @@ -To convert this file to c++ syntax use -$ cat USAGE.txt | sed 's/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n\n/g' | sed 's/^/"/' | sed 's/$/"/' - -NAME +## NAME ttrts - Tiny Terminal RTS -SYNOPSYS +## SYNOPSYS ttrts [OPTIONS...] MAPFILE -DESCRIPTION +## DESCRIPTION ttrts is a tiny terminal based RTS where that uses text files as order lists to control it's units. This means that any user, program or cat that can read and write to text files can play the game. -USAGE +## USAGE When invoked, ttrts will set up a full game and output a single file representing the current gamestate into a - local directory called ttrts_{GAME_NAME}. + local directory called `ttrts_{GAME_NAME}`. This file can be read in and interpretted by human, robot or cat. ttrts will wait for orders files to be placed in @@ -29,30 +26,32 @@ USAGE This process repeats until a winner is chosen! -OPTIONS - MAPFILE: - File to read in the initial game state from +## OPTIONS + MAPFILE - File to read in the initial game state from -GAMESTATE FILE FORMAT - Name - Turn_{TURN_NUMBER}.txt - Contents - ===== {GAME_NAME} ===== - SIZE:[{X},{Y}] - TURN:{TURN_NUMBER} - ~~~~ - UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}] - ... +------------------------------------------------------------------------------- -ORDER FILE FORMAT - Name - Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt - Contents - ORDER:{ORDER_CHAR} id:{UNIT_ID} - ... +## GAMESTATE FILE FORMAT +### Name + Turn_{TURN_NUMBER}.txt +### Contents + ===== {GAME_NAME} ===== + SIZE:[{X},{Y}] + TURN:{TURN_NUMBER} + ~~~~ + UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}] + ... -ORDERS +## ORDER FILE FORMAT +### Name + Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt +### Contents + ORDER:{ORDER_CHAR} id:{UNIT_ID} + ... + +### Orders F - Move unit forward one space L/R - Rotate unit left or right - A - Attack space in front of unit - + A - Attack row in front of unit + +`$ cat usage.md | sed 's/^#* //g' | sed 's/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n\n/g' | sed 's/^/"/' | sed 's/$/"/' | sed '$ d' > usage.h # To convert this file to c++` \ No newline at end of file From 5a56a400c56e607f32049f2e7bb0ba6e66963d6b Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 13:58:21 +0000 Subject: [PATCH 072/190] Even more cleanup and removal of legacy code --- README.md | 58 +++++----------------------- source/README.md | 27 +++++++++++++ source/game/README.md | 19 +-------- source/net/CMakeLists.txt | 0 source/ttrts/{usage.md => README.md} | 2 +- source/ui/CMakeLists.txt | 19 --------- source/ui/board.cpp | 43 --------------------- source/ui/board.h | 54 -------------------------- 8 files changed, 40 insertions(+), 182 deletions(-) create mode 100644 source/README.md delete mode 100644 source/net/CMakeLists.txt rename source/ttrts/{usage.md => README.md} (89%) delete mode 100644 source/ui/CMakeLists.txt delete mode 100644 source/ui/board.cpp delete mode 100644 source/ui/board.h diff --git a/README.md b/README.md index 4c2c2a1..361a30c 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,17 @@ # TTRTS *The Tiny Terminal RTS where the players write their AIs* ------------------------------------------------------------------------------------- +------------------------------------------------------------------------------- ## Introduction -We aim to create a simple terminal based rts where a user can program an AI to control their army +A simple terminal based RTS game that uses txt files to communicate game state and unit commands. TTRTS was ------------------------------------------------------------------------------------- +------------------------------------------------------------------------------- ## Gameplay -1. ttrts clients are run from the command line -2. ttrts-server is launched from the command line -3. clients will connect to server and confirm initial board state - 1. clients output a text file with game data for this turn - 2. a player, or program, reads the game data file and outputs an instructions file - 3. clients read the instructions file, simulates the turn - 4. game state is verified between clients and server - 5. repeat until an end state is reached -4. once game is finished, host and clients disconnect and a winner is notified - -*see [game](game) for full game rules* - ------------------------------------------------------------------------------------- -## Source - - -### Targets -##### ttrts -Main TTRTS executable , runs from the command line and acts as client - -##### ttrts-server -TTRTS server executable, runs from the command line acting as server - -##### player -Custom player AI code, this should contain examples and test code to help newcomers begin their journey - -##### ttrts-test -Test executable, to be compiled and run to test various functionality - -### Libraries -##### game -Implementation of the RTS rules and simulation. [game](game) has full information on it's implementation. - -##### net -Net code for hosting the server and communicating with clients - -##### ui -Wrapper for user interface for the terminal, this only really needs three stages -* Initialise the game with settings and connect the clients -* Run the game simulation to it's conclusion -* Display the game result -* ASCII Colour wrapper for separate teams - -##### maths -simple maths library for 2D calculations and types +1. The ttrts client is run from the command line with initial parameters +2. The client outputs a gamestate text file +3. A player, program or cat reads the state and outputs instructions for their units. +4. The client reads in instructions and processes the turn +5. If no winner is reached, skip back to step 2 +6. The game client outputs a final summary file with the winner +*see [game](game) for full game rules* \ No newline at end of file diff --git a/source/README.md b/source/README.md new file mode 100644 index 0000000..0a16888 --- /dev/null +++ b/source/README.md @@ -0,0 +1,27 @@ +# Targets +### ttrts +Main TTRTS executable , runs from the command line and acts as client + +### ttrts-test +Test executable, to be compiled and run to test various functionality + +### ttrts-gen +Binary to generate map example map files + +# Libraries +### game +Implementation of the RTS rules and simulation. [game](game) has full information on it's implementation. + +### net +Net code for hosting the server and communicating with clients + +### ui +Wrapper for user interface for the terminal, this only really needs three stages +* Initialise the game with settings and connect the clients +* Run the game simulation to it's conclusion +* Display the game result +* ASCII Colour wrapper for separate teams + +### maths +simple maths library for 2D calculations and types + diff --git a/source/game/README.md b/source/game/README.md index d8bf38c..b1846b3 100644 --- a/source/game/README.md +++ b/source/game/README.md @@ -1,4 +1,4 @@ -Game Design +TTRTS Gameplay ================= The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board. @@ -11,22 +11,7 @@ The engine then takes all commands, evaluates all movement first simultaneously, All attempted movement to the same square by two or more units will fail. -Friendly fire is enabled by default +Friendly fire is enabled by default. --------------------------------------------------------- -Units ------ -Currently only one unit, this will be expanded in the future. - -Units have a set of properties, and commands than can be issued. - -All units take one hit to kill. - -##### properties -See [the unit header](unit.h) for full details on unit properties - -##### orders -Commands take the form of a single char literal. -See [the order header](order.h) for details on the orders diff --git a/source/net/CMakeLists.txt b/source/net/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 diff --git a/source/ttrts/usage.md b/source/ttrts/README.md similarity index 89% rename from source/ttrts/usage.md rename to source/ttrts/README.md index 007cac2..f00384b 100644 --- a/source/ttrts/usage.md +++ b/source/ttrts/README.md @@ -54,4 +54,4 @@ L/R - Rotate unit left or right A - Attack row in front of unit -`$ cat usage.md | sed 's/^#* //g' | sed 's/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n\n/g' | sed 's/^/"/' | sed 's/$/"/' | sed '$ d' > usage.h # To convert this file to c++` \ No newline at end of file +`$ cat README.md | sed 's/^#* //g' | sed 's/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n\n/g' | sed 's/^/"/' | sed 's/$/"/' | sed '$ d' > usage.h # To convert this file to c++` \ No newline at end of file diff --git a/source/ui/CMakeLists.txt b/source/ui/CMakeLists.txt deleted file mode 100644 index 8b508ff..0000000 --- a/source/ui/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 2.8.7) - -# game project -project( ui ) - -include_directories( - ../maths - ../game -) - -# Set to use c++11, because we're cool like that -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11" ) - -# Add the sources -set( SOURCES - board.cpp -) - -add_library( ui ${SOURCES} ) \ No newline at end of file diff --git a/source/ui/board.cpp b/source/ui/board.cpp deleted file mode 100644 index 39a4aa5..0000000 --- a/source/ui/board.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "board.h" - -// ---------------------------------------------- - -// Default constructor for the board -CBoard::CBoard( unsigned int c, unsigned int r ) -: cols ( c ) -, rows ( r ) -, total ( rows * cols ) -{ - board.resize(total,square_empty); -} - - -// constructor -CBoard::CBoard( unsigned int c, unsigned int r, vunitVis_c&& b ) -: cols ( c ) -, rows ( r ) -, total ( rows * cols ) -, board ( std::move(b) ) -{ - board.resize(total,square_empty); -} - -// print get a slot on the board -unitVis_c CBoard::get( const unsigned int c, const unsigned int r ) const -{ - if ( (r >= rows) || (c >= cols) ) - return square_invalid; - - return board[r*c]; -} - -// Get a square on the board -unitVis_c CBoard::set( const unsigned int c, const unsigned int r , const unitVis_c n ) -{ - if ( (r >= rows) || (c >= cols) ) - return square_invalid; - - unitVis_c old = board[r*c]; - board[r*c] = n; - return old; -} \ No newline at end of file diff --git a/source/ui/board.h b/source/ui/board.h deleted file mode 100644 index 38703a4..0000000 --- a/source/ui/board.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef _BOARD_H_ -#define _BOARD_H_ - -#include "gametypes.h" -#include "mathtypes.h" - -#include // std::numeric_limits -#include // std::vector - -typedef std::vector< unitVis_c > vunitVis_c; - -// Invalid value for the board square -constexpr unitVis_c square_invalid = std::numeric_limits::max(); -constexpr unitVis_c square_empty = ' '; - -// Class to store simple data about a board -class CBoard -{ -public: - - const unsigned int cols; // Number of columns - const unsigned int rows; // Number of rows - const unsigned int total; // Total number of pieces - - // constructor - CBoard( unsigned int c, unsigned int r ); - - // constructor - CBoard( unsigned int c, unsigned int r, vunitVis_c&& b ); - - // Default destructor - ~CBoard() = default; - - // clear the board - inline void clear() { fill(square_empty); } - - // fill the board - inline void fill(unitVis_c v) { std::fill(board.begin(),board.end(),v); }; - - // Get a square on the board - unitVis_c get( const unsigned int c, const unsigned int r ) const; - - // Get the full board - inline const vunitVis_c& get() const { return board; }; - - // Get a square on the board - unitVis_c set( const unsigned int c, const unsigned int r , const unitVis_c n ); - -private: - - vunitVis_c board; // Board data storage -}; - -#endif //_BOARD_H_ \ No newline at end of file From e08b8190bb423b4299591473978a47763c50c67b Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 13:59:19 +0000 Subject: [PATCH 073/190] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 361a30c..831d12b 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,4 @@ A simple terminal based RTS game that uses txt files to communicate game state a 5. If no winner is reached, skip back to step 2 6. The game client outputs a final summary file with the winner -*see [game](game) for full game rules* \ No newline at end of file +*see [game](source/game) for full game rules* From 37f95607b0588ec096609ec92dc06f136fdcffab Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 14:01:03 +0000 Subject: [PATCH 074/190] Update CMakeLists.txt --- source/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index f0e2055..abba789 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -16,7 +16,6 @@ endif() # Subprojects add_subdirectory( ttrts ) add_subdirectory( game ) -add_subdirectory( ui ) # Auxhilary binaries add_subdirectory( test ) From fed4c4a054caec3271208c8b1c58b08c86ed6fe9 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 14:01:54 +0000 Subject: [PATCH 075/190] Update test.cpp --- source/test/test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/test/test.cpp b/source/test/test.cpp index 0231552..1c8f38d 100644 --- a/source/test/test.cpp +++ b/source/test/test.cpp @@ -1,6 +1,5 @@ #include // std::cout -#include "board.h" #include "order.h" #include "game.h" From c1bd6eb3683ecc8a5a9b8b172e1bded9adb5c036 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 14:02:05 +0000 Subject: [PATCH 076/190] Update CMakeLists.txt --- source/test/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/test/CMakeLists.txt b/source/test/CMakeLists.txt index c212c3a..2ff0923 100644 --- a/source/test/CMakeLists.txt +++ b/source/test/CMakeLists.txt @@ -6,7 +6,6 @@ project( ttrts-test ) include_directories( ../game ../maths - ../ui ) set( SOURCES @@ -16,4 +15,4 @@ set( SOURCES # Add the executable add_executable( ttrts-test ${SOURCES} ) -target_link_libraries( ttrts-test game ui ) \ No newline at end of file +target_link_libraries( ttrts-test game ui ) From 30cbdff1f517358f9e18b90488bec8f674dbc83f Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 14:02:12 +0000 Subject: [PATCH 077/190] Update CMakeLists.txt --- source/test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/test/CMakeLists.txt b/source/test/CMakeLists.txt index 2ff0923..3779ca8 100644 --- a/source/test/CMakeLists.txt +++ b/source/test/CMakeLists.txt @@ -15,4 +15,4 @@ set( SOURCES # Add the executable add_executable( ttrts-test ${SOURCES} ) -target_link_libraries( ttrts-test game ui ) +target_link_libraries( ttrts-test game ) From a7bc45497aee86c9e4a66c64f04cedc2e4fee6e2 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 18 Dec 2014 14:03:01 +0000 Subject: [PATCH 078/190] Update README.md --- source/README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/source/README.md b/source/README.md index 0a16888..f793144 100644 --- a/source/README.md +++ b/source/README.md @@ -10,17 +10,7 @@ Binary to generate map example map files # Libraries ### game -Implementation of the RTS rules and simulation. [game](game) has full information on it's implementation. - -### net -Net code for hosting the server and communicating with clients - -### ui -Wrapper for user interface for the terminal, this only really needs three stages -* Initialise the game with settings and connect the clients -* Run the game simulation to it's conclusion -* Display the game result -* ASCII Colour wrapper for separate teams +Implementation of the RTS rules and simulation. ### maths simple maths library for 2D calculations and types From 71d00849089d97fba886b518af59d6942e03d9ca Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 15:35:16 +0000 Subject: [PATCH 079/190] Use a pre-build script to generate usage.h from the README.md Use a generated header from README.md Finalise git ignore file --- .gitignore | 11 ++++++-- source/scripts/gen_usage.sh | 9 ++++++ source/ttrts/CMakeLists.txt | 15 ++++++++-- source/ttrts/README.md | 10 +++---- source/ttrts/usage.h | 56 ------------------------------------- 5 files changed, 35 insertions(+), 66 deletions(-) create mode 100755 source/scripts/gen_usage.sh delete mode 100644 source/ttrts/usage.h diff --git a/.gitignore b/.gitignore index 030061a..379896d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,12 @@ -build/ -maps/ +# Build directory +/build/ + +# Built binary and maps +/maps/ +/ttrts + +# user files *.user *.sublime* *.idea + diff --git a/source/scripts/gen_usage.sh b/source/scripts/gen_usage.sh new file mode 100755 index 0000000..a183af7 --- /dev/null +++ b/source/scripts/gen_usage.sh @@ -0,0 +1,9 @@ +#! /bin/bash +# Used to generate usage text from markdown +cat README.md \ + | sed 's/^#* //g' \ + | sed 's/\t/\\t/g' \ + | sed ':a;N;$!ba;s/\n/\\n\n/g' \ + | sed 's/^/\"/' \ + | sed 's/$/\"/' \ + > $1 diff --git a/source/ttrts/CMakeLists.txt b/source/ttrts/CMakeLists.txt index 08af9fb..2a49815 100644 --- a/source/ttrts/CMakeLists.txt +++ b/source/ttrts/CMakeLists.txt @@ -3,6 +3,7 @@ project( ttrts ) include_directories( + ${CMAKE_CURRENT_BINARY_DIR} ../maths ../game ) @@ -13,6 +14,16 @@ set( SOURCES ) # Add the executable -add_executable( ttrts ${SOURCES} ) +add_executable( ${PROJECT_NAME} ${SOURCES} ) -target_link_libraries( ttrts game ) \ No newline at end of file +target_link_libraries( ${PROJECT_NAME} game ) + +# Installation target +install( TARGETS ${PROJECT_NAME} DESTINATION bin ) + +# Run the gen_usage script to generate our usage header +add_custom_target( + gen_ttrts_usage + cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_SOURCE_DIR}/scripts/gen_usage.sh "${CMAKE_CURRENT_BINARY_DIR}/usage.h" +) +add_dependencies(${PROJECT_NAME} gen_ttrts_usage) diff --git a/source/ttrts/README.md b/source/ttrts/README.md index f00384b..2579dc5 100644 --- a/source/ttrts/README.md +++ b/source/ttrts/README.md @@ -2,7 +2,7 @@ ttrts - Tiny Terminal RTS ## SYNOPSYS - ttrts [OPTIONS...] MAPFILE + ttrts MAPFILE ## DESCRIPTION ttrts is a tiny terminal based RTS where that uses text @@ -50,8 +50,6 @@ ... ### Orders - F - Move unit forward one space - L/R - Rotate unit left or right - A - Attack row in front of unit - -`$ cat README.md | sed 's/^#* //g' | sed 's/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n\n/g' | sed 's/^/"/' | sed 's/$/"/' | sed '$ d' > usage.h # To convert this file to c++` \ No newline at end of file + F - move unit [F]orward one space + L/R - rotate unit [L]eft or [R]ight + A - [A]ttack in straight line in front of unit \ No newline at end of file diff --git a/source/ttrts/usage.h b/source/ttrts/usage.h deleted file mode 100644 index 3a94c8d..0000000 --- a/source/ttrts/usage.h +++ /dev/null @@ -1,56 +0,0 @@ -"NAME\n" -"\tttrts - Tiny Terminal RTS\n" -"\n" -"SYNOPSYS\n" -"\tttrts [OPTIONS...] MAPFILE\n" -"\n" -"DESCRIPTION\n" -"\tttrts is a tiny terminal based RTS where that uses text\n" -"\tfiles as order lists to control it's units.\n" -"\n" -"\tThis means that any user, program or cat that can read\n" -"\tand write to text files can play the game.\n" -"\n" -"USAGE\n" -"\tWhen invoked, ttrts will set up a full game and output a\n" -"\tsingle file representing the current gamestate into a \n" -"\tlocal directory called `ttrts_{GAME_NAME}`.\n" -"\n" -"\tThis file can be read in and interpretted by human, robot\n" -"\tor cat. ttrts will wait for orders files to be placed in \n" -"\tit's current working directory.\n" -"\n" -"\tOnce orders have been set for each player taking part \n" -"\tttrts will calculate the new game state and output a new \n" -"\tgamestate file for the next turn.\n" -"\n" -"\tThis process repeats until a winner is chosen!\n" -"\n" -"OPTIONS\n" -"\tMAPFILE - File to read in the initial game state from\n" -"\n" -"-------------------------------------------------------------------------------\n" -"\n" -"GAMESTATE FILE FORMAT\n" -"Name\n" -"\tTurn_{TURN_NUMBER}.txt\n" -"Contents\n" -"\t===== {GAME_NAME} =====\n" -"\tSIZE:[{X},{Y}]\n" -"\tTURN:{TURN_NUMBER}\n" -"\t~~~~\n" -"\tUNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]\n" -"\t...\n" -"\n" -"ORDER FILE FORMAT\n" -"Name\n" -"\tTurn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n" -"Contents\n" -"\tORDER:{ORDER_CHAR} id:{UNIT_ID}\n" -"\t...\n" -"\n" -"Orders\n" -"\tF - Move unit forward one space\n" -"\tL/R - Rotate unit left or right\n" -"\tA - Attack row in front of unit\n" -"\t\n" From f4109e7b369e3108cb502f092bbf7524b40332d3 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 15:35:17 +0000 Subject: [PATCH 080/190] Adjust some output --- source/ttrts/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index cacc29f..cbdbca7 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -46,6 +46,8 @@ int main(int argc, char* argv[]) return 1; } + std::cout<<"Launching TTRTS!"< Date: Sat, 20 Dec 2014 15:35:18 +0000 Subject: [PATCH 081/190] Fix some bugs Fix a case where game folder existed in verious forms Fix bug where game would not correctly end at draw state Fix bug where the team order file was looked for in the wrong directory Extract gamestate function and properly handle game ending scenarios Stop hitting yourselves Fixed bug where units would not move forward because they were hitting themselves --- source/game/game.cpp | 5 ++- source/ttrts/main.cpp | 89 ++++++++++++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/source/game/game.cpp b/source/game/game.cpp index 0608415..3608f70 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -144,7 +144,10 @@ int CTTRTSGame::SimulateToNextTurn() // If any unit is in this spot, or moving unit moving to said spot, reject this for ( const OrderUnitPair& pair2 : m_OrderUnitPairs ) { - if( GetNewPosition(pair2) != newpos ) + // Skip myself + if( pair.unit.getID() == pair2.unit.getID() ) continue; + + if( GetNewPosition(pair2) == newpos ) { possible = false; break; diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index cbdbca7..7ecc424 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "game.h" @@ -26,6 +27,25 @@ inline void WaitForFile( const std::string& name, const std::chrono::millisecond while( !FileExists(name) ) std::this_thread::sleep_for(time); } +bool OutputGameStateFile(CTTRTSGame &game, std::string &gameDir) +{ + char turnFileName[128]; + snprintf(turnFileName,128,"%s/Turn_%i.txt",gameDir.c_str(),game.GetTurn()); + std::ofstream turnFile(turnFileName, std::ios_base::trunc); // truncate to overwrite if a file exists + + if ( turnFile.bad() ) + { + return false; + } + + // Output the turn description + std::string turnDescriptor = game.GetStateAsString(); + turnFile<>input; + if( !input.size() || std::tolower(input[0]) != 'y' ) + { + std::cerr<<"Aborting..."< 0 ) // We have no units left + { + std::cout<<"Starting turn "< Date: Sat, 20 Dec 2014 15:35:18 +0000 Subject: [PATCH 082/190] Reorder team colours to match with bash colour codes --- source/game/gametypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/game/gametypes.h b/source/game/gametypes.h index c3ce010..43cc4d3 100644 --- a/source/game/gametypes.h +++ b/source/game/gametypes.h @@ -7,9 +7,9 @@ enum class Team : char { Red = 0, - Blue, Green, Yellow, + Blue, NUM_INVALID }; From b37fe327f2346a6b02dd7a1126065608d6d25a87 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 15:35:19 +0000 Subject: [PATCH 083/190] Re-do generation now that attack rule has changed --- source/gen/gen.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/source/gen/gen.cpp b/source/gen/gen.cpp index 6dd2d38..fd85b3c 100644 --- a/source/gen/gen.cpp +++ b/source/gen/gen.cpp @@ -25,28 +25,27 @@ int main() { // Tiny 2v2 Game { - CTTRTSGame game(7, 5); + CTTRTSGame game(6, 6); game.SetName("Tiny2v2"); - AddUnitToGame( Team::Blue, '>', uvector2(1, 1), game); - AddUnitToGame( Team::Blue, '>', uvector2(1, 3), game); - - AddUnitToGame( Team::Red, '<', uvector2(5, 1), game); - AddUnitToGame( Team::Red, '<', uvector2(5, 3), game); + AddUnitToGame( Team::Red, '<', uvector2(4, 2), game); + AddUnitToGame( Team::Red, '<', uvector2(4, 4), game); + AddUnitToGame( Team::Green, '>', uvector2(1, 1), game); + AddUnitToGame( Team::Green, '>', uvector2(1, 3), game); OutputGame(std::move(game)); } // Basic 5v5 game { - CTTRTSGame game(21, 11); - game.SetName("Basic5v5"); + CTTRTSGame game(20, 12); + game.SetName("Big2v2"); + for ( ucoord_t y : { 2,4,6,8,10 } ) + AddUnitToGame( Team::Red, '<', uvector2(18, y), game); for ( ucoord_t y : { 1,3,5,7,9 } ) - AddUnitToGame( Team::Blue, '>', uvector2(1, y), game); + AddUnitToGame( Team::Green, '>', uvector2(1, y), game); - for ( ucoord_t y : { 1,3,5,7,9 } ) - AddUnitToGame( Team::Red, '<', uvector2(19, y), game); OutputGame(std::move(game)); } @@ -56,16 +55,16 @@ int main() CTTRTSGame game(8, 8); game.SetName("Chess"); - for ( ucoord_t y : { 0,1,2,3,4,5,6,7 } ) { - AddUnitToGame(Team::Blue, '>', uvector2(0, y), game); - AddUnitToGame(Team::Blue, '>', uvector2(1, y), game); - } - - for ( ucoord_t y : { 0,1,2,3,4,5,6,7 } ) { + for ( ucoord_t y : { 1,3,5,7 } ) { AddUnitToGame(Team::Red, '<', uvector2(6, y), game); AddUnitToGame(Team::Red, '<', uvector2(7, y), game); } + for ( ucoord_t y : { 0,2,4,6 } ) { + AddUnitToGame(Team::Green, '>', uvector2(0, y), game); + AddUnitToGame(Team::Green, '>', uvector2(1, y), game); + } + OutputGame(std::move(game)); } } \ No newline at end of file From 4edca836593c4ea2fbe2a95a2b9d8200a2a936d7 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 15:35:19 +0000 Subject: [PATCH 084/190] Add a Random player, simply does random commands on each turn Remove simplePlayer in favor of new randomPlayer, which uses new ttrts.pm module --- players/perl/ttrts.pm | 179 ++++++++++++++++++++++++++++++++++++++++ players/randomPlayer.pl | 104 +++++++++++++++++++++++ players/simplePlayer.pl | 131 ----------------------------- 3 files changed, 283 insertions(+), 131 deletions(-) create mode 100644 players/perl/ttrts.pm create mode 100755 players/randomPlayer.pl delete mode 100755 players/simplePlayer.pl diff --git a/players/perl/ttrts.pm b/players/perl/ttrts.pm new file mode 100644 index 0000000..7e9fdbf --- /dev/null +++ b/players/perl/ttrts.pm @@ -0,0 +1,179 @@ +#! /usr/bin/perl +use strict; +use warnings; + +# Get information about a unit from it's descriptor +sub getUnit +{ + return ($_[0] =~ /UNIT:(\d+) tm:(\d+) vs:([^ ]+) dr:([^ ]+) ps:\[(\d+),(\d+)\]/); +} + +# Get the units from a turn file +sub GetUnitsForTurn +{ + my $turnFile = shift; + + # Open the turn file + open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; + + # Skip the header information + my $headerLine = <$TURNHANDLE>; + my $sizeLine = <$TURNHANDLE>; + my $turnLine = <$TURNHANDLE>; + ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; + + my @units; + while( my $unitLine = <$TURNHANDLE> ) + { + chomp $unitLine; + push(@units,$unitLine); + } + + close $TURNHANDLE; + + return @units; +} + +# Get information from the header for this turn +sub GetHeaderForTurn +{ + my $turnFile = shift; + + # Open the turn file + open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; + + # Pull in the header information + my $headerLine = <$TURNHANDLE>; + chomp $headerLine; + my $sizeLine = <$TURNHANDLE>; + chomp $sizeLine; + my $turnLine = <$TURNHANDLE>; + chomp $turnLine; + + my ($gameName) = ( $headerLine =~ /===== ([^ ]+) =====/ ); + my ($gameX,$gameY) = ( $sizeLine =~ /SIZE:\[(\d+),(\d+)\]/ ); + + ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; + + close $TURNHANDLE; + + return ($gameName,$gameX,$gameY); +} + +# Get units from a specific team +sub getUnitsOnTeam +{ + my $theTeam = shift; + my @allUnits = @_; + my @myUnits; + + for my $unit (@allUnits) + { + my ($unitTeam) = $unit =~ /tm:(\d+)/; + if ( $unitTeam == $theTeam ) + { + push(@myUnits,$unit); + } + } + + return @myUnits; +} + +sub GetTurnFile +{ + my $turn = shift; + my $turnFile = "Turn_TURN.txt"; + $turnFile =~ s/TURN/$turn/; + return $turnFile; +} + +sub GetCommandFile +{ + my $turn = shift; + my $team = shift; + my $cmdFileName = "Turn_TURN_Team_TEAM.txt"; + $cmdFileName =~ s/TURN/$turn/; + $cmdFileName =~ s/TEAM/$team/; + return $cmdFileName; +} + +# Output the commands file +sub OutputCommandsFile +{ + my $turn = shift; + my $team = shift; + my $commands = shift; + + # Get output file + our $cmdFileName = GetCommandFile($turn,$team); + + if (! -e $cmdFileName) + { + open(my $cmdFile, '>', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; + print $cmdFile $commands; + close $cmdFile; + + printf "Outputted $cmdFileName\n"; + printf "$commands"; + } + else + { + open(my $cmdFile, '<', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; + my $old_commands = do { <$cmdFile> }; + + printf "Replaying $cmdFileName\n"; + printf "$old_commands"; + } +} + +# Print a game map +sub PrintGameMap +{ + my $gameX = shift; + my $gameY = shift; + my @units = @_; + + my @map; + + # Fill with blanks + for my $x (0 .. $gameX-1) + { + for my $y (0 .. $gameY-1) + { + $map[$x][$y] = "-"; + } + } + + # Fill with units + for my $unit (@units) + { + my ($id,$tm,$vs,$dr,$psx,$psy) = getUnit($unit); + + $tm += 31; + $vs = "\e[".$tm."m".$vs."\e[0m"; + + $map[$psx][$psy] = $vs; + } + + # Print whole map bottom left is 0,0 + for my $y ( reverse 0 .. $gameY-1 ) + { + for my $x (0 .. $gameX-1) + { + printf($map[$x][$y]); + } + printf("\n"); + } +} + +# Wait for a file to exist +sub WaitForFile +{ + my $file = $_[0]; + while( ! -e $file ) + { + select(undef, undef, undef, 0.01); + } +} + +return 1; \ No newline at end of file diff --git a/players/randomPlayer.pl b/players/randomPlayer.pl new file mode 100755 index 0000000..14d6338 --- /dev/null +++ b/players/randomPlayer.pl @@ -0,0 +1,104 @@ +#! /usr/bin/perl +use strict; +use warnings; +use Term::ANSIColor; + +# From http://perlmaven.com/how-to-create-a-perl-module-for-code-reuse +# expect ttrts perl module in a neighboring perl/ dir +use File::Basename qw(dirname); +use Cwd qw(abs_path); +use lib dirname(abs_path($0))."/perl"; + +# Use our ttrts perl library +use ttrts; + +our $usage_text=<; - chomp $headerLine; - my $sizeLine = <$TURNHANDLE>; - chomp $sizeLine; - my $turnLine = <$TURNHANDLE>; - chomp $turnLine; - ( <$TURNHANDLE> =~ m/~~~~/ ) or die "Gamestate file did not match expected format"; - - my @units; - while( my $unitLine = <$TURNHANDLE> ) - { - chomp $unitLine; - push(@units,$unitLine); - } - - return @units; -} - -# Output the commands file -sub OutputCommandsFile -{ - my $commands = $_[0]; - - # Get output file - our $orderFile = "Turn_TURN_Team_TEAM.txt"; - $orderFile =~ s/TURN/$turn/; - $orderFile =~ s/TEAM/$team/; - - system ("echo $commands > $orderFile"); -} - -# Sort units into teams -sub getUnitsOnTeam -{ - my $theTeam = shift; - my @allUnits = @_;; - my @myUnits; - - for my $unit (@allUnits) - { - my ($unitTeam) = $unit =~ /tm:(\d+)/; - if ( $unitTeam == $theTeam ) - { - push(@myUnits,$unit); - } - } - - return @myUnits; -} - -# Get commands for a turn -sub GetCommandsForTurn -{ - my @units = @_; - my @myUnits = getUnitsOnTeam($team,@units); - - # perform AI here - - return ""; -} - -# Show launch params -printf("Launching with team %i\n",$team); - -# Stay looping the AI -while ( 1 ) -{ - # Wait for turn file - our $turnFile = "Turn_TURN.txt"; - $turnFile =~ s/TURN/$turn/; - - # Wait for the turn file - WaitForFile $turnFile; - - # Read in the game state from turnFile - my @units = GetUnitsForTurn($turnFile); - - # Generate some commands - my $commands = GetCommandsForTurn @units; - - OutputCommandsFile $commands; - - $turn++; - -} \ No newline at end of file From 469e3ffa8a775e8a6dd731b99a37582b521be9e4 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 15:35:19 +0000 Subject: [PATCH 085/190] Add a Random player, simply does random commands on each turn Remove simplePlayer in favor of new randomPlayer, which uses new ttrts.pm module --- ttrts.pm | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 ttrts.pm diff --git a/ttrts.pm b/ttrts.pm new file mode 100644 index 0000000..7e9fdbf --- /dev/null +++ b/ttrts.pm @@ -0,0 +1,179 @@ +#! /usr/bin/perl +use strict; +use warnings; + +# Get information about a unit from it's descriptor +sub getUnit +{ + return ($_[0] =~ /UNIT:(\d+) tm:(\d+) vs:([^ ]+) dr:([^ ]+) ps:\[(\d+),(\d+)\]/); +} + +# Get the units from a turn file +sub GetUnitsForTurn +{ + my $turnFile = shift; + + # Open the turn file + open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; + + # Skip the header information + my $headerLine = <$TURNHANDLE>; + my $sizeLine = <$TURNHANDLE>; + my $turnLine = <$TURNHANDLE>; + ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; + + my @units; + while( my $unitLine = <$TURNHANDLE> ) + { + chomp $unitLine; + push(@units,$unitLine); + } + + close $TURNHANDLE; + + return @units; +} + +# Get information from the header for this turn +sub GetHeaderForTurn +{ + my $turnFile = shift; + + # Open the turn file + open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; + + # Pull in the header information + my $headerLine = <$TURNHANDLE>; + chomp $headerLine; + my $sizeLine = <$TURNHANDLE>; + chomp $sizeLine; + my $turnLine = <$TURNHANDLE>; + chomp $turnLine; + + my ($gameName) = ( $headerLine =~ /===== ([^ ]+) =====/ ); + my ($gameX,$gameY) = ( $sizeLine =~ /SIZE:\[(\d+),(\d+)\]/ ); + + ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; + + close $TURNHANDLE; + + return ($gameName,$gameX,$gameY); +} + +# Get units from a specific team +sub getUnitsOnTeam +{ + my $theTeam = shift; + my @allUnits = @_; + my @myUnits; + + for my $unit (@allUnits) + { + my ($unitTeam) = $unit =~ /tm:(\d+)/; + if ( $unitTeam == $theTeam ) + { + push(@myUnits,$unit); + } + } + + return @myUnits; +} + +sub GetTurnFile +{ + my $turn = shift; + my $turnFile = "Turn_TURN.txt"; + $turnFile =~ s/TURN/$turn/; + return $turnFile; +} + +sub GetCommandFile +{ + my $turn = shift; + my $team = shift; + my $cmdFileName = "Turn_TURN_Team_TEAM.txt"; + $cmdFileName =~ s/TURN/$turn/; + $cmdFileName =~ s/TEAM/$team/; + return $cmdFileName; +} + +# Output the commands file +sub OutputCommandsFile +{ + my $turn = shift; + my $team = shift; + my $commands = shift; + + # Get output file + our $cmdFileName = GetCommandFile($turn,$team); + + if (! -e $cmdFileName) + { + open(my $cmdFile, '>', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; + print $cmdFile $commands; + close $cmdFile; + + printf "Outputted $cmdFileName\n"; + printf "$commands"; + } + else + { + open(my $cmdFile, '<', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; + my $old_commands = do { <$cmdFile> }; + + printf "Replaying $cmdFileName\n"; + printf "$old_commands"; + } +} + +# Print a game map +sub PrintGameMap +{ + my $gameX = shift; + my $gameY = shift; + my @units = @_; + + my @map; + + # Fill with blanks + for my $x (0 .. $gameX-1) + { + for my $y (0 .. $gameY-1) + { + $map[$x][$y] = "-"; + } + } + + # Fill with units + for my $unit (@units) + { + my ($id,$tm,$vs,$dr,$psx,$psy) = getUnit($unit); + + $tm += 31; + $vs = "\e[".$tm."m".$vs."\e[0m"; + + $map[$psx][$psy] = $vs; + } + + # Print whole map bottom left is 0,0 + for my $y ( reverse 0 .. $gameY-1 ) + { + for my $x (0 .. $gameX-1) + { + printf($map[$x][$y]); + } + printf("\n"); + } +} + +# Wait for a file to exist +sub WaitForFile +{ + my $file = $_[0]; + while( ! -e $file ) + { + select(undef, undef, undef, 0.01); + } +} + +return 1; \ No newline at end of file From ec7ed601a373ffa98a739274be9c901c330019c9 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 15:56:08 +0000 Subject: [PATCH 086/190] Players moved to external repository Can be found at https://github.com/mdiluz/ttrts-players --- players/perl/ttrts.pm | 179 ---------------------------------------- players/randomPlayer.pl | 104 ----------------------- 2 files changed, 283 deletions(-) delete mode 100644 players/perl/ttrts.pm delete mode 100755 players/randomPlayer.pl diff --git a/players/perl/ttrts.pm b/players/perl/ttrts.pm deleted file mode 100644 index 7e9fdbf..0000000 --- a/players/perl/ttrts.pm +++ /dev/null @@ -1,179 +0,0 @@ -#! /usr/bin/perl -use strict; -use warnings; - -# Get information about a unit from it's descriptor -sub getUnit -{ - return ($_[0] =~ /UNIT:(\d+) tm:(\d+) vs:([^ ]+) dr:([^ ]+) ps:\[(\d+),(\d+)\]/); -} - -# Get the units from a turn file -sub GetUnitsForTurn -{ - my $turnFile = shift; - - # Open the turn file - open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; - - # Skip the header information - my $headerLine = <$TURNHANDLE>; - my $sizeLine = <$TURNHANDLE>; - my $turnLine = <$TURNHANDLE>; - ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; - - my @units; - while( my $unitLine = <$TURNHANDLE> ) - { - chomp $unitLine; - push(@units,$unitLine); - } - - close $TURNHANDLE; - - return @units; -} - -# Get information from the header for this turn -sub GetHeaderForTurn -{ - my $turnFile = shift; - - # Open the turn file - open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; - - # Pull in the header information - my $headerLine = <$TURNHANDLE>; - chomp $headerLine; - my $sizeLine = <$TURNHANDLE>; - chomp $sizeLine; - my $turnLine = <$TURNHANDLE>; - chomp $turnLine; - - my ($gameName) = ( $headerLine =~ /===== ([^ ]+) =====/ ); - my ($gameX,$gameY) = ( $sizeLine =~ /SIZE:\[(\d+),(\d+)\]/ ); - - ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; - - close $TURNHANDLE; - - return ($gameName,$gameX,$gameY); -} - -# Get units from a specific team -sub getUnitsOnTeam -{ - my $theTeam = shift; - my @allUnits = @_; - my @myUnits; - - for my $unit (@allUnits) - { - my ($unitTeam) = $unit =~ /tm:(\d+)/; - if ( $unitTeam == $theTeam ) - { - push(@myUnits,$unit); - } - } - - return @myUnits; -} - -sub GetTurnFile -{ - my $turn = shift; - my $turnFile = "Turn_TURN.txt"; - $turnFile =~ s/TURN/$turn/; - return $turnFile; -} - -sub GetCommandFile -{ - my $turn = shift; - my $team = shift; - my $cmdFileName = "Turn_TURN_Team_TEAM.txt"; - $cmdFileName =~ s/TURN/$turn/; - $cmdFileName =~ s/TEAM/$team/; - return $cmdFileName; -} - -# Output the commands file -sub OutputCommandsFile -{ - my $turn = shift; - my $team = shift; - my $commands = shift; - - # Get output file - our $cmdFileName = GetCommandFile($turn,$team); - - if (! -e $cmdFileName) - { - open(my $cmdFile, '>', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; - print $cmdFile $commands; - close $cmdFile; - - printf "Outputted $cmdFileName\n"; - printf "$commands"; - } - else - { - open(my $cmdFile, '<', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; - my $old_commands = do { <$cmdFile> }; - - printf "Replaying $cmdFileName\n"; - printf "$old_commands"; - } -} - -# Print a game map -sub PrintGameMap -{ - my $gameX = shift; - my $gameY = shift; - my @units = @_; - - my @map; - - # Fill with blanks - for my $x (0 .. $gameX-1) - { - for my $y (0 .. $gameY-1) - { - $map[$x][$y] = "-"; - } - } - - # Fill with units - for my $unit (@units) - { - my ($id,$tm,$vs,$dr,$psx,$psy) = getUnit($unit); - - $tm += 31; - $vs = "\e[".$tm."m".$vs."\e[0m"; - - $map[$psx][$psy] = $vs; - } - - # Print whole map bottom left is 0,0 - for my $y ( reverse 0 .. $gameY-1 ) - { - for my $x (0 .. $gameX-1) - { - printf($map[$x][$y]); - } - printf("\n"); - } -} - -# Wait for a file to exist -sub WaitForFile -{ - my $file = $_[0]; - while( ! -e $file ) - { - select(undef, undef, undef, 0.01); - } -} - -return 1; \ No newline at end of file diff --git a/players/randomPlayer.pl b/players/randomPlayer.pl deleted file mode 100755 index 14d6338..0000000 --- a/players/randomPlayer.pl +++ /dev/null @@ -1,104 +0,0 @@ -#! /usr/bin/perl -use strict; -use warnings; -use Term::ANSIColor; - -# From http://perlmaven.com/how-to-create-a-perl-module-for-code-reuse -# expect ttrts perl module in a neighboring perl/ dir -use File::Basename qw(dirname); -use Cwd qw(abs_path); -use lib dirname(abs_path($0))."/perl"; - -# Use our ttrts perl library -use ttrts; - -our $usage_text=< Date: Sat, 20 Dec 2014 16:14:35 +0000 Subject: [PATCH 087/190] Add MAJOR.MINOR.PATCH versioning to the binary. This also modified the output format for the gamestatefile so AI's will have to be updated --- source/CMakeLists.txt | 16 ++++++++++++++++ source/game/game.h | 2 +- source/ttrts/CMakeLists.txt | 8 ++++---- source/ttrts/README.md | 8 +++++--- source/ttrts/main.cpp | 8 ++++++++ source/ttrts/version.h | 9 +++++++++ 6 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 source/ttrts/version.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index abba789..2f3021c 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,5 +1,11 @@ cmake_minimum_required( VERSION 2.8.7 ) +# Set version information +set( TTRTS_VERSION_MAJOR 0 ) +set( TTRTS_VERSION_MINOR 0 ) +set( TTRTS_VERSION_PATCH 1 ) +set( TTRTS_VERSION_STRING "${TTRTS_VERSION_MAJOR}.${TTRTS_VERSION_MINOR}.${TTRTS_VERSION_PATCH}") + # Use c++1y (14) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++1y" ) @@ -9,10 +15,20 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-reorder" ) # Turn off reorder warnings as they're kind of irrelevant set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder" ) +# This shouldn't be needed, but it looks like IDE's like clion can forget to set -g for Debug if( CMAKE_BUILD_TYPE MATCHES "Debug" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g" ) endif() +# Add definitions for the version number +add_definitions( + -DTTRTS_VERSION_MAJOR=${TTRTS_VERSION_MAJOR} + -DTTRTS_VERSION_MINOR=${TTRTS_VERSION_MINOR} + -DTTRTS_VERSION_PATCH=${TTRTS_VERSION_PATCH} + -DTTRTS_VERSION_STRING=\"${DTTRTS_VERSION_STRING}\" +) + + # Subprojects add_subdirectory( ttrts ) add_subdirectory( game ) diff --git a/source/game/game.h b/source/game/game.h index 5e68b42..4662776 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -5,7 +5,7 @@ #include "gametypes.h" #include "order.h" -#define GAME_HEADER_FORMATTER "===== %s =====\nSIZE:[%u,%u]\nTURN:%u" +#define GAME_HEADER_FORMATTER "NAME:%s\nSIZE:[%u,%u]\nTURN:%u" #define GAME_HEADER_DELIMITER "~~~~\n" // Type for order and unit pairs diff --git a/source/ttrts/CMakeLists.txt b/source/ttrts/CMakeLists.txt index 2a49815..d7aa9da 100644 --- a/source/ttrts/CMakeLists.txt +++ b/source/ttrts/CMakeLists.txt @@ -8,8 +8,8 @@ include_directories( ../game ) -# Add the sources -set( SOURCES +# Add the sources +set( SOURCES main.cpp ) @@ -22,8 +22,8 @@ target_link_libraries( ${PROJECT_NAME} game ) install( TARGETS ${PROJECT_NAME} DESTINATION bin ) # Run the gen_usage script to generate our usage header -add_custom_target( +add_custom_target( gen_ttrts_usage cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_SOURCE_DIR}/scripts/gen_usage.sh "${CMAKE_CURRENT_BINARY_DIR}/usage.h" ) -add_dependencies(${PROJECT_NAME} gen_ttrts_usage) +add_dependencies(${PROJECT_NAME} gen_ttrts_usage) \ No newline at end of file diff --git a/source/ttrts/README.md b/source/ttrts/README.md index 2579dc5..a6d197a 100644 --- a/source/ttrts/README.md +++ b/source/ttrts/README.md @@ -29,18 +29,20 @@ ## OPTIONS MAPFILE - File to read in the initial game state from -------------------------------------------------------------------------------- +-------------------------------------------------------------- ## GAMESTATE FILE FORMAT ### Name Turn_{TURN_NUMBER}.txt ### Contents - ===== {GAME_NAME} ===== + ===== ttrts v{MAJOR}.{MINOR}.{PATCH} ===== + NAME:{GAMENAME} SIZE:[{X},{Y}] TURN:{TURN_NUMBER} + ... {any extra properties could go here} ~~~~ UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}] - ... + ... {continue for all units} ## ORDER FILE FORMAT ### Name diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index 7ecc424..e783116 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -6,6 +6,7 @@ #include #include "game.h" +#include "version.h" static const char* sk_usage = #include "usage.h" @@ -40,6 +41,13 @@ bool OutputGameStateFile(CTTRTSGame &game, std::string &gameDir) // Output the turn description std::string turnDescriptor = game.GetStateAsString(); + + // Append the version number + turnDescriptor = std::string("==== ttrts v") + + sk_ttrts_version_string + + std::string(" ====") + + turnDescriptor; + turnFile< Date: Sat, 20 Dec 2014 16:24:07 +0000 Subject: [PATCH 088/190] Add an END specifiers to gamestate and order files Game will now wait until the last line of an order file matches "END" AI's MUST now finalise their order file with "END" fixes #1 --- source/game/game.cpp | 1 + source/ttrts/README.md | 4 +++- source/ttrts/main.cpp | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/source/game/game.cpp b/source/game/game.cpp index 3608f70..9b9ab6b 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -510,6 +510,7 @@ std::string CTTRTSGame::GetStateAsString() const state += '\n'; state += GAME_HEADER_DELIMITER; state += units; + state += "END"; return state; } diff --git a/source/ttrts/README.md b/source/ttrts/README.md index a6d197a..aa57c51 100644 --- a/source/ttrts/README.md +++ b/source/ttrts/README.md @@ -43,13 +43,15 @@ ~~~~ UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}] ... {continue for all units} + END ## ORDER FILE FORMAT ### Name Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt ### Contents ORDER:{ORDER_CHAR} id:{UNIT_ID} - ... + ... {continue for all orders} + END ### Orders F - move unit [F]orward one space diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index e783116..abc457b 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "game.h" #include "version.h" @@ -155,7 +156,40 @@ int main(int argc, char* argv[]) // Wait for the team order file to be created std::cout<<"Waiting for "< Date: Sat, 20 Dec 2014 16:50:57 +0000 Subject: [PATCH 089/190] Update the README.md files with newer and more useful information fixes #4 --- README.md | 32 +++++++++++++++++++++----------- source/README.md | 16 +++++++++------- source/game/README.md | 8 ++++---- source/ttrts/README.md | 2 +- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 831d12b..4ad10f5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,27 @@ # TTRTS +*v0.0.1* *The Tiny Terminal RTS where the players write their AIs* -------------------------------------------------------------------------------- +----------------------------------------------------------- ## Introduction -A simple terminal based RTS game that uses txt files to communicate game state and unit commands. TTRTS was +A simple terminal based RTS game that uses text files to communicate game state and unit commands. -------------------------------------------------------------------------------- -## Gameplay -1. The ttrts client is run from the command line with initial parameters -2. The client outputs a gamestate text file -3. A player, program or cat reads the state and outputs instructions for their units. -4. The client reads in instructions and processes the turn -5. If no winner is reached, skip back to step 2 -6. The game client outputs a final summary file with the winner +TTRTS was is from the ground up designed to be a fun way to practice programming. Any programming language than can handle file I/O can be used to make an AI for TTRTS, and this extensibility allows for any type of programmer to have fun and enjoy designing and playing against their friends. -*see [game](source/game) for full game rules* +----------------------------------------------------------- +## Building TTRTS + +#### Requirements +* cmake - our build system uses cmake +* Linux/OSX - currently no support for Windows + +#### To Build + $ git clone https://github.com/mdiluz/ttrts.git + $ cd ttrts + $ ./bootstrap.sh + $ ./ttrts # To launch binary and display usage + +----------------------------------------------------------- +## Further Information + +See [the ttrts binary readme](source/ttrts/README.md) for full usage and game rules diff --git a/source/README.md b/source/README.md index f793144..a18fa94 100644 --- a/source/README.md +++ b/source/README.md @@ -1,17 +1,19 @@ # Targets ### ttrts -Main TTRTS executable , runs from the command line and acts as client +Main TTRTS binary, runs from the command line and acts as host for games -### ttrts-test -Test executable, to be compiled and run to test various functionality +### test (ttrts-test) +Test binary, to be compiled and run to test various functionality -### ttrts-gen -Binary to generate map example map files +### gen (ttrts-gen) +Binary to generate example map files # Libraries ### game -Implementation of the RTS rules and simulation. +Implementation of the RTS rules and simulation ### maths -simple maths library for 2D calculations and types +Simple maths library for 2D calculations and types +### scripts +Directory of scripts used in build process diff --git a/source/game/README.md b/source/game/README.md index b1846b3..4b61793 100644 --- a/source/game/README.md +++ b/source/game/README.md @@ -3,15 +3,15 @@ TTRTS Gameplay The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board. -Each player is in control of a set number of starting units, each turn receives data on the status of the board. +Each player is in control of a set number of starting units, and each turn receives data on the status of the board. -Each player must then issue a single command to each unit in their control. +Each player must then issue a command to each unit in their control. -The engine then takes all commands, evaluates all movement first simultaneously, then all other commands. +All commands are evaluated simultaniously. All attempted movement to the same square by two or more units will fail. Friendly fire is enabled by default. - +A player wins when all opposing units have been destroyed. diff --git a/source/ttrts/README.md b/source/ttrts/README.md index aa57c51..041ece2 100644 --- a/source/ttrts/README.md +++ b/source/ttrts/README.md @@ -1,5 +1,5 @@ ## NAME - ttrts - Tiny Terminal RTS + ttrts - Tiny Terminal RTS v0.0.1 ## SYNOPSYS ttrts MAPFILE From 4f595ff92e0ea53e7a23bf123704cf7024aa31db Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 17:27:25 +0000 Subject: [PATCH 090/190] Fix #8 by defining the version properly and adding a proper line end --- source/CMakeLists.txt | 3 +-- source/ttrts/main.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 2f3021c..b65975c 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -4,7 +4,6 @@ cmake_minimum_required( VERSION 2.8.7 ) set( TTRTS_VERSION_MAJOR 0 ) set( TTRTS_VERSION_MINOR 0 ) set( TTRTS_VERSION_PATCH 1 ) -set( TTRTS_VERSION_STRING "${TTRTS_VERSION_MAJOR}.${TTRTS_VERSION_MINOR}.${TTRTS_VERSION_PATCH}") # Use c++1y (14) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++1y" ) @@ -25,7 +24,7 @@ add_definitions( -DTTRTS_VERSION_MAJOR=${TTRTS_VERSION_MAJOR} -DTTRTS_VERSION_MINOR=${TTRTS_VERSION_MINOR} -DTTRTS_VERSION_PATCH=${TTRTS_VERSION_PATCH} - -DTTRTS_VERSION_STRING=\"${DTTRTS_VERSION_STRING}\" + -DTTRTS_VERSION_STRING=\"${TTRTS_VERSION_MAJOR}.${TTRTS_VERSION_MINOR}.${TTRTS_VERSION_PATCH}\" ) diff --git a/source/ttrts/main.cpp b/source/ttrts/main.cpp index abc457b..16815f1 100644 --- a/source/ttrts/main.cpp +++ b/source/ttrts/main.cpp @@ -46,7 +46,7 @@ bool OutputGameStateFile(CTTRTSGame &game, std::string &gameDir) // Append the version number turnDescriptor = std::string("==== ttrts v") + sk_ttrts_version_string - + std::string(" ====") + + std::string(" ====\n") + turnDescriptor; turnFile< Date: Sat, 20 Dec 2014 17:17:04 +0000 Subject: [PATCH 091/190] Update to 0.0.1 version system for main ttrts client and check versions --- ttrts.pm | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/ttrts.pm b/ttrts.pm index 7e9fdbf..9479e9c 100644 --- a/ttrts.pm +++ b/ttrts.pm @@ -2,6 +2,9 @@ use strict; use warnings; +our $ttrts_perlai_versioncompat_major = 0; +our $ttrts_perlai_versioncompat_minor = 0; + # Get information about a unit from it's descriptor sub getUnit { @@ -17,16 +20,21 @@ sub GetUnitsForTurn open (my $TURNHANDLE, '<', $turnFile) or die "Could not open '$turnFile' $!"; # Skip the header information - my $headerLine = <$TURNHANDLE>; - my $sizeLine = <$TURNHANDLE>; - my $turnLine = <$TURNHANDLE>; - ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; + my $num = 0; + while( !( <$TURNHANDLE> =~ /~~~~/ ) ) + { + $num++; + $num > 20 and die "gamestate file did not reach ~~~~ line within 10 lines"; + } my @units; while( my $unitLine = <$TURNHANDLE> ) { chomp $unitLine; - push(@units,$unitLine); + if( !($unitLine eq "END") ) + { + push(@units,$unitLine); + } } close $TURNHANDLE; @@ -34,6 +42,19 @@ sub GetUnitsForTurn return @units; } +# Check version numbers against ttrts.pm version +sub checkVersion +{ + my $version_major = shift; + my $version_minor = shift; + if( ($version_major != $ttrts_perlai_versioncompat_major) + or ($version_minor != $ttrts_perlai_versioncompat_minor) ) + { + printf "ttrts.pm version does not match with this ttrts version\n"; + die "ttrts.pm = v$ttrts_perlai_versioncompat_minor.$ttrts_perlai_versioncompat_major ttrts = v$version_major.$version_minor"; + } +} + # Get information from the header for this turn sub GetHeaderForTurn { @@ -45,15 +66,18 @@ sub GetHeaderForTurn # Pull in the header information my $headerLine = <$TURNHANDLE>; chomp $headerLine; + my $nameLine = <$TURNHANDLE>; + chomp $nameLine; my $sizeLine = <$TURNHANDLE>; chomp $sizeLine; my $turnLine = <$TURNHANDLE>; chomp $turnLine; - my ($gameName) = ( $headerLine =~ /===== ([^ ]+) =====/ ); - my ($gameX,$gameY) = ( $sizeLine =~ /SIZE:\[(\d+),(\d+)\]/ ); + my ($version_major,$version_minor) = ( $headerLine =~ /==== ttrts v(\d+)\.(\d+)\.\d+ ====/ ); + checkVersion $version_major,$version_minor; - ( <$TURNHANDLE> =~ /~~~~/ ) or die "Gamestate file did not match expected format"; + my ($gameName) = ( $nameLine =~ /NAME:(.+)/ ); + my ($gameX,$gameY) = ( $sizeLine =~ /SIZE:\[(\d+),(\d+)\]/ ); close $TURNHANDLE; @@ -111,6 +135,7 @@ sub OutputCommandsFile { open(my $cmdFile, '>', $cmdFileName) or die "Couldn't open '$cmdFileName' $!"; print $cmdFile $commands; + print $cmdFile "END"; close $cmdFile; printf "Outputted $cmdFileName\n"; From d71983dcb191b5281876551ac12bb25893bf2b09 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 17:31:13 +0000 Subject: [PATCH 092/190] Update to version 0.1.0 --- README.md | 4 ++-- source/CMakeLists.txt | 4 ++-- source/ttrts/README.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4ad10f5..be67d0d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# TTRTS -*v0.0.1* +# TTRTS v0.1.0 + *The Tiny Terminal RTS where the players write their AIs* ----------------------------------------------------------- diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b65975c..7891d29 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required( VERSION 2.8.7 ) # Set version information set( TTRTS_VERSION_MAJOR 0 ) -set( TTRTS_VERSION_MINOR 0 ) -set( TTRTS_VERSION_PATCH 1 ) +set( TTRTS_VERSION_MINOR 1 ) +set( TTRTS_VERSION_PATCH 0 ) # Use c++1y (14) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++1y" ) diff --git a/source/ttrts/README.md b/source/ttrts/README.md index 041ece2..0bd15c2 100644 --- a/source/ttrts/README.md +++ b/source/ttrts/README.md @@ -1,5 +1,5 @@ ## NAME - ttrts - Tiny Terminal RTS v0.0.1 + ttrts - Tiny Terminal RTS v0.1.0 ## SYNOPSYS ttrts MAPFILE From 19623c67119cc90cc2156430a0feb85b162b12f9 Mon Sep 17 00:00:00 2001 From: mdiluzio Date: Sat, 20 Dec 2014 17:33:36 +0000 Subject: [PATCH 093/190] Update version to match with compatible ttrts version --- ttrts.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ttrts.pm b/ttrts.pm index 9479e9c..eb588ba 100644 --- a/ttrts.pm +++ b/ttrts.pm @@ -3,7 +3,7 @@ use strict; use warnings; our $ttrts_perlai_versioncompat_major = 0; -our $ttrts_perlai_versioncompat_minor = 0; +our $ttrts_perlai_versioncompat_minor = 1; # Get information about a unit from it's descriptor sub getUnit From f68d16b8b943143134e1f44eae0b3229bcbe43e0 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sat, 20 Dec 2014 17:37:49 +0000 Subject: [PATCH 094/190] Add link to my ttrts-players repository to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index be67d0d..ce9a37d 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,5 @@ TTRTS was is from the ground up designed to be a fun way to practice programming ## Further Information See [the ttrts binary readme](source/ttrts/README.md) for full usage and game rules + +See [my ttrts-players repository](https://github.com/mdiluz/ttrts-players) for examples of players From 321690faaa2577ff4cde5dea2d3afcc638392e22 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sat, 20 Dec 2014 17:47:10 +0000 Subject: [PATCH 095/190] Add link to issue #9 to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce9a37d..279f0d7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ TTRTS was is from the ground up designed to be a fun way to practice programming #### Requirements * cmake - our build system uses cmake -* Linux/OSX - currently no support for Windows +* Linux/OSX - currently no support for Windows, tracked with [Issue #9](https://github.com/mdiluz/ttrts/issues/9) #### To Build $ git clone https://github.com/mdiluz/ttrts.git From 5931643ab85b3ea99525773b6308b91debb9bfd2 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sat, 20 Dec 2014 19:32:06 +0000 Subject: [PATCH 096/190] Create gh-pages branch via GitHub --- index.html | 106 ++++++++++++++++------------------------------------ params.json | 2 +- 2 files changed, 34 insertions(+), 74 deletions(-) diff --git a/index.html b/index.html index 48a2aa0..801b011 100644 --- a/index.html +++ b/index.html @@ -46,91 +46,51 @@
-

+

+TTRTS v0.1.0

+ +

The Tiny Terminal RTS where the players write their AIs

+ +
+ +

Introduction

-

We aim to create a simple terminal based rts where a user can program an AI to control their army

+

A simple terminal based RTS game that uses text files to communicate game state and unit commands.

+ +

TTRTS was is from the ground up designed to be a fun way to practice programming. Any programming language than can handle file I/O can be used to make an AI for TTRTS, and this extensibility allows for any type of programmer to have fun and enjoy designing and playing against their friends.


-Gameplay

+Building TTRTS -
    -
  1. ttrts clients are run from the command line
  2. -
  3. ttrts-server is launched from the command line
  4. -
  5. clients will connect to server and confirm initial board state - -
      -
    1. clients output a text file with game data for this turn
    2. -
    3. a player, or program, reads the game data file and outputs an instructions file
    4. -
    5. clients read the instructions file, simulates the turn
    6. -
    7. game state is verified between clients and server
    8. -
    9. repeat until an end state is reached
    10. -
    -
  6. -
  7. once game is finished, host and clients disconnect and a winner is notified
  8. -
- -

see game for full game rules

- -
- -

-Source

- -

-Targets

- -
-ttrts
- -

Main TTRTS executable , runs from the command line and acts as client

- -
-ttrts-server
- -

TTRTS server executable, runs from the command line acting as server

- -
-player
- -

Custom player AI code, this should contain examples and test code to help newcomers begin their journey

- -
-ttrts-test
- -

Test executable, to be compiled and run to test various functionality

- -

-Libraries

- -
-game
- -

Implementation of the RTS rules and simulation. game has full information on it's implementation.

- -
-net
- -

Net code for hosting the server and communicating with clients

- -
-ui
- -

Wrapper for user interface for the terminal, this only really needs three stages

+

+Requirements

    -
  • Initialise the game with settings and connect the clients
  • -
  • Run the game simulation to it's conclusion
  • -
  • Display the game result
  • -
  • ASCII Colour wrapper for separate teams
  • +
  • cmake - our build system uses cmake
  • +
  • Linux/OSX - currently no support for Windows, tracked with Issue #9 +
-
-maths
+

+To Build

-

simple maths library for 2D calculations and types

+
$ git clone https://github.com/mdiluz/ttrts.git
+$ cd ttrts
+$ ./bootstrap.sh
+$ ./ttrts # To launch binary and display usage
+
+ +
+ +

+Further Information

+ +

See the ttrts binary readme for full usage and game rules

+ +

See my ttrts-players repository for examples of players