Compare commits

...

81 commits

Author SHA1 Message Date
mdiluzio
ae8a3bf0c2 Update changelog for v0.4.0 2015-01-10 17:29:22 +00:00
mdiluzio
f13a8bd5c4 Ensure that hostname is also properly highlighted 2015-01-10 17:25:01 +00:00
mdiluzio
5e66596d30 Update the Readme with new server and client communication options 2015-01-10 17:23:48 +00:00
mdiluzio
3bb5766061 Fix completion script to take into account non-positional arguments 2015-01-10 17:06:23 +00:00
mdiluzio
96153f8c60 Update launcher script with handing of return value 2015-01-10 17:04:55 +00:00
mdiluzio
96f5c08c14 Update completion script with new parameters 2015-01-10 16:59:45 +00:00
mdiluzio
d65ef46634 Fix some bugs in generation scripts 2015-01-10 16:56:42 +00:00
mdiluzio
06057e6e52 Add perl launcher script and new Readme 2015-01-10 16:56:27 +00:00
mdiluzio
1e38a5f107 Update CMakelists to reflect refactor 2015-01-10 16:56:01 +00:00
mdiluzio
0ead12c7dd Huge refactor, pulling server and local out into their own binaries 2015-01-10 16:55:30 +00:00
mdiluzio
1b2010faba Use some version checking on generator scripts, preventing a full rebuild on every build 2015-01-10 15:11:36 +00:00
Marc Di Luzio
30af43ae95 Fix walls in strings and move constructor 2015-01-07 13:25:16 +00:00
Marc Di Luzio
7811daa78b Merge branch 'net' of https://github.com/mdiluz/ttrts into net 2015-01-07 12:40:44 +00:00
Marc Di Luzio
c51119c3e2 Add TTRTS: Prefix to output 2015-01-07 12:40:41 +00:00
25e0fa04de Merge branch 'net' of https://github.com/mdiluz/ttrts into net 2015-01-07 12:40:07 +00:00
3291bf126e Compare write result correctly with 0
Fixes client sending of orders
2015-01-07 12:39:27 +00:00
JackUnthank
a0ff8680a3 Make the AI print out the walls not the map.
Don’t worry I wasn’t doing this all afternoon I had a spare 5 minutes
whilst waiting for some .DAT files to md5. =P
2015-01-06 17:01:15 +00:00
Marc Di Luzio
a7421fa07b Guard against missing file param for server 2015-01-06 12:39:56 +00:00
mdiluzio
1c97177956 Major cleanup of client code, extracting various functionality 2015-01-04 13:09:37 +00:00
mdiluzio
3ed25cd37f Fix up all logging output
Remove debug logging
Use cerr or clog in the correct places
refactor a few more functions
2015-01-04 11:59:58 +00:00
mdiluzio
b141314434 Some more refactoring with additional comments
Server now performs handshake instantly
2015-01-04 11:22:54 +00:00
mdiluzio
5c8666d4fb More refactoring by pulling code out of client and server files 2015-01-04 11:10:08 +00:00
mdiluzio
43d688a728 Refactor running game server into own function 2015-01-03 22:43:39 +00:00
mdiluzio
4055d85d99 Use new fatal_error and fatal_perror functions for errors 2015-01-03 22:30:52 +00:00
mdiluzio
1785ce2fc0 Add game name to handshake and formalise handshake format 2015-01-03 20:04:31 +00:00
mdiluzio
61c012370d Fix filesystem game method 2015-01-03 19:31:42 +00:00
mdiluzio
b532f1c9c8 Make client output and read in game state and order files 2015-01-03 19:27:08 +00:00
mdiluzio
18cfcff264 Improve both client and server code to account for END of messages, allowing for incomplete packets with TCP 2015-01-03 18:55:44 +00:00
mdiluzio
b34b933dcd Pull base netcode functions out into net files 2015-01-02 19:51:14 +00:00
mdiluzio
b43927a1da add user input for starting the game 2015-01-02 19:46:09 +00:00
mdiluzio
770502184c Make client and server perform a handshake to agree on player IDs 2015-01-02 19:31:14 +00:00
mdiluzio
bccd043d2c Fix server functions being used wrongly 2015-01-02 19:05:58 +00:00
mdiluzio
a81b4ff8d0 Handle end game state properly 2015-01-02 19:00:28 +00:00
mdiluzio
e01a718ac6 Fix bug where we passed client info to the thread by ref, like an ape 2015-01-02 18:57:47 +00:00
mdiluzio
56e767bb5b Clients now connect and recieve gamestate information 2015-01-02 17:14:16 +00:00
mdiluzio
4285770d52 Allow multiple client connections on different threads 2015-01-02 16:18:18 +00:00
mdiluzio
dcb9d68fb4 Revert back to cleaner copy of server code 2015-01-02 16:09:24 +00:00
mdiluzio
83b9990bcc some more cleanup and using TTRTS_PORT 2015-01-02 15:25:30 +00:00
mdiluzio
b4240cf1c8 pull in very simple client/server demo code
inspired by http://www.linuxhowtos.org/C_C++/socket.htm
2015-01-02 15:13:05 +00:00
mdiluzio
0619031cb6 Use argc and argv passed down 2015-01-02 15:07:35 +00:00
mdiluzio
2281bcb6cd More refactoring of functionality into seperate files, with stubs for server and client 2015-01-02 15:03:29 +00:00
mdiluzio
8835bfb82a On second thoughts move filesystem game code out to a filesystem cpp 2015-01-02 14:09:29 +00:00
mdiluzio
0962546a82 Move current client code out to server file, still uses old file-system method 2015-01-02 14:04:37 +00:00
mdiluzio
3b5599ffad Remove references to non-existent maths 2015-01-02 14:04:11 +00:00
mdiluzio
ca5526b2b7 Add base files for net code 2015-01-02 13:55:23 +00:00
mdiluzio
05c8318099 Update to v0.3.2 2014-12-31 18:43:30 +00:00
mdiluzio
082810de4a Merge branch 'dev' of https://github.com/mdiluz/ttrts into dev 2014-12-31 18:40:05 +00:00
mdiluzio
189241853b Update the map generation
New maps! 3 player!
Update names, as it wasn't 2v2 so much as 1v1 with 2 units each
2014-12-31 18:39:58 +00:00
mdiluzio
faa7cdc501 Fix bug when loading games with walls
Game would fail to load as it was cutting off the first [
2014-12-31 18:39:11 +00:00
3f892ec2aa Move maps to /usr/local and allow these settings to get defaults from the cmakelists 2014-12-31 14:08:51 +00:00
3f76b6430b Fix usage of sed and man page on OSX 2014-12-31 13:50:15 +00:00
mdiluzio
9c68372f64 Update to patch 0.3.1 2014-12-30 19:26:14 +00:00
mdiluzio
66f5226797 Correct spelling mistake in README and add sudo to install 2014-12-30 19:25:11 +00:00
mdiluzio
cb46c84de5 Actually use real version number in the man page 2014-12-30 19:23:03 +00:00
mdiluzio
ad803a5d7a Rework bootstrap to use new install targets 2014-12-30 18:53:15 +00:00
mdiluzio
2273c93f11 Update with more install targets.
Installing headers and library to /usr/local/
Requires workaround for version information header generation

Install a man page

Install some basic bash completion for maps

Also update the readme to fit with the manpage and new autocompletion features

Fixes the oddity where generated map files were missing the initial line of the header
2014-12-30 18:37:46 +00:00
mdiluzio
53e882ae12 Allow env variables to set game and map locations
the client will also now look in /usr/local for maps
2014-12-30 18:36:01 +00:00
mdiluzio
31644b1f95 Fix a couple of bugs in gen_usage.sh 2014-12-30 18:33:59 +00:00
mdiluzio
f1e4f432e6 Move top level cmakelist up to main directory 2014-12-30 18:32:17 +00:00
Marc Di Luzio
2c0a393f77 Update readme to reflect new libraries 2014-12-30 13:24:21 +00:00
Marc Di Luzio
71aab498dc extract format functions out into formatters files 2014-12-30 13:24:21 +00:00
Marc Di Luzio
3342300324 Remove math library and merge gametypes and mathtypes 2014-12-30 13:24:20 +00:00
Marc Di Luzio
d9b9f3d7dd Re-organise source directory and targets
ttrts -> client
game -> ttrts
2014-12-30 13:24:19 +00:00
Marc Di Luzio
415361ac9c Update CMakelists
Additional install targets for maps, and reworked dependencies
2014-12-30 13:24:17 +00:00
mdiluzio
481153606b Remove version numbers from unneeded places 2014-12-29 22:44:47 +00:00
Marc Di Luzio
97ef46db16 Update README with fixes to links 2014-12-29 22:35:40 +00:00
mdiluzio
a80c415932 Remove french line 2014-12-29 22:31:35 +00:00
mdiluzio
485a05293c Rename invalid positions to walls 2014-12-29 22:30:11 +00:00
mdiluzio
128dc14bb6 Move ttrts perl module into api/perl dubdirectory 2014-12-29 22:07:59 +00:00
mdiluzio
c0d5044f81 Merge branch 'master' of ../ttrts-players2/ into dev 2014-12-29 22:06:41 +00:00
mdiluzio
b50c838f28 Large refactor and addition of new features in line with 0.3.0 2014-12-29 21:59:59 +00:00
mdiluzio
286cc81f3c Update README files with new changes.
Also add changelog to top level readme
2014-12-29 21:57:52 +00:00
mdiluzio
fc62785768 update to v0.3.0 to account for new format and features
In this case, new WALLs to take into account for the game
2014-12-29 21:57:14 +00:00
mdiluzio
d172922698 Implement invalid unit positions 2014-12-29 21:55:32 +00:00
mdiluzio
c98ac95759 Output usage with cout not cerr 2014-12-29 21:54:34 +00:00
mdiluzio
5737ae31be Some small cleanup and refactoring.
Update name of a few functions to best suit their usage.
Split up checking for winning player and checking for game over state.
2014-12-29 21:52:25 +00:00
mdiluzio
f9ee646c93 Update to be compatible with 0.2 2014-12-22 20:05:30 +00:00
mdiluzio
ac9ed13be8 Update using player instead of team and matching new file formats 2014-12-22 20:03:31 +00:00
mdiluzio
19623c6711 Update version to match with compatible ttrts version 2014-12-20 17:33:36 +00:00
mdiluzio
b0af969b29 Update to 0.0.1 version system for main ttrts client and check versions 2014-12-20 17:28:53 +00:00
mdiluzio
469e3ffa8a Add a Random player, simply does random commands on each turn
Remove simplePlayer in favor of new randomPlayer, which uses new ttrts.pm module
2014-12-20 15:35:19 +00:00
50 changed files with 2200 additions and 663 deletions

1
.gitignore vendored
View file

@ -9,4 +9,5 @@
*.user
*.sublime*
*.idea
*~

54
CMakeLists.txt Normal file
View file

@ -0,0 +1,54 @@
cmake_minimum_required( VERSION 2.8.7 )
# Set version information
set( TTRTS_VERSION_MAJOR 0 )
set( TTRTS_VERSION_MINOR 4 )
set( TTRTS_VERSION_PATCH 0 )
# Set defaults for ttrts variables
set( TTRTS_MAPS "/usr/local/share/ttrts/maps/" )
set( TTRTS_GAMES "/tmp/" )
set( TTRTS_PORT 11715 )
# define these defaults in code
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_MAPS=${TTRTS_MAPS}" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_GAMES=${TTRTS_GAMES}" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_PORT=${TTRTS_PORT}" )
# 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" )
# 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 bash completion to install
install( FILES scripts/ttrts_complete DESTINATION /etc/bash_completion.d/ )
# Add target to generate man page
# Run the gen_usage script to generate our usage header
add_custom_target(
ttrts-gen-manpage ALL
${CMAKE_SOURCE_DIR}/scripts/gen_manpage.sh "${TTRTS_VERSION_MAJOR}" "${TTRTS_VERSION_MINOR}" "${TTRTS_VERSION_PATCH}" "ttrts.6" "${CMAKE_SOURCE_DIR}/source/README.md"
)
# Install the ttrts man page
if( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
set ( MANPAGE_LOC share/man/man6 )
elseif( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" )
set ( MANPAGE_LOC man/man6 )
else()
message(ERROR "Unsupported system detected")
endif()
install( FILES "${CMAKE_BINARY_DIR}/ttrts.6" DESTINATION ${MANPAGE_LOC} )
# Subprojects
add_subdirectory( source )

View file

@ -1,5 +1,4 @@
# TTRTS v0.2.0
# TTRTS
*The Tiny Terminal RTS where the players write their AIs*
-----------------------------------------------------------
@ -12,7 +11,7 @@ TTRTS is from the ground up designed to be a fun way to practice programming. An
## Building TTRTS
#### Requirements
* cmake - our build system uses cmake
* CMake - our build system uses cmake
* Linux/OSX - currently no support for Windows, tracked with [Issue #9](https://github.com/mdiluz/ttrts/issues/9)
#### To Build
@ -20,10 +19,67 @@ TTRTS is from the ground up designed to be a fun way to practice programming. An
$ cd ttrts
$ ./bootstrap.sh
$ ./ttrts # To launch binary and display usage
-----------------------------------------------------------
## Development
* master branch always stores latest stable release
* master/{hotfix} branches store in progress hotfixes for the stable branch
* dev branch stores in progress development
* dev/{feature} branches store features
-----------------------------------------------------------
## Changelog
#### v0.4.0
* Updated with network functionality
* Game can now be hosted with ttrts --server option
* Server can be connected to with ttrts --client
* Updated command line interface with new launcher script
* map file must now be specified with --map=FILE
* Slight refactor of libraries to account for new run targets
#### v0.3.2
* Fix bug when loading map files with walls
* Fix ttrts on OSX
* Install man files to correct location
* Update usage of sed to be compatible with BSD as well as GNU versions
* New maps and renames of old ones
#### v0.3.1
* Upgraded install target to repository
* libttrts static library binary in /usr/local/lib
* ttrts headers in /usr/local/include/ttrts
* bash completion into /etc/bash_completion.d/
* man page into /usr/local/man/man6
* maps into /usr/share/ttrts/maps
* client now supports env variable configuration
* TTRTS_MAPS for location of map files, defaults to /usr/share/ttrts/maps
* TTRTS_GAMES for gameplay directories, defaults to /tmp/
* Map files now have proper header
* NOTE: This version is compatible with v0.3.0, but old generated mapfiles will need the additional header line added
#### v0.3.0
* Additional functionality of walls
* Walls are noted in gamestate file on new "WALL:[X,Y]..." line
* Walls are impassable by all movement
* Units leave an impassable wall behind after movement
* Game can now end if no units are able to move
* Various C++ api simplifications
* Integration of perl api from [ttrts-players](https://github.com/mdiluz/ttrts-players)
#### v0.2.0
* All team references changed to player
* Order file format changed to Player_#_Turn_#.txt
* Unit descriptors now use pl: instead of tm:
* Various other C++ api corrections and refactors
#### v0.1.0
* First playable version of ttrts
-----------------------------------------------------------
## Further Information
See [the ttrts binary readme](source/ttrts/README.md) for full usage and game rules
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
See [ttrts-players](https://github.com/mdiluz/ttrts-players) for examples of AIs

306
api/perl/ttrts.pm Normal file
View file

@ -0,0 +1,306 @@
#! /usr/bin/perl
use strict;
use warnings;
our $ttrts_perlai_versioncompat_major = 0;
our $ttrts_perlai_versioncompat_minor = 3;
our $headerDelimiter="~~~~";
our $VERBOSE = $ENV{"VERBOSE"};
# Format of the a gamestate header
our $headerFormatter = qr/==== ttrts v(\d+)\.(\d+)\.(\d+)+ ====
NAME:(.+)
SIZE:\[(\d+),(\d+)\]
TURN:(\d+)
(WALL:.*?)
$headerDelimiter/;
# Formatter for coords
our $coordFormatter = qr/\[\d+,\d+\]/;
# Format of a unit descriptor
our $unitFormatterNonCapture = qr/UNIT:\d+ pl:\d+ vs:[^ ]+ dr:[^ ]+ ps:\[\d+,\d+\]\n?/;
# Format of a unit descriptor
our $unitFormatter = qr/UNIT:(\d+) pl:(\d+) vs:([^ ]+) dr:([^ ]+) ps:\[(\d+),(\d+)\]\n?/;
# Get x and y
sub getPositionsXandYString
{
return (shift =~ /\[(\d+),(\d+)\]/);
}
# Get all positions
sub getPositionStringsFromLine
{
return (shift =~ /$coordFormatter/gm );
}
# Get information about a unit from it's descriptor
sub getUnitInfo
{
return (shift =~ /$unitFormatter/);
}
# Get set of units from a string
sub GetUnitStringsFromGamestate
{
my $gamestate = shift;
my @units = ( $gamestate =~ /$unitFormatterNonCapture/gm );
foreach my $unit (@units)
{
chomp($unit);
}
return @units;
}
# in the format $major,$minor,$patch,$name,$sizex,$sizey,$turn,$invalidpositions+
sub GetGameInfoFromGamestate
{
my $header = shift;
(! defined $header) and die "GetGameInfoFromGamestate was not passed valid header parameter";
my @info = ($header =~ /$headerFormatter/ );
return @info;
}
# Get the units from a turn file
sub GetUnitStringsFromFile
{
my $turnFile = shift or die "GetUnitStringsFromFile needs file parameter";
# Read in the whole file method from http://www.perlmonks.org/?node_id=1952
my $text;
{
local $/=undef;
open FILE, $turnFile or die "Couldn't open file: $!";
$text = <FILE>;
close FILE;
}
return GetUnitStringsFromGamestate($text);
}
# Check version numbers against ttrts.pm version
sub verifyVersion
{
my $version_major = shift;
(! defined $version_major) and die "verifyVersion needs version_major parameter";
my $version_minor = shift;
(! defined $version_minor) and die "verifyVersion needs version_minor parameter";
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 GetGameInfoFromFile
{
my $turnFile = shift or die "GetGameInfoFromFile needs turnFile parameter";
# Read in the whole file method from http://www.perlmonks.org/?node_id=1952
my $text;
{
local $/=undef;
open FILE, $turnFile or die "Couldn't open file: $!";
$text = <FILE>;
close FILE;
}
my @info = GetGameInfoFromGamestate($text);
verifyVersion @info;
return @info;
}
# Get units from a specific player
sub GetPlayerUnits
{
my $thePlayer = shift;
(! defined $thePlayer) and die "GetPlayerUnits needs player parameter";
my @allUnits = @_;
(! @allUnits) and die "GetPlayerUnits needs units parameters";
my @myUnits;
for my $unit (@allUnits)
{
my ($unitplayer) = $unit =~ /pl:(\d+)/;
if ( $unitplayer == $thePlayer )
{
push(@myUnits,$unit);
}
}
return @myUnits;
}
sub GetTurnFileName
{
my $turn = shift;
(! defined $turn) and die "GetTurnFileName needs turn parameter";
my $turnFile = "Turn_TURN.txt";
$turnFile =~ s/TURN/$turn/;
return $turnFile;
}
sub GetCommandFileName
{
my $turn = shift;
(! defined $turn) and die "GetCommandFileName needs turn parameter";
my $player = shift;
(! defined $player) and die "GetCommandFileName needs player parameter";
my $cmdFileName = "Player_PLAYER_Turn_TURN.txt";
$cmdFileName =~ s/TURN/$turn/;
$cmdFileName =~ s/PLAYER/$player/;
return $cmdFileName;
}
# Output the commands file
sub OutputCommandsFile
{
my $turn = shift;
(! defined $turn) and die "OutputCommandsFile needs turn parameter";
my $player = shift;
(! defined $player) and die "OutputCommandsFile needs player parameter";
my $commands = shift or die "OutputCommandsFile needs commands parameter";
# Get output file
our $cmdFileName = GetCommandFileName($turn,$player);
if (! -e $cmdFileName)
{
open(my $cmdFile, '>', $cmdFileName) or die "Couldn't open '$cmdFileName' $!";
print $cmdFile $commands;
print $cmdFile "END";
close $cmdFile;
$VERBOSE and printf "Outputted $cmdFileName\n";
printf "$commands";
}
else
{
# Read in the whole file method from http://www.perlmonks.org/?node_id=1952
my $text;
{
local $/=undef;
open FILE, $cmdFileName or die "Couldn't open file: $!";
$text = <FILE>;
close FILE;
}
$text =~ s/\nEND//;
printf "Replaying $cmdFileName\n";
printf "$text\n";
}
}
# Print a game map
sub PrintGameFromGamestateString
{
my $gamestateString = shift or die "PrintGameFromGamestateString needs string parameter";
my @info = GetGameInfoFromGamestate($gamestateString);
my @units = GetUnitStringsFromGamestate($gamestateString);
# $major,$minor,$patch,$name,$sizex,$sizey,$turn,$invalidpositions+
my $gameX = $info[4];
my $gameY = $info[5];
# Shift into info to where the invalid positions are stored
my @invalids = getPositionStringsFromLine($info[7]);
my @map;
# Fill with blanks
for my $x (0 .. $gameX-1)
{
for my $y (0 .. $gameY-1)
{
$map[$x][$y] = "-";
}
}
# Fill in all invalid coordinates
foreach my $coord (@invalids)
{
my @invalidPos = getPositionsXandYString($coord);
$map[$invalidPos[0]][$invalidPos[1]] = "~";
}
# Fill with walls
foreach my $wall ( $info[8] =~ /\[(\d+,\d+)\]/g )
{
$wall =~ /(\d+),(\d+)/;
$map[$1][$2] = "|";
}
# Fill with units
for my $unit (@units)
{
my ($id,$pl,$vs,$dr,$psx,$psy) = getUnitInfo($unit);
$pl += 31;
$vs = "\e[".$pl."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");
}
}
# Print a game map
sub PrintGameFromFile
{
my $turnFile = shift or die "PrintGameFromFile needs file parameter";
# Read in the whole file method from http://www.perlmonks.org/?node_id=1952
my $text;
{
local $/=undef;
open FILE, $turnFile or die "Couldn't open file: $!";
$text = <FILE>;
close FILE;
}
PrintGameFromGamestateString($text);
}
# Print a turn
sub PrintGameMapForTurn
{
my $turn = shift;
(! defined $turn) and die "PrintGameMapForTurn needs turn parameter";
$turn = GetTurnFileName($turn);
PrintGameFromFile( $turn );
}
# Wait for a file to exist
sub WaitForFile
{
my $file = shift or die "WaitForFile needs file parameter";
while( ! -e $file )
{
select(undef, undef, undef, 0.01);
}
}
return 1;

View file

@ -1,38 +1,34 @@
#! /bin/bash
# Double check for cmakelist
if [ ! -e "CMakeLists.txt" ]; then
echo "TTRTS: No source cmakelist found"
exit
fi
# Run cmake
echo "TTRTS: Running cmake"
test ! -e build && mkdir build
cd build/
cmake ../source
cmake ..
if [[ $? != 0 ]]; then
echo "TTRTS: CMake failed, exiting Bootstrap"
exit
fi
echo "TTRTS: Running make"
make
echo "TTRTS: Performing install"
sudo make install
if [[ $? != 0 ]]; then
echo "TTRTS: make failed, exiting Bootstrap"
echo "TTRTS: Install failed, check output"
exit
fi
# Run final test to make sure
echo "TTRTS: Running tests"
./test/ttrts-test
./source/test/ttrts-test
if [[ $? != 0 ]]; then
echo "TTRTS: Tests failed, build must be broken"
exit
fi
echo "TTRTS: Generating maps"
test ! -e ../maps && mkdir ../maps
cd ../maps
./../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
echo "TTRTS: Bootstrap complete"

44
scripts/gen_manpage.sh Executable file
View file

@ -0,0 +1,44 @@
#! /bin/bash
# Used to a man page from markdown
FILE="$4"
TEMP="$FILE.tmp"
echo ".\" Man page for the ttrts project" > $TEMP
echo ".\" this man page is auto-generated, do not edit directly" >> $TEMP
echo ".TH TTRTS\ v$1.$2.$3 6 $(date +%Y-%m-%d) http://mdiluz.github.io/ttrts/" >> $TEMP
# NOTE: For the OSX version of sed we use -E, which on linux appears be an undocumented switch for -r
# we also have to use [A-Za-z] instead of \w for some reason
# as well as escaped new line characters and literal tabs instead of \n and \t
# sections to section headers
# sub-sections in man page sub-sections
# 4-space lines to tabs
# tab starts removed
# Environment variables in bold
# User variables in italics
# remove all line-widths
# Put all back-ticks quotes in bold
# underline mapfile opt
# ensure name section uses correct
cat "$5" \
| sed -E 's/^# ([A-Za-z]+)/.SH \1/g' \
| sed -E 's/^##+ ([A-Za-z]+)/.SS \1/g' \
| sed -E 's/^ (.*)$/\
\1\
/g' \
| sed -E 's/^ //g' \
| sed -E 's/\$\{([A-Za-z]+)\}/\\fB\$\1\\fR/g' \
| sed -E 's/\{([A-Za-z]+)\}/\\fI\1\\fR/g' \
| sed -E 's/-----+//g' \
| sed -E 's/`(.*)`/\\fB\1\\fR/g' \
| sed -E 's/MAPFILE/\\fImapfile\\fR/g' \
| sed -E 's/HOSTNAME/\\fIhostname\\fR/g' \
| sed -E 's/ ttrts -/ ttrts \\-/g' >> $TEMP
if [ ! -e $FILE ] || [ ! -z "$( diff $FILE $TEMP )" ]; then
mv -f $TEMP $FILE
fi

13
scripts/gen_maps.sh Executable file
View file

@ -0,0 +1,13 @@
#! /bin/bash
# Use to generate the ttrts maps
ttrtsgen=$1
test ! -e maps && mkdir maps # Make maps directory if needed
if [ ! -e maps ]; then
exit 1
fi
cd maps
$ttrtsgen

18
scripts/gen_usage.sh Executable file
View file

@ -0,0 +1,18 @@
#! /bin/bash
# Used to generate usage text from markdown
FILE="$1"
TEMP="${FILE}_tmp"
cat README.md \
| sed -E 's/^#+ //g' \
| sed -E 's/^ /\\t/g' \
| sed -E 's/^ /\\t/g' \
| sed -E 's/^/\"/' \
| sed -E 's/$/\\n\"/' \
> $TEMP
# If no difference
if [ ! -e $FILE ] || [ ! -z "$( diff $TEMP $FILE )" ]; then
mv -f $TEMP $FILE
fi

18
scripts/gen_version_header.sh Executable file
View file

@ -0,0 +1,18 @@
HEADER="// Auto generated ttrts version header
// do not edit manually
#ifndef _TTRTS_VERSION_H_
#define _TTRTS_VERSION_H_
#define TTRTS_VERSION_MAJOR $1
#define TTRTS_VERSION_MINOR $2
#define TTRTS_VERSION_PATCH $3
#define TTRTS_VERSION_STRING \"v$1.$2.$3\"
#endif //_TTRTS_VERSION_H_"
echo "$HEADER" > "version.h.tmp"
# If no difference
if [ ! -e version.h ] || [ ! -z "$( diff version.h version.h.tmp )" ]; then
mv -f version.h.tmp version.h
fi

23
scripts/ttrts_complete Executable file
View file

@ -0,0 +1,23 @@
# ttrts completion
test ! -z TTRTS_MAPS && TTRTS_MAPS=/usr/share/ttrts/maps/
have ttrts &&
function _ttrts
{
commandnames="--server --client --host= --host=localhost "
for filename in ${TTRTS_MAPS}/*
do
map="${filename##*/}"
commandnames+="--map=$map "
done
local cur prev
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=( $(compgen -W "${commandnames}" -- ${cur}) )
}
complete -F _ttrts ttrts

View file

@ -1,37 +1,13 @@
cmake_minimum_required( VERSION 2.8.7 )
# Set version information
set( TTRTS_VERSION_MAJOR 0 )
set( TTRTS_VERSION_MINOR 2 )
set( TTRTS_VERSION_PATCH 0 )
# 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" )
# 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=\"${TTRTS_VERSION_MAJOR}.${TTRTS_VERSION_MINOR}.${TTRTS_VERSION_PATCH}\"
)
# Subprojects
# Main libraries
add_subdirectory( ttrts )
add_subdirectory( game )
add_subdirectory( system )
# Main binaries
add_subdirectory( launcher )
add_subdirectory( client )
add_subdirectory( server )
add_subdirectory( local )
# Auxhilary binaries
add_subdirectory( test )
add_subdirectory( gen )
add_subdirectory( gen )

View file

@ -1,19 +1,121 @@
# Targets
### ttrts
Main TTRTS binary, runs from the command line and acts as host for games
# NAME
ttrts - Tiny Terminal RTS
### test (ttrts-test)
Test binary, to be compiled and run to test various functionality
# SYNOPSIS
ttrts [--server] [--client] [--host=HOSTNAME] [--map=MAPFILE]
# DESCRIPTION
ttrts is a tiny terminal based RTS that uses text files as order lists to control the units
### gen (ttrts-gen)
Binary to generate example map files
This means that any user, program or cat that can read and write to text files can play the game
# Libraries
### game
Implementation of the RTS rules and simulation
# RETURN VALUE
ttrts will return -1 on error, or the winning player on completion
### maths
Simple maths library for 2D calculations and types
# OPTIONS
--server - Run in server mode, must provide a map file
### scripts
Directory of scripts used in build process
--client - Run in client mode, must provide a hostname for a running server
--map=MAPFILE - File to read in the initial game state. Local or in ${TTRTS_MAPS}
--host=HOSTNAME - Name of host to connect to in client mode
# USAGE
When invoked, ttrts will set up the game in a directory within ${TTRTS_GAMES} by the name of the map
The files in this directory can be read and interpreted by human, robot or cat
ttrts will then await order files from each participant
Once all order files have been received ttrts will calculate the turn and output a new gamestate file
In server and client mode, the client will output and read in these files while the server simulates the game
This process repeats until the game is over
# ENVIRONMENT
${TTRTS_MAPS} - Map file lookup location, defaults to `/usr/share/ttrts/maps/`
${TTRTS_GAMES} - Game directory for I/O, defaults to `/tmp/`
-----------------------------------------------------------
# FILES
`/usr/share/ttrts/maps/` holds a sample set of maps
## Gamestate File
Turn_{TURNNUMBER}.txt
### Contents
===== ttrts v{MAJOR}.{MINOR}.{PATCH} =====
NAME:{GAMENAME}
SIZE:[{X},{Y}]
TURN:{TURNNUMBER}
WALL:[{X},{Y}][{X},{Y}][{X},{Y}]...{repeat for all walls}
~~~~
UNIT:{ID} pl:{PLAYER} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]
... {continue for all units}
END
## Order File
Player_{PLAYER_ID}_Turn_{TURN_NUMBER}.txt
### Contents
ORDER:{ORDER_CHAR} id:{UNIT_ID}
... {continue for all orders}
END
-----------------------------------------------------------
# SERVER/CLIENT
When in server or client mode, the game can be played across a network. If desired, a player could design an AI to act as a client instead of using the client mode and intermediary filesystem.
## Protocol
The server is accesible on port 11715
To perform the handshake the server will write to the socket with the format "player PLAYER_ID name GAME_NAME", it will expect this exact information to be written back to in reply.
Once handshake is performed, the server will write to the socket in the form of the Gamestate file as above.
The server will then wait for a new-line delimited and END terminated list of orders
This will be repeated until the game is over
-----------------------------------------------------------
# GAMEPLAY
The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board
Each turn, the client outputs a gamestate file and waits for an order file from each player
All commands are evaluated simultaneously with friendly fire enabled by default
The game is over when any of three conditions are met -
* All remaining units are controlled by a single player
* No units are left (draw)
* All units left are unable to move (draw)
# UNITS
Each unit occupies a single tile on the board, facing in a compass direction (NESW)
Units will only accept orders from their owner
Units can receive only a single order each turn
Units cannot occupy the same tile as other units/walls
# ORDERS
### F - Move unit [F]orward one space, leaving a wall
This wall will remain until the end of the game, blocking movement to that tile
Movement orders have no effect if impossible, eg.
* Attempting to move outside of map
* Attempting to move on to tile occupied by unit/wall
### L/R - Rotate unit [L]eft or [R]ight
Unit will rotate clockwise or counter-clockwise, this order cannot fail
### A - [A]ttack in straight line in front of unit
Attack will continue forward until unit can't progress, all units within the path of the attack are destroyed.

View file

@ -0,0 +1,23 @@
# ====================== ttrts =======================
# Project name
project( ttrts-client )
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
../system
../ttrts
)
# Add the sources
set( SOURCES
client.cpp
)
# Add the executable
add_executable( ${PROJECT_NAME} ${SOURCES} )
# dependent on main ttrts libary
target_link_libraries( ${PROJECT_NAME} ttrts ttrts-system )
# Installation target
install( TARGETS ${PROJECT_NAME} DESTINATION bin )

69
source/client/client.cpp Normal file
View file

@ -0,0 +1,69 @@
#include <iostream>
#include "net.h"
#include "game.h"
#include "error.h"
#include "filesystem.h"
int main(int argc, char* argv[])
{
// must provide information
if (argc < 2)
fatal_error("Usage: ttrts-client HOST");
std::string hostname = argv[1];
sockaddr_in serv_addr; // Server address
memset(&serv_addr,0, sizeof(serv_addr));
// Set the server to AF_INET
serv_addr.sin_family = AF_INET;
// Set our server address port to the port number provided
serv_addr.sin_port = htons(TTRTS_PORT);
std::cout<<"TTRTS: Connecting to "<<hostname<<std::endl;
int sockfd = ConnectToHostServer(hostname, serv_addr);
unsigned int player;
std::string gameNameString;
// Handshake with server to fetch player and gamestring
PerformClientHandshake(sockfd, player, gameNameString);
// output our information
player_t myPlayer = (player_t)player;
std::cout<<"TTRTS: I am player "<<std::to_string((int)myPlayer)<<std::endl;
std::cout<<"TTRTS: Game is "<<gameNameString<<std::endl;
// Clean out the games dir
CreateAndCleanGameDir(gameNameString);
// Buffer for messages
char buffer[1028];
memset(buffer,0,sizeof(buffer));
int n = 0; // return value for read and write calls
while ( n >= 0 )
{
std::cout<<"TTRTS: Waiting for gamestate"<<std::endl;
std::string gamestate = WaitForGamestateMessage(sockfd);
// Output the gamestate file for this game
CTTRTSGame thisGame = GetGameFromString(gamestate);
OutputGameStateFile(thisGame);
// If game over, exit with out winning player and message
if(thisGame.GameOver())
exit( OutputGameEnd(thisGame) );
// Get the order file for this turn`
std::string orders = GetOrdersFromPlayerFile(thisGame,myPlayer);
std::cout<<"TTRTS: Sending orders"<<std::endl;
std::cout<<orders<<std::endl;
// Write to the socket with the buffer
n = SendOrdersToServer(sockfd, orders);
}
return 0;
}

View file

@ -1,17 +0,0 @@
cmake_minimum_required(VERSION 2.8.7)
# game project
project( game )
include_directories(
../maths
)
# Add the sources
set( SOURCES
game.cpp
unit.cpp
order.cpp
)
add_library( game ${SOURCES} )

View file

@ -1,17 +0,0 @@
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, and each turn receives data on the status of the board.
Each player must then issue a command to each unit in their control.
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.

View file

@ -1,23 +0,0 @@
#ifndef _GAME_TYPES_H_
#define _GAME_TYPES_H_
#include <limits> // std::numeric_limits
// Type for a Player IDs
enum class player_t : char
{
Red = 0,
Green,
Yellow,
Blue,
NUM_INVALID
};
typedef unsigned short unit_id_t; // Type for unit IDs
typedef char unitvis_c; // Typedef for unit visual representations
static const unit_id_t unit_id_invalid = std::numeric_limits<unit_id_t>::max();
static const unitvis_c unitvis_invalid = std::numeric_limits<unitvis_c>::max();
#endif //_GAME_TYPES_H_

View file

@ -1,33 +0,0 @@
#include <string.h>
#include "order.h"
// Convert an order to a string
std::string GetStringFromOrder(const SOrder & order )
{
static char buff[128];
memset(buff,0,sizeof(buff));
snprintf(buff,128, ORDER_FORMATTER,
order.command,
order.unit);
return buff;
}
// Convert a string to an order
SOrder GetOrderFromString( const std::string& order )
{
SOrder ret;
char corder;
unsigned int unit;
sscanf(order.c_str(), ORDER_FORMATTER,
&corder,
&unit);
ret.command = (command_c)corder;
ret.unit = unit;
return ret;
}

View file

@ -3,8 +3,7 @@
project( ttrts-gen )
include_directories(
../game
../maths
../ttrts
)
set( SOURCES
@ -14,4 +13,15 @@ set( SOURCES
# Add the executable
add_executable( ttrts-gen ${SOURCES} )
target_link_libraries( ttrts-gen game )
target_link_libraries( ttrts-gen ttrts )
# Run the gen_usage script to generate our usage header
add_custom_target(
ttrts-gen-maps ALL
${CMAKE_SOURCE_DIR}/scripts/gen_maps.sh "${CMAKE_CURRENT_BINARY_DIR}/ttrts-gen"
)
add_dependencies(ttrts-gen-maps ${PROJECT_NAME})
# Installation target
install( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/maps DESTINATION /usr/local/share/ttrts )

View file

@ -2,6 +2,7 @@
#include <iostream>
#include <fstream>
#include "formatters.h"
void AddUnitToGame( player_t player, char vis, uvector2 vec, CTTRTSGame& game )
{
@ -15,7 +16,7 @@ void OutputGame( CTTRTSGame&& game )
{
std::ofstream output;
output.open (game.GetName() + ".txt");
output << game.GetStateAsString();
output << GetStringFromGame(game);
output.close();
__forceResetCUnitID();
@ -23,23 +24,44 @@ void OutputGame( CTTRTSGame&& game )
int main()
{
// Tiny 2v2 Game
// Tiny 1v1 Game
//------
//-G----
//----R-
//-G----
//----R-
//------
{
CTTRTSGame game(6, 6);
game.SetName("Tiny2v2");
game.SetName("Tiny2Player");
AddUnitToGame( player_t::Red, '<', uvector2(4, 2), game);
AddUnitToGame( player_t::Red, '<', uvector2(4, 4), game);
AddUnitToGame( player_t::Green, '>', uvector2(1, 1), game);
AddUnitToGame( player_t::Green, '>', uvector2(1, 3), game);
game.AddWall(uvector2(3,2));
game.AddWall(uvector2(3,3));
OutputGame(std::move(game));
}
// Basic 5v5 game
// Basic 1v1 game
// --------------------
// -G------------------
// ------------------R-
// -G------------------
// ------------------R-
// -G------------------
// ------------------R-
// -G------------------
// ------------------R-
// -G------------------
// ------------------R-
// --------------------
{
CTTRTSGame game(20, 12);
game.SetName("Big2v2");
game.SetName("Big2Player");
for ( ucoord_t y : { 2,4,6,8,10 } )
AddUnitToGame( player_t::Red, '<', uvector2(18, y), game);
@ -50,7 +72,15 @@ int main()
OutputGame(std::move(game));
}
// Chess 10v10 game
// Sort of like Chess
//GG------
//------RR
//GG------
//------RR
//GG------
//------RR
//GG------
//------RR
{
CTTRTSGame game(8, 8);
game.SetName("Chess");
@ -67,4 +97,69 @@ int main()
OutputGame(std::move(game));
}
// Medium 4 player game
//----------
//----------
//---GGGG---
//--R -- B--
//--R- -B--
//--R- -B--
//--R -- B--
//---YYYY---
//----------
//----------
{
CTTRTSGame game(10, 10);
game.SetName("Medium4Player");
for ( ucoord_t y : { 2,3,4,5 } ) {
AddUnitToGame(player_t::Red, '>', uvector2(2, y), game);
AddUnitToGame(player_t::Blue, '<', uvector2(7, y), game);
}
for ( ucoord_t x : { 2,3,4,5 } ) {
AddUnitToGame(player_t::Yellow, '^', uvector2(x,7), game);
AddUnitToGame(player_t::Green, 'v', uvector2(x,2), game);
}
// Diagonal walls
game.AddWall(uvector2(3,3));
game.AddWall(uvector2(3,6));
game.AddWall(uvector2(6,3));
game.AddWall(uvector2(6,6));
// middle walls
game.AddWall(uvector2(4,4));
game.AddWall(uvector2(4,5));
game.AddWall(uvector2(5,4));
game.AddWall(uvector2(5,5));
OutputGame(std::move(game));
}
// Medium 3 player game
//----------
//--------Y-
//--------Y-
//----------
//-G--------
//-G--------
//----------
//--------R-
//--------R-
//----------
{
CTTRTSGame game(10, 10);
game.SetName("Medium3Player");
AddUnitToGame(player_t::Red, '<', uvector2(8, 1), game);
AddUnitToGame(player_t::Red, '<', uvector2(8, 2), game);
AddUnitToGame(player_t::Green, '>', uvector2(1, 4), game);
AddUnitToGame(player_t::Green, '>', uvector2(1, 5), game);
AddUnitToGame(player_t::Yellow, '<', uvector2(8,7), game);
AddUnitToGame(player_t::Yellow, '<', uvector2(8,8), game);
OutputGame(std::move(game));
}
}

View file

@ -0,0 +1,6 @@
# ====================== ttrts =======================
# Project name
project( ttrts-perl-launch )
# Add bash completion to install
install( PROGRAMS ttrts.pl DESTINATION bin RENAME ttrts )

44
source/launcher/ttrts.pl Executable file
View file

@ -0,0 +1,44 @@
#! /usr/bin/perl
# Main ttrts launcher script
use strict;
use warnings;
use 5.0;
use Getopt::Long qw(GetOptions);
sub print_usage
{
print "Unknown option: @_\n" if ( @_ );
print "Usage: ttrts [--server] [--client] [--host=HOSTNAME] [--map=MAPFILE]\n";
exit;
}
our $VERBOSE = $ENV{"VERBOSE"};
our $server;
our $client;
our $host;
our $map;
print_usage() if ( @ARGV < 1 or
!GetOptions(
'client' => \$client,
'server' => \$server,
'host=s' => \$host,
'map=s' => \$map,
) );
# Verify we have the right parameters
print "Cannot run as both client and server\n" and exit if $client and $server;
print "Client requires hostname\n" and exit if $client and not $host;
print "Server requires mapfile\n" and exit if $server and not $map;
print "Running locally requires mapfile\n" and exit if not $server and not $client and not $map;
# Run client, server or local
my $res = -1;
$res = system("ttrts-client $host") if $client and $host;
$res = system("ttrts-server $map") if $server and $map;
$res = system("ttrts-local $map") if not $server and not $client and $map;
return $res

View file

@ -0,0 +1,23 @@
# ====================== ttrts =======================
# Project name
project( ttrts-local )
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
../system
../ttrts
)
# Add the sources
set( SOURCES
local.cpp
)
# Add the executable
add_executable( ${PROJECT_NAME} ${SOURCES} )
# dependent on main ttrts libary
target_link_libraries( ${PROJECT_NAME} ttrts ttrts-system pthread )
# Installation target
install( TARGETS ${PROJECT_NAME} DESTINATION bin )

62
source/local/local.cpp Normal file
View file

@ -0,0 +1,62 @@
#include "game.h"
#include "filesystem.h"
#include "error.h"
#include "net.h"
#include <iostream>
// =====================================================================================================================
int main(int argc, char* argv[])
{
// must provide information
if (argc < 2)
fatal_error("Usage: ttrts-local MAPFILE");
std::string gamefile = argv[1];
std::cout<<"TTRTS: Launching with "<<gamefile<<std::endl;
CTTRTSGame game = GetGameFromFile(gamefile);
// Grab the players involved
auto players = game.GetPlayers();
// Default for games
std::string ttrts_games_dir = getGamesDir();
// Empty the current game directory
if ( CreateAndCleanGameDir(game.GetName()) < 0)
return -1;
// While the game isn't finished
while ( ! game.GameOver() )
{
std::cout<<"TTRTS: Starting turn "<<game.GetTurn()<<std::endl;
// Create a turn file
if( !OutputGameStateFile(game))
fatal_error("Error: Failed to output new turn file");
// Wait for order files
for( player_t player : players)
{
// Construct the player order filename
std::string orders = GetOrdersFromPlayerFile(game, player);
// Issue the orders to the game
if( game.IssueOrders(player, orders) )
std::cerr<<"Warning: Orders for player "<<(int) player <<" failed to correctly parse"<<std::endl;
}
// Simulate turn
std::cout<<"TTRTS: Simulating this turn!"<<std::endl;
if ( game.SimulateToNextTurn() )
fatal_error("Failed to simulate game turn");
}
// Output final gamestate
OutputGameStateFile(game);
return OutputGameEnd( game );
}

View file

@ -1,28 +0,0 @@
#ifndef _BASETYPES_H_
#define _BASETYPES_H_
#include <limits> // std::numeric_limits
#include "stdlib.h" // for size_t
template<class T, size_t N>
constexpr size_t _countof(T (&)[N]) { return N; }
// Coordinate types
typedef short coord_t;
typedef unsigned short ucoord_t;
// Invalid values
static const coord_t coord_invalid = std::numeric_limits<coord_t>::max();
static const ucoord_t ucoord_invalid = std::numeric_limits<ucoord_t>::max();
// Direction representation
enum class dir_c : char
{
N = 'N',
S = 'S',
E = 'E',
W = 'W'
};
#endif //_BASETYPES_H_

View file

@ -1,9 +0,0 @@
#! /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

View file

@ -0,0 +1,23 @@
# ====================== ttrts =======================
# Project name
project( ttrts-server )
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
../system
../ttrts
)
# Add the sources
set( SOURCES
server.cpp
)
# Add the executable
add_executable( ${PROJECT_NAME} ${SOURCES} )
# dependent on main ttrts libary
target_link_libraries( ${PROJECT_NAME} ttrts ttrts-system pthread )
# Installation target
install( TARGETS ${PROJECT_NAME} DESTINATION bin )

98
source/server/server.cpp Normal file
View file

@ -0,0 +1,98 @@
#include "error.h"
#include <thread>
#include <vector>
#include <iostream>
#include <mutex>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include "net.h"
#include "filesystem.h"
void RunServerForGame(CTTRTSGame &game)
{
std::cout<<"TTRTS: Setting up server"<<std::endl;
// Server side information
sockaddr_in serv_addr = GetLocalServerAddress();
// socket File descriptor
int sockfd = SetUpServerListeningSocket(serv_addr);
// Get information about the game
std::vector<player_t> players = game.GetPlayers();
unsigned int numClients = players.size();
auto player_iterator = players.begin();
// game mutex
std::mutex gameMutex;
// Set of clients
std::vector<ClientInfo> myClients;
std::cout<<"TTRTS: Waiting for "<<numClients<<" players"<<std::endl;
// Loop while we're connecting the clients
while ( myClients.size() < numClients )
{
// information for each client
ClientInfo clientInfo;
clientInfo.player = *player_iterator;
player_iterator++;
clientInfo = WaitForClientConnection(sockfd, game.GetName(), clientInfo);
// Add out client info to our list
myClients.push_back(clientInfo);
}
std::cout<<"TTRTS: All players connected"<< std::endl;
std::cout<<"TTRTS: Hit enter to begin...";
std::cin.ignore();
// Loop for each turn
while ( !game.GameOver() )
{
// Send data to clients
std::cout<<"TTRTS: Broadcasting Gamestate"<< std::endl;
SendGamestateToClients(myClients, game, gameMutex);
// Wait for orders from clients
std::cout<<"TTRTS: Waiting for orders from players"<< std::endl;
WaitForOrdersFromClients(myClients, game, gameMutex);
std::cout<<"TTRTS: All orders recieved, simulating turn"<< std::endl;
// Step to the next turn
gameMutex.lock();
game.SimulateToNextTurn();
gameMutex.unlock();
}
// Send final state to all the clients
SendGamestateToClients(myClients, game, gameMutex);
}
int main(int argc, char* argv[])
{
// argv[1] needs to be a valid game file
if( argc < 2 )
fatal_error("Usage: ttrts-server MAPFILE");
// Set up game
CTTRTSGame game = GetGameFromFile(argv[1]);
if(game.GetNumUnits() == 0)
fatal_error("game not valid");
RunServerForGame(game);
// Return winning player and output game end
return OutputGameEnd(game);
}

View file

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 2.8.7)
# Main ttrts library
project( ttrts-system )
# Include the maths
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
../ttrts
)
# Add our sources
set( SOURCES
net.cpp
filesystem.cpp
)
# Add this library
add_library( ${PROJECT_NAME} ${SOURCES} )
target_link_libraries( ${PROJECT_NAME} ttrts pthread )

25
source/system/error.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef _TTRTS_ERROR_H_
#define _TTRTS_ERROR_H_
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
//======================================================================================================================
// Error functions
// For local fatal errors
inline void fatal_error(const char *msg)
{
std::cerr<<msg<<std::endl;
exit(1);
}
// For system fatal errors (ie. functions that set errno)
inline void fatal_perror(const char *msg)
{
perror(msg);
exit(1);
}
#endif

View file

@ -0,0 +1,248 @@
#include "filesystem.h"
#include "net.h"
#include "error.h"
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <sys/stat.h>
#include <stdio.h>
#include <stdbool.h>
#include <formatters.h>
#include <unistd.h>
#include <stdlib.h>
// =====================================================================================================================
// time for waiting between file stats
static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100);
// Check if a file exists
bool FileExists( const std::string& name )
{
struct stat buffer;
return (stat (name.c_str(), &buffer) == 0);
}
// Wait for a file to exist
void WaitForFile( const std::string& name, const std::chrono::milliseconds& time )
{
while( !FileExists(name) ) std::this_thread::sleep_for(time);
}
bool OutputGameStateFile(CTTRTSGame &game)
{
char turnFileName[128];
snprintf(turnFileName,128,"%s%s/Turn_%i.txt", getGamesDir().c_str(),game.GetName().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 = GetStringFromGame(game);
turnFile<<turnDescriptor;
turnFile.close();
return true;
}
std::string getMapsDir()
{
std::string maps = STRINGIFY(TTRTS_MAPS);
if( getenv("TTRTS_MAPS") )
{
maps = getenv("TTRTS_MAPS");
// Additional trailing slash
if( maps.back() != '/' )
maps += "/";
}
return maps;
}
std::string getGamesDir()
{
std::string dir = STRINGIFY(TTRTS_GAMES);
if( getenv("TTRTS_GAMES") )
{
dir = getenv("TTRTS_GAMES");
// Additional trailing slash
if( dir.back() != '/' )
dir += "/";
}
return dir;
}
CTTRTSGame GetGameFromFile( const std::string& filename )
{
std::string gamefile = filename;
// Default for maps
std::string ttrts_maps_dir = getMapsDir();
// If file path is not local path and file doesn't exist
if( gamefile.find("/") == std::string::npos
&& access( gamefile.c_str(), F_OK ) == -1 )
{
gamefile = ttrts_maps_dir + gamefile;
}
// If still not good
if( access( gamefile.c_str(), F_OK ) == -1 )
fatal_perror("Could not open game file");
std::ifstream file(gamefile);
std::string gameDescriptor;
// Reserve the string needed up front
file.seekg(0, std::ios::end);
gameDescriptor.reserve(file.tellg());
file.seekg(0, std::ios::beg);
// Grab the string from the file
gameDescriptor.assign((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());
if( gameDescriptor.size() == 0 )
fatal_error("failed to read in any information from gamefile");
// Create the game
return GetGameFromString(gameDescriptor);
}
std::string GetOrdersFromPlayerFile(const CTTRTSGame &game, player_t &player)
{
std::string gameDir = getGamesDir();
char playerOrderFileName[128];
snprintf(playerOrderFileName, 128, "%s%s/Player_%i_Turn_%i.txt", gameDir.c_str(),game.GetName().c_str(),(int) player, game.GetTurn());
// Wait for the player order file to be created
std::clog<<"TTRTS: Waiting for "<< playerOrderFileName << std::endl;
bool hasOrderFile = false;
while(!hasOrderFile)
{
WaitForFile(playerOrderFileName,sk_waitTime); // Wait for the file
// File must have END
// Method taken from http://stackoverflow.com/questions/11876290/c-fastest-way-to-read-only-last-line-of-text-file
std::ifstream turnFile(playerOrderFileName);
turnFile.seekg(-1, std::ios_base::end);
// Loop back from the end of file
bool keepLooping = true;
while(keepLooping) {
char ch;
turnFile.get(ch); // Get current byte's data
if((int)turnFile.tellg() <= 1) { // If the data was at or before the 0th byte
turnFile.seekg(0); // The first line is the last line
keepLooping = false; // So stop there
}
else if(ch == '\n') { // If the data was a newline
keepLooping = false; // Stop at the current position.
}
else { // If the data was neither a newline nor at the 0 byte
turnFile.seekg(-2, std::ios_base::cur); // Move to the front of that data, then to the front of the data before it
}
}
// Grab this line
std::string lastLine;
getline(turnFile,lastLine);
if(lastLine == "END")
hasOrderFile = true;
}
std::ifstream turnFile(playerOrderFileName);
// Reserve the full order string
std::string orders;
turnFile.seekg(0, std::ios_base::end);
orders.reserve(turnFile.tellg());
turnFile.seekg(0, std::ios_base::beg);
// Grab the string from the file
orders.assign((std::istreambuf_iterator<char>(turnFile)), std::istreambuf_iterator<char>());
return orders;
}
int CreateAndCleanGameDir(const std::string& gameName)
{
std::string gameDir = getGamesDir()+gameName;
struct stat info;
int ret = stat( gameDir.c_str(), &info );
if( ret == 0 && info.st_mode & S_IFDIR )
{
std::cout<<"TTRTS: " << gameDir << " game directory already exists"<<std::endl;
std::cout<<"TTRTS: Confirm to delete contents [y/N] ";
std::string input;
std::cin>>input;
if( !input.size() || std::tolower(input[0]) != 'y' )
return -1;
}
else if ( ret == 0 )
{
fatal_error("TTRTS_GAMES exists but is not directory \nAborting...");
}
// Create the game directory
char cmd2[128];
snprintf(cmd2,128, "test -d %s || mkdir %s",gameDir.c_str(),gameDir.c_str());
if( system(cmd2) == -1)
fatal_error("Error: Failed to create the game directory");
// Clean out the game directory
char cmd1[128];
snprintf(cmd1,128, "rm -rf %s/*",gameDir.c_str());
if ( system(cmd1) == -1 )
fatal_error("Error: Failed to clean the game directory");
return 0;
}
int OutputGameEnd(const CTTRTSGame &game) {
std::cout<<"TTRTS: Game Over!"<< std::endl;
// Get the winning player
player_t winningPlayer = game.GetWinningPlayer();
// Print the winner!
if ( winningPlayer != player_t::NUM_INVALID )
{
std::cout<<"TTRTS: Winner:"<<(int) winningPlayer <<std::endl;
}
else
{
std::cout<<"TTRTS: It was a draw!"<<std::endl;
}
return (int)winningPlayer;
}
int OutputgameEnd(const CTTRTSGame &game) {
std::cout<<"TTRTS: Game Over!"<< std::endl;
// Get the winning player
player_t winningPlayer = game.GetWinningPlayer();
// Print the winner!
if ( winningPlayer != player_t::NUM_INVALID )
{
std::cout<<"TTRTS: Winner:"<<(int) winningPlayer <<std::endl;
}
else
{
std::cout<<"TTRTS: It was a draw!"<<std::endl;
}
return (int)winningPlayer;
}

View file

@ -0,0 +1,29 @@
#ifndef _TTRTS_FILESYSTEM_H_
#define _TTRTS_FILESYSTEM_H_
#include <string>
#include <chrono>
#include "game.h"
#define STRINGIFY(x) _STRINGIFY(x)
#define _STRINGIFY(x) #x
bool FileExists( const std::string& name );
void WaitForFile( const std::string& name, const std::chrono::milliseconds& time );
bool OutputGameStateFile(CTTRTSGame &game);
std::string GetOrdersFromPlayerFile(const CTTRTSGame &game, player_t &player);
CTTRTSGame GetGameFromFile( const std::string& file );
std::string getMapsDir();
std::string getGamesDir();
int runFromFilesystem(int argc, char* argv[]);
int CreateAndCleanGameDir(const std::string& gameName);
#endif

261
source/system/net.cpp Normal file
View file

@ -0,0 +1,261 @@
#include "net.h"
#include "error.h"
#include <netdb.h>
#include <iostream>
#include <thread>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void WaitForOrdersFromClient(const ClientInfo info, CTTRTSGame &game, std::mutex &mut)
{
char buffer[1028]; // buffer for orders
std::clog<<"TTRTS: Waiting for player "<<(int)info.player<<" at "<<inet_ntoa(info.cli_addr.sin_addr)<<std::endl;
std::string orders;
while ( orders.find("END") == std::string::npos )
{
memset(buffer,0,sizeof(buffer));
// Read in the new socket
// read will block until the client has called write
// up to the full size of the buffer
if (read(info.clientsockfd,buffer,sizeof(buffer)-1) < 0)
fatal_perror("ERROR reading from client");
// Append the received orders
orders+=buffer;
}
std::clog<<"TTRTS: Recieved orders from "<<inet_ntoa(info.cli_addr.sin_addr)<<std::endl;
mut.lock();
game.IssueOrders(info.player , orders);
mut.unlock();
}
void WaitForOrdersFromClients(std::vector<ClientInfo> &myClients, CTTRTSGame &game, std::mutex &gameMutex)
{
// Spawn threads
std::vector<std::thread> clientThreads;
for(auto client : myClients)
{
std::thread clientThread(WaitForOrdersFromClient, client, std::ref(game), ref(gameMutex));
clientThreads.push_back(move(clientThread));
}
// Join up all the threads
for ( std::thread& thread : clientThreads )
{
thread.join();
}
}
void SendGamestateToClients(std::vector<ClientInfo> &myClients, const CTTRTSGame &game, std::mutex &gameMutex)
{
gameMutex.lock();
std::string gamestate_string = GetStringFromGame(game);
gameMutex.unlock();
for (auto client : myClients)
{
// Write to the socket with the buffer
if ( write( client.clientsockfd, gamestate_string.c_str(), gamestate_string.length() ) < 0 )
fatal_perror("ERROR sending to client");
}
}
void PerformClientHandshake(int sockfd, unsigned int &player, std::string &gameNameString)
{
char handshakeBuffer[128];
memset(handshakeBuffer,0,sizeof(handshakeBuffer));
if (read(sockfd, handshakeBuffer,sizeof(handshakeBuffer)-1) < 0)
fatal_perror("ERROR recieving handshake from server");
std::string handshake(handshakeBuffer);
if ( write( sockfd, handshake.c_str(), handshake.length()+1 ) < 0 )
fatal_perror("ERROR sending handshake to server");
char gameName[64];
if ( sscanf(handshake.c_str(),TTRTS_HANDSHAKE_FORMAT,&player,gameName) < 2 )
fatal_error("Handshake failed");
gameNameString = gameName;
}
void PerformServerHandshake(const ClientInfo &client, const std::string &game)
{
char handshake[64];
snprintf(handshake, sizeof(handshake), TTRTS_HANDSHAKE_FORMAT,(unsigned int)client.player,game.c_str());
// Send handshake
if ( write( client.clientsockfd,handshake,sizeof(handshake) ) < 0 )
fatal_perror("ERROR sending to client");
// Receive handshake
char buffer[64];
if ( read(client.clientsockfd,buffer,sizeof(buffer)-1) < 0 )
fatal_perror("ERROR reading from client");
// Verify handshake
if ( std::string(buffer) != std::string(handshake) )
fatal_error("Error in client handshake");
std::clog<<"TTRTS: Success on handshake with player "<<(int)client.player<< std::endl;
}
void TryBindSocket(int sockfd, const sockaddr_in &serv_addr)
{
std::clog<<"TTRTS: Binding to socket"<< std::endl;
int retry = 1;
while (1)
{
if(retry > 10)
fatal_error("Binding failed");
// Attempt to bind our listening socket
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0)
break;
std::cerr<<"Warning: Binding failed on try "<<retry<< std::endl;
sleep(retry);
retry++;
}
}
ClientInfo &WaitForClientConnection(int sockfd, const std::string &game, ClientInfo &clientInfo)
{
socklen_t clilen = sizeof(sockaddr_in);
// accept waits for a connection from a client
// it returns a new socket file descriptor for this connection
// client information will be stored in cli_addr
clientInfo.clientsockfd = accept(sockfd, (sockaddr *) &clientInfo.cli_addr, &clilen);
if (clientInfo.clientsockfd < 0)
fatal_perror("ERROR on accept");
std::clog<<"TTRTS: Client connected from "<<inet_ntoa(clientInfo.cli_addr.sin_addr)<<" socket "<<clientInfo.clientsockfd<< std::endl;
// Handshake with client
PerformServerHandshake(clientInfo, game);
return clientInfo;
}
int SetUpServerListeningSocket(const sockaddr_in &serv_addr)
{
int sockfd;
std::clog<<"TTRTS: Opening socket"<< std::endl;
// Create a new socket
// AF_INET is general internet socket domain
// SOCK_STREAM as messages will be read in on this socket, SOCK_DGRAM would be for packets
// 0 is for default protocol
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
fatal_perror("ERROR opening socket");
// bind our socket to this server address
TryBindSocket(sockfd, serv_addr);
// Listen on the socket for messages
// Second param is length of backlog queue, the maximum number of connections
// that can be waiting while the process is handling a single connection
// max is usually set to 5
listen(sockfd,5);
return sockfd;
}
sockaddr_in GetLocalServerAddress()
{
sockaddr_in serv_addr; // Server address
// empty the server address
memset(&serv_addr,0, sizeof(serv_addr));
// Set the server address family to AF_INET
serv_addr.sin_family = AF_INET;
// htons swaps from host byte order to network byte order
serv_addr.sin_port = htons(TTRTS_PORT);
// The host for this address is this current machine's IP, INADDR_ANY fetches this
serv_addr.sin_addr.s_addr = INADDR_ANY;
return serv_addr;
}
int ConnectToHostServer(const std::string &hostname, sockaddr_in &serv_addr)
{
// Create a new socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
fatal_perror("ERROR opening socket");
// Get the hostent information for the host by name
hostent *server = gethostbyname(hostname.c_str());
if (server == NULL)
fatal_error("ERROR, no such host");
// copy the server address into our server_addr struct
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
// Attempt to connect to the server using the socket and server address info
if (connect(sockfd, (const sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
fatal_perror("ERROR connecting");
return sockfd;
}
std::string WaitForGamestateMessage(int sockfd)
{
std::string gamestate;
char gamestateBuffer[1028];
while( gamestate.find("END") == std::string::npos )
{
memset(gamestateBuffer,0,sizeof(gamestateBuffer));
// Receive gamestate
if (read(sockfd,gamestateBuffer,sizeof(gamestateBuffer)-1) < 0)
fatal_perror("ERROR reading from client");
gamestate+=gamestateBuffer;
}
return gamestate;
}
int SendOrdersToServer(int sockfd, const std::string &orders)
{
int n = write(sockfd,orders.c_str(),orders.length());
if (n < 0)
fatal_perror("ERROR writing to socket");
return n;
}
int OutputGameEnd( CTTRTSGame& game )
{
std::cout<<"TTRTS: Game Over!"<<std::endl;
// Get the winning player
player_t winningPlayer = game.GetWinningPlayer();
// Print the winner!
if ( winningPlayer != player_t::NUM_INVALID )
{
std::cout<<"TTRTS: Winner:"<<(int) winningPlayer <<std::endl;
}
else
{
std::cout<<"TTRTS: It was a draw!"<<std::endl;
}
return (int)winningPlayer;
}

81
source/system/net.h Normal file
View file

@ -0,0 +1,81 @@
#ifndef _TTRTS_NET_H_
#define _TTRTS_NET_H_
#include <vector>
#include <mutex>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <game.h>
#include <formatters.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#define TTRTS_HANDSHAKE_FORMAT "player %u name %s"
//======================================================================================================================
// Structs for net management
// Struct for net client info
struct ClientInfo
{
sockaddr_in cli_addr;
int clientsockfd;
player_t player;
};
//======================================================================================================================
// Server side function
// Get the address of a local server
sockaddr_in GetLocalServerAddress();
// Set up a new listening socket for the server
int SetUpServerListeningSocket(const sockaddr_in &serv_addr);
// Wait for client connection on listening socket sockfd
// Will fill clientInfo with client information
ClientInfo &WaitForClientConnection(int sockfd, const std::string &game, ClientInfo &clientInfo);
// Wait for orders from a client, will not return until client has send valid orders
// Will automatically add orders to the game
void WaitForOrdersFromClient(const ClientInfo info, CTTRTSGame &game, std::mutex &mut);
// Iterates through a list of clients calling WaitForOrdersFromClient
void WaitForOrdersFromClients(std::vector<ClientInfo> &myClients, CTTRTSGame &game, std::mutex &gameMutex);
// Sends current gamestate to each client
void SendGamestateToClients(std::vector<ClientInfo> &myClients, const CTTRTSGame &game, std::mutex &gameMutex);
// Tries to bind to a socket, will attempt 10 times with longer waits between
void TryBindSocket(int sockfd, const sockaddr_in &serv_addr);
// Perform the server side handshake with a client
void PerformServerHandshake(const ClientInfo &client, const std::string &game);
//======================================================================================================================
// Client side functions
// Connect to the host, returns new socket for communication
int ConnectToHostServer(const std::string &hostname, sockaddr_in &serv_addr);
// Perform the client side handshake with the server
void PerformClientHandshake(int sockfd, unsigned int &player, std::string &gameNameString);
// Wait for gamestate message from host
std::string WaitForGamestateMessage(int sockfd);
// Send orders to the server
int SendOrdersToServer(int sockfd, const std::string &orders);
//======================================================================================================================
// Other functions
int OutputGameEnd( CTTRTSGame& game );
#endif

View file

@ -4,8 +4,7 @@
project( ttrts-test )
include_directories(
../game
../maths
../ttrts
)
set( SOURCES
@ -15,4 +14,4 @@ set( SOURCES
# Add the executable
add_executable( ttrts-test ${SOURCES} )
target_link_libraries( ttrts-test game )
target_link_libraries( ttrts-test ttrts )

View file

@ -1,3 +1,4 @@
#include <formatters.h>
#include <iostream> // std::cout
#include "order.h"
@ -25,8 +26,8 @@ const char* tests()
{
CUnit unit1;
std::string unit1Desc = CUnit::GetStringFromUnit(unit1);
CUnit unit2 = CUnit::GetUnitFromString(unit1Desc);
std::string unit1Desc = GetStringFromUnit(unit1);
CUnit unit2 = GetUnitFromString(unit1Desc);
if ( unit1 != unit2 )
return "Failed to convert an empty unit to string and back";
@ -39,8 +40,8 @@ const char* tests()
unit1.SetPlayer(player_t::Green);
unit1.SetPos(uvector2(5, 10));
std::string unit1Desc = CUnit::GetStringFromUnit(unit1);
CUnit unit2 = CUnit::GetUnitFromString(unit1Desc);
std::string unit1Desc = GetStringFromUnit(unit1);
CUnit unit2 = GetUnitFromString(unit1Desc);
if ( unit1 != unit2 )
return "Failed to convert custom unit to string and back";
@ -172,13 +173,13 @@ const char* tests()
if (game.GetUnitByIndex(0).GetID() != id )
return "Game killed the wrong unit";
if ( game.CheckForWin() != player_t::Blue )
if ( game.GetWinningPlayer() != player_t::Blue )
return "Game failed to recognise a win for the right Player";
std::string game_string = game.GetStateAsString();
CTTRTSGame game2 = CTTRTSGame::CreateFromString(game_string);
std::string game_string = GetStringFromGame(game);
CTTRTSGame game2 = GetGameFromString(game_string);
std::string game2_string = game2.GetStateAsString();
std::string game2_string = GetStringFromGame(game2);
// Try matching up the game descriptors
if( game_string != game2_string )

View file

@ -1,29 +1,46 @@
# ====================== ttrts =======================
# Project name
cmake_minimum_required(VERSION 2.8.7)
# Main ttrts library
project( ttrts )
# Include the maths
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
../maths
../game
${CMAKE_CURRENT_BINARY_DIR}
)
# Add the sources
set( SOURCES
main.cpp
# Add our sources
set( SOURCES
game.cpp
unit.cpp
formatters.cpp
)
# Add the executable
add_executable( ${PROJECT_NAME} ${SOURCES} )
# Add this library
add_library( ttrts ${SOURCES} )
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"
ttrts-version-header ALL
${CMAKE_SOURCE_DIR}/scripts/gen_version_header.sh ${TTRTS_VERSION_MAJOR} ${TTRTS_VERSION_MINOR} ${TTRTS_VERSION_PATCH}
)
add_dependencies(${PROJECT_NAME} gen_ttrts_usage)
# ttrts is dependent on this
add_dependencies( ${PROJECT_NAME} ttrts-version-header )
# Install headers
install(
FILES
game.h
unit.h
order.h
formatters.h
vector2.h
orderunitpair.h
gametypes.h
${CMAKE_CURRENT_BINARY_DIR}/version.h
DESTINATION
include/ttrts
)
# Install the ttrts static lib
install( TARGETS ttrts DESTINATION lib )

View file

@ -1,59 +0,0 @@
## NAME
ttrts - Tiny Terminal RTS v0.2.0
## SYNOPSYS
ttrts 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.
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!
## OPTIONS
MAPFILE - File to read in the initial game state from
--------------------------------------------------------------
## GAMESTATE FILE FORMAT
### Name
Turn_{TURN_NUMBER}.txt
### Contents
===== ttrts v{MAJOR}.{MINOR}.{PATCH} =====
NAME:{GAMENAME}
SIZE:[{X},{Y}]
TURN:{TURN_NUMBER}
... {any extra properties could go here}
~~~~
UNIT:{ID} pl:{PLAYER} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]
... {continue for all units}
END
## ORDER FILE FORMAT
### Name
Player_{PLAYER_ID}_Turn_{TURN_NUMBER}.txt
### Contents
ORDER:{ORDER_CHAR} id:{UNIT_ID}
... {continue for all orders}
END
### Orders
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

212
source/ttrts/formatters.cpp Normal file
View file

@ -0,0 +1,212 @@
#include "formatters.h"
#include <iostream>
#include "version.h"
// Get the game information as a string
std::string GetStringFromGame( const CTTRTSGame& game )
{
// Grab the walls
std::string walls;
if( game.GetWalls().size() == 0 )
{
walls = "NONE";
}
for ( auto wall : game.GetWalls() )
{
char pos[16];
if( snprintf(pos, 16, GAME_POS_FORMATTER , wall.x, wall.y ) < 0 )
{
return "BUFFER OVERFLOW";
}
walls += pos;
}
// Print out the header
char header[512];
if ( snprintf(header, 512, GAME_HEADER_FORMATTER ,
TTRTS_VERSION_MAJOR,
TTRTS_VERSION_MINOR,
TTRTS_VERSION_PATCH,
game.GetName().c_str(),
game.GetDimensions().x,
game.GetDimensions().y,
game.GetTurn(),
walls.c_str() ) < 0 )
{
return "BUFFER OVERFLOW";
}
// Gather unit information
std::string units;
for ( const SOrderUnitPair & pair : game.GetOrderUnitPairs() )
{
units += GetStringFromUnit(pair.unit);
units += '\n';
}
// Append the header and units
std::string state(header);
state += '\n';
state += GAME_HEADER_DELIMITER;
state += units;
state += "END";
return state;
}
// Get the game information as a string
CTTRTSGame GetGameFromString( 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
unsigned int major;
unsigned int minor;
unsigned int patch;
char name[64];
unsigned int turn;
unsigned int sizex;
unsigned int sizey;
char walls[512];
if( sscanf(header.c_str(), GAME_HEADER_FORMATTER, &major, &minor, &patch, name, &sizex, &sizey, &turn, walls) != 8 )
{
std::cerr<<"Error: Failed to properly read game state from text"<<std::endl;
return CTTRTSGame(0,0);
}
if( major != TTRTS_VERSION_MAJOR
|| minor != TTRTS_VERSION_MINOR )
{
std::cerr<<"Error: ttrts map/binary version missmatch BINARY:v"<<major<<"."<<minor<<"."<<patch<<" FILE:"<<TTRTS_VERSION_STRING<<std::endl;
}
std::vector<uvector2> walls_vector;
{
std::string walls_str = walls;
size_t pos;
while ( ( pos = walls_str.find(']') ) != std::string::npos )
{
std::string pos_string = walls_str.substr(0,pos+1);
// Use scanf to extract positions
unsigned int x;
unsigned int y;
if( sscanf(pos_string.c_str(), GAME_POS_FORMATTER, &x, &y ) != 2 )
{
return CTTRTSGame(0,0);
}
// Erase this coordinate
walls_str.erase(0,pos+1);
// Append our list
walls_vector.push_back({(ucoord_t)x,(ucoord_t)y});
}
}
CTTRTSGame game(sizex,sizey);
game.SetName(name);
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(GetUnitFromString(unit_string));
}
}
// Add all walls
for ( auto wall : walls_vector)
{
game.AddWall(wall);
}
return game;
}
// Get a string descriptor of a unit
std::string GetStringFromUnit(const CUnit& unit )
{
static char buff[128];
memset(buff,0,sizeof(buff));
snprintf(buff,128, UNIT_FORMATTER,
unit.GetID(),
(int)unit.GetPlayer(),
unit.GetVisual(),
unit.GetDir(),
unit.GetPos().x,
unit.GetPos().y );
return buff;
}
// Get a unit from a string descriptor
CUnit GetUnitFromString(const std::string& unit )
{
CUnit ret;
unsigned int id;
int player;
char vis;
char dir;
unsigned int posx;
unsigned int posy;
sscanf(unit.c_str(), UNIT_FORMATTER,
&id,
&player,
&vis,
&dir,
&posx,
&posy );
ret.ForceSetID((unit_id_t)id);
ret.SetPlayer((player_t) player);
ret.SetVisual((unitvis_c)vis);
ret.SetDir((dir_c)dir);
ret.SetPos(uvector2(posx,posy));
return ret;
}
// Convert an order to a string
std::string GetStringFromOrder(const SOrder & order )
{
static char buff[128];
memset(buff,0,sizeof(buff));
snprintf(buff,128, ORDER_FORMATTER,
order.command,
order.unit);
return buff;
}
// Convert a string to an order
SOrder GetOrderFromString( const std::string& order )
{
SOrder ret;
char corder;
unsigned int unit;
sscanf(order.c_str(), ORDER_FORMATTER,
&corder,
&unit);
ret.command = (command_c)corder;
ret.unit = unit;
return ret;
}

29
source/ttrts/formatters.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef _TTRTS_FORMATTERS_H_
#define _TTRTS_FORMATTERS_H_
#include "order.h"
#include "game.h"
#include "unit.h"
#include "string.h"
#define GAME_POS_FORMATTER "[%u,%u]"
#define GAME_HEADER_FORMATTER "==== ttrts v%u.%u.%u ====\nNAME:%s\nSIZE:" GAME_POS_FORMATTER "\nTURN:%u\nWALL:%s"
#define GAME_HEADER_DELIMITER "~~~~\n"
#define UNIT_FORMATTER "UNIT:%u pl:%u vs:%c dr:%c ps:" GAME_POS_FORMATTER
// order <--> string conversion functions
std::string GetStringFromOrder(const SOrder & order );
SOrder GetOrderFromString( const std::string& order );
// game <--> string conversion functions
CTTRTSGame GetGameFromString( const std::string& input );
std::string GetStringFromGame( const CTTRTSGame& game );
// unit <--> string conversion functions
std::string GetStringFromUnit(const CUnit& unit );
CUnit GetUnitFromString(const std::string& unit );
#endif

View file

@ -2,6 +2,7 @@
#include <algorithm>
#include <string.h>
#include "formatters.h"
CTTRTSGame::CTTRTSGame( ucoord_t c, ucoord_t r )
: dimensions( c,r )
@ -16,6 +17,7 @@ CTTRTSGame::CTTRTSGame(CTTRTSGame&& game)
, dimensions(std::move(game.dimensions))
, turn(std::move(game.turn))
, name(std::move(game.name))
, m_walls(std::move(game.m_walls))
{
}
@ -92,7 +94,7 @@ int CTTRTSGame::IssueOrder( player_t player, const SOrder & order )
}
// Verify a position
int CTTRTSGame::VerifyPos(uvector2 vec) const
int CTTRTSGame::VerifyPosIsValidMovement(uvector2 vec) const
{
// Simply check if within the bounds of our dimensions for now
if ( ( vec.x >= dimensions.x )
@ -101,6 +103,15 @@ int CTTRTSGame::VerifyPos(uvector2 vec) const
return 1;
}
// Check within our invalid positions
for ( const uvector2& invalid : m_walls)
{
if( vec == invalid )
{
return 2;
}
}
return 0;
}
@ -137,7 +148,7 @@ int CTTRTSGame::SimulateToNextTurn()
uvector2 newpos = GetNewPosition(pair);
// Verify the position is even available
bool possible = ( VerifyPos(newpos) == 0 );
bool possible = (VerifyPosIsValidMovement(newpos) == 0 );
if ( possible )
{
@ -158,6 +169,8 @@ int CTTRTSGame::SimulateToNextTurn()
// If the movement is still possible
if ( possible )
{
// Create a wall at our old position
AddWall(pair.unit.GetPos());
pair.unit.SetPos(newpos);
}
}
@ -202,7 +215,7 @@ int CTTRTSGame::SimulateToNextTurn()
{
uvector2 newpos = pair.unit.GetInFront();
// If move would be within the arena
if ( ( newpos.x <= dimensions.x-1 ) && ( newpos.y <= dimensions.y-1 ) )
if (VerifyPosIsValidMovement(newpos) == 0 )
{
pair.unit.SetPos(newpos);
@ -454,7 +467,7 @@ std::vector<player_t> CTTRTSGame::GetPlayers() const
}
// Check if we have a win state
player_t CTTRTSGame::CheckForWin() const
player_t CTTRTSGame::GetWinningPlayer() const
{
// Array of units for each Player
unsigned int units[(int) player_t::NUM_INVALID];
@ -490,58 +503,30 @@ player_t CTTRTSGame::CheckForWin() const
return winningPlayer;
}
// Get the game information as a string
std::string CTTRTSGame::GetStateAsString() const
// Check if any of the units can move
bool CTTRTSGame::UnitsCanMove() const
{
// Print out the header
char header[64];
snprintf(header, 512, GAME_HEADER_FORMATTER , name.c_str(), dimensions.x, dimensions.y, turn );
// Gather unit information
std::string units;
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
for( const SOrderUnitPair& pair: m_OrderUnitPairs )
{
units += CUnit::GetStringFromUnit(pair.unit);
units += '\n';
uvector2 pos = pair.unit.GetPos();
// Assume if unit is adjacent to any valid tile, then it can move there
if( VerifyPosIsValidMovement(pos + vector2(1, 0) ) == 0
|| VerifyPosIsValidMovement(pos + vector2(0, 1)) == 0
|| VerifyPosIsValidMovement(pos + vector2(-1, 0)) == 0
|| VerifyPosIsValidMovement(pos + vector2(0, -1)) == 0 )
{
return true;
}
}
// Append the header and units
std::string state(header);
state += '\n';
state += GAME_HEADER_DELIMITER;
state += units;
state += "END";
return state;
return false;
}
// Get the game information as a string
CTTRTSGame CTTRTSGame::CreateFromString( const std::string& input )
// Check if the game is over
bool CTTRTSGame::GameOver() const
{
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;
return ( (GetWinningPlayer() != player_t::NUM_INVALID ) // We have a winning player
|| GetNumUnits() == 0
|| !UnitsCanMove() ); // OR we have no units
}

View file

@ -6,9 +6,6 @@
#include "order.h"
#include "orderunitpair.h"
#define GAME_HEADER_FORMATTER "NAME:%s\nSIZE:[%u,%u]\nTURN:%u"
#define GAME_HEADER_DELIMITER "~~~~\n"
// Full TTRTS Game class
// Stores information about the game
// Can convert from a string or to a string
@ -16,9 +13,6 @@ class CTTRTSGame
{
public:
// 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);
@ -30,13 +24,16 @@ public:
// 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
// best practice would be to call with GetNumUnits() == 0
player_t CheckForWin() const;
// Check for winning player, returns invalid for no win state reached
// Note: this function will return invalid if a draw was reached
// do not rely on this to test for end state
player_t GetWinningPlayer() const;
// Get the game information as a string
std::string GetStateAsString() const;
// Check if the game is over
bool GameOver() const;
// Check if any of the units can move
bool UnitsCanMove() const;
// Issue orders to the game, returns non-zero if orders are incorrect
int IssueOrders( player_t player, const std::string& orders );
@ -58,6 +55,8 @@ public:
const CUnit& GetUnitByIDConst( unit_id_t id ) const;
const SOrder & GetOrderByIDConst( unit_id_t id ) const;
inline const OrderUnitPairVector& GetOrderUnitPairs() const { return m_OrderUnitPairs; }
// Get dimensions
inline const uvector2& GetDimensions() const { return dimensions; }
@ -73,6 +72,12 @@ public:
// Get a vector of the players in the current game
std::vector<player_t> GetPlayers() const;
// Get the vector of wall positions
inline std::vector<uvector2> GetWalls() const { return m_walls; }
// Add an invalid position
inline void AddWall(uvector2 vec) { m_walls.push_back(vec); }
private:
// Check for a pass through
@ -80,7 +85,7 @@ private:
// Verify any order or position - non-zero is error
int VerifyOrder( player_t player, const SOrder & order ) const;
int VerifyPos( uvector2 vec ) const;
int VerifyPosIsValidMovement(uvector2 vec) const;
// Get a units new position after an order
uvector2 GetNewPosition( const SOrderUnitPair & pair ) const;
@ -95,6 +100,7 @@ private:
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
std::vector<uvector2> m_walls; // Vector of wall positions
};

45
source/ttrts/gametypes.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef _GAME_TYPES_H_
#define _GAME_TYPES_H_
#include <limits> // std::numeric_limits
#include "stdlib.h" // for size_t
// Type for a Player IDs
enum class player_t : char
{
Red = 0,
Green,
Yellow,
Blue,
NUM_INVALID
};
// Type for unit IDs
typedef unsigned short unit_id_t;
static const unit_id_t unit_id_invalid = std::numeric_limits<unit_id_t>::max();
// Typedef for unit visual representations
typedef char unitvis_c;
static const unitvis_c unitvis_invalid = std::numeric_limits<unitvis_c>::max();
// Coordinate types
typedef short coord_t;
static const coord_t coord_invalid = std::numeric_limits<coord_t>::max();
typedef unsigned short ucoord_t;
static const ucoord_t ucoord_invalid = std::numeric_limits<ucoord_t>::max();
// Direction representation
enum class dir_c : char
{
N = 'N',
S = 'S',
E = 'E',
W = 'W'
};
// Helper function for count of an array
template<class T, size_t N>
constexpr size_t _countof(T (&)[N]) { return N; }
#endif //_GAME_TYPES_H_

View file

@ -1,234 +0,0 @@
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <sys/stat.h>
#include <stdio.h>
#include <stdbool.h>
#include "game.h"
#include "version.h"
static const char* sk_usage =
#include "usage.h"
;
// 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);
}
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();
// Append the version number
turnDescriptor = std::string("==== ttrts v")
+ sk_ttrts_version_string
+ std::string(" ====\n")
+ turnDescriptor;
turnFile<<turnDescriptor;
turnFile.close();
return true;
}
// Main program entry point
int main(int argc, char* argv[])
{
// If no args, print usage
if ( argc == 1 )
{
std::cerr<<sk_usage<<std::endl;
return 1;
}
// Attempt to open the game file
std::string gameFile = argv[1];
std::ifstream file(gameFile);
if( file.bad() )
{
std::cerr<<"Error: "<<gameFile<<" file not found"<<std::endl;
return 1;
}
std::cout<<"Launching TTRTS!"<<std::endl;
std::string gameDescriptor;
// Reserve the string needed up front
file.seekg(0, std::ios::end);
gameDescriptor.reserve(file.tellg());
file.seekg(0, std::ios::beg);
// Grab the string from the file
gameDescriptor.assign((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());
if( gameDescriptor.size() == 0 )
{
std::cerr<<"Error: failed to read in any information from "<<gameFile<<std::endl;
return 1;
}
// Create the game
CTTRTSGame game = CTTRTSGame::CreateFromString(gameDescriptor);
// Grab the players involved
auto players = game.GetPlayers();
// Current game directory
std::string gameDir = "ttrts_" + game.GetName();
// Empty the current game directory
struct stat info;
int ret = stat( gameDir.c_str(), &info );
if( ret == 0 && info.st_mode & S_IFDIR )
{
std::cout<< gameDir << " already exists"<<std::endl;
std::cout<<"Confirm to delete contents [y/N] ";
std::string input;
std::cin>>input;
if( !input.size() || std::tolower(input[0]) != 'y' )
{
std::cerr<<"Aborting..."<<std::endl;
return 1;
}
}
else if ( ret == 0 )
{
std::cerr<< gameDir << " exists but is not directory \nAborting..."<<std::endl;
return 1;
}
// Create the game directory
char cmd2[128];
snprintf(cmd2,128, "test -d %s || mkdir %s",gameDir.c_str(),gameDir.c_str());
system(cmd2);
// Clean out the game directory
char cmd1[128];
snprintf(cmd1,128, "rm -rf %s/*",gameDir.c_str());
system(cmd1);
// While the game hasn't been won
player_t winningPlayer;
while ( ((winningPlayer = game.CheckForWin()) == player_t::NUM_INVALID) // We have a winning player
&& game.GetNumUnits() > 0 ) // We have no units left
{
std::cout<<"Starting turn "<<game.GetTurn()<<std::endl;
// Create a turn file
if( !OutputGameStateFile(game, gameDir))
{
std::cerr<<"Error: Failed to output new turn file" << std::endl;
return 1;
}
// Wait for order files
for( player_t player : players)
{
// Construct the player order filename
char playerOrderFileName[128];
snprintf(playerOrderFileName, 128, "%s/Player_%i_Turn_%i.txt", gameDir.c_str(), (int) player, game.GetTurn());
// Wait for the player order file to be created
std::cout<<"Waiting for "<< playerOrderFileName <<std::endl;
bool hasOrderFile = false;
while(!hasOrderFile)
{
WaitForFile(playerOrderFileName,sk_waitTime); // Wait for the file
// File must have END
// Method taken from http://stackoverflow.com/questions/11876290/c-fastest-way-to-read-only-last-line-of-text-file
std::ifstream turnFile(playerOrderFileName);
turnFile.seekg(-1,std::ios_base::end);
// Loop back from the end of file
bool keepLooping = true;
while(keepLooping) {
char ch;
turnFile.get(ch); // Get current byte's data
if((int)turnFile.tellg() <= 1) { // If the data was at or before the 0th byte
turnFile.seekg(0); // The first line is the last line
keepLooping = false; // So stop there
}
else if(ch == '\n') { // If the data was a newline
keepLooping = false; // Stop at the current position.
}
else { // If the data was neither a newline nor at the 0 byte
turnFile.seekg(-2,std::ios_base::cur); // Move to the front of that data, then to the front of the data before it
}
}
// Grab this line
std::string lastLine;
std::getline(turnFile,lastLine);
if(lastLine == "END")
hasOrderFile = true;
}
std::ifstream turnFile(playerOrderFileName);
// Reserve the full order string
std::string orders;
turnFile.seekg(0, std::ios::end);
orders.reserve(turnFile.tellg());
turnFile.seekg(0, std::ios::beg);
// Grab the string from the file
orders.assign((std::istreambuf_iterator<char>(turnFile)),std::istreambuf_iterator<char>());
// Issue the orders to the game
if( game.IssueOrders(player, orders) )
std::cerr<<"Warning: Orders for player "<<(int) player <<" failed to correctly parse"<<std::endl;
}
// Simulate turn
std::cout<<"Simulating this turn!"<<std::endl;
if ( game.SimulateToNextTurn() )
{
std::cerr << "Error: Failed to simulate for turn "<<game.GetTurn()<<std::endl;
return 1;
}
}
// Output final gamestate
OutputGameStateFile(game, gameDir);
// Print the winner!
if ( winningPlayer != player_t::NUM_INVALID )
{
std::cout<<"Game over! Winner:"<<(int) winningPlayer <<std::endl;
}
else
{
std::cout<<"Game over! It was a draw!"<<std::endl;
}
return 0;
};

View file

@ -47,8 +47,4 @@ inline bool SOrder::operator== ( const SOrder & rhs ) const
// Typedef a vector of orders
typedef std::vector<SOrder> COrderVector;
// string <--> order conversion functions
std::string GetStringFromOrder(const SOrder & order );
SOrder GetOrderFromString( const std::string& order );
#endif //_ORDERS_H_

View file

@ -50,52 +50,6 @@ CUnit CUnit::GetUnitFromVis( unitvis_c vis )
return unit;
}
// Get a string descriptor of a unit
std::string CUnit::GetStringFromUnit(const CUnit& unit )
{
static char buff[128];
memset(buff,0,sizeof(buff));
snprintf(buff,128, UNIT_FORMATTER,
unit.unit_id,
(int)unit.player_id,
unit.unit_vis,
unit.dir,
unit.pos.x,
unit.pos.y );
return buff;
}
// Get a unit from a string descriptor
CUnit CUnit::GetUnitFromString(const std::string& unit )
{
CUnit ret;
unsigned int id;
int player;
char vis;
char dir;
unsigned int posx;
unsigned int posy;
sscanf(unit.c_str(), UNIT_FORMATTER,
&id,
&player,
&vis,
&dir,
&posx,
&posy );
ret.unit_id = (unit_id_t)id;
ret.player_id = (player_t) player;
ret.unit_vis = (unitvis_c)vis;
ret.dir = (dir_c)dir;
ret.pos = uvector2(posx,posy);
return ret;
}
// Plain constructor
CUnit::CUnit()
: unit_id ( get_unique_unit_id() )

View file

@ -7,8 +7,6 @@
#include "gametypes.h"
#include "vector2.h"
#define UNIT_FORMATTER "UNIT:%u pl:%u vs:%c dr:%c ps:[%u,%u]"
// force a reset of the unit ID value
void __forceResetCUnitID();
@ -20,10 +18,6 @@ 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();
@ -61,6 +55,9 @@ public:
dir_c TurnRight();
dir_c TurnAround();
// Force set an ID
inline void ForceSetID( unit_id_t id ) { unit_id = id; }
private:
// Update my visual must be called when setting direction

View file

@ -1,7 +1,7 @@
#ifndef _VECTOR2_H_
#define _VECTOR2_H_
#include "mathtypes.h"
#include "gametypes.h"
struct uvector2;

View file

@ -1,9 +0,0 @@
#ifndef _TTRTS_VERSION_H_
#define _TTRTS_VERSION_H_
static const int sk_ttrts_version_major = TTRTS_VERSION_MAJOR;
static const int sk_ttrts_version_minor = TTRTS_VERSION_MINOR;
static const int sk_ttrts_version_patch = TTRTS_VERSION_PATCH;
static const char* sk_ttrts_version_string = TTRTS_VERSION_STRING;
#endif //_TTRTS_VERSION_H_