Single Player
From Thousand Parsec Wiki
Single-player mode is a cross-client effort to allow users to configure a local server and AI opponents automatically via an easy wizard interface. This is being developed for Google Summer of Code 2008 by Aaron Mavrinac / ezod.
Contents |
Configuration
Configuration in tpclient-pywx is done via a wizard-style interface available from the initial "Connect To Server" screen. The wizard is a user-friendly front end to the functions in the SinglePlayer module of libtpclient-py.
| | | | | | |
Flowchart
Implementation
The SinglePlayerGame object in libtpclient-py provides all back end functionality:
from tp.client.SinglePlayer import SinglePlayerGame game = SinglePlayerGame()
The player first chooses a ruleset from the ruleset list:
print 'SELECT RULESET'
while game.rname not in game.rulesets:
game.rname = raw_input('Choose a ruleset from ' + str(game.rulesets) + ': ')
If there are multiple servers implementing the chosen ruleset, a server must be chosen:
slist = game.list_servers_with_ruleset()
if len(slist) > 1:
print 'SELECT SERVER'
print 'There are multiple servers implementing the', game.rname, 'ruleset.'
while game.sname not in slist:
game.sname = raw_input('Choose a server from ' + str(slist) + ': ')
else:
game.sname = slist[0]
Ruleset options, if any, may be specified:
paramlist = game.serverlist[game.sname]['rulesets'][game.rname]['parameters']
if len(paramlist):
print 'RULESET OPTIONS'
for param in paramlist.keys():
game.rparams[param] = raw_input(paramlist[param]['longname'] + ' (' + paramlist[param]['type'] + '): ')
Likewise for server options:
paramlist = game.serverlist[game.sname]['parameters']
if len(paramlist):
print 'SERVER OPTIONS'
for param in paramlist.keys():
game.sparams[param] = raw_input(paramlist[param]['longname'] + ' (' + paramlist[param]['type'] + '): ')
Opponents may then be added:
ailist = game.list_aiclients_with_ruleset()
if len(ailist):
print 'ADD OPPONENTS'
while len(ailist) > 0:
aiuser = raw_input('Enter an opponent name (leave blank to stop adding opponents): ')
if aiuser:
ainame =
while ainame not in ailist:
ainame = raw_input('Please select an AI client from ' + str(ailist) + ': ')
aiparams = {}
paramlist = game.ailist[ainame]['parameters']
for param in paramlist.keys():
aiparams[param] = raw_input(paramlist[param]['longname'] + ' (' + paramlist[param]['type'] + '): ')
game.add_opponent(ainame, aiuser, aiparams)
else:
break
When everything is complete, start the game:
port = game.start()
if port:
# start player's client and connect to localhost:port
Finally, to clean up when the game is done, stop it:
game.stop()
Adding Single-Player Support
In order for a component to be available for single-player mode, it must install an XML description of itself (using tpconfig.dtd) in a shared directory (currently, libtpclient-py looks in /usr/share/tp, /usr/local/share/tp, /opt/tp, and ./singleplayer in its own install path).
In the XML files, there are forced parameters (simply added to the command line as-is) and regular parameters (offered to the player in the wizard). Any options required for single-player mode (including those which allow for non-interactive control) should be forced parameters; any that are relevant to a single-player game and can be set by the player should be regular parameters.
Servers
Server files live in the servers subdirectory.
tpserver-cpp.xml
<?xml version="1.0"?>
<!DOCTYPE tpconfig SYSTEM "../tpconfig.dtd">
<tpconfig>
<server name="tpserver-cpp">
<longname>tpserver-cpp</longname>
<version>0.5.1</version>
<description>Thousand Parsec server in C++.</description>
<commandstring>tpserver-cpp -d --configure /dev/null --ruleset %(rname)s --tp_port %(port)d</commandstring>
<forced>--game_load yes</forced>
<forced>--game_start yes</forced>
<forced>--network_start yes</forced>
<forced>--add_players yes</forced>
<forced>--autoadd_players yes</forced>
<forced>--turn_player_threshold 100</forced>
<parameter name="turnlength" type="I">
<longname>Turn Length</longname>
<description>Maximum length of a turn (0 for unlimited).</description>
<default>0</default>
<commandstring>--turn_length_over_threshold %d</commandstring>
</parameter>
<ruleset name="minisec">
<longname>MiniSec</longname>
<version>0.3</version>
<description>The first milestone game. Simple test of being able to issue orders and perform simple combat.</description>
</ruleset>
<ruleset name="mtsec">
<longname>MTSec</longname>
<version>0.0</version>
<description>The second milestone game. It is designed to both be a fun game and to exercise the object design capabilities of the framework.</description>
</ruleset>
</server>
</tpconfig>
Ruleset Modules
Rulesets are part of the server they are modules for, and thus also live in the servers subdirectory. Rulesets built into the server may simply be added to that server's XML file, as shown in tpserver-cpp-0.5.1.xml above, but in cases where the ruleset is installed separately, something like the following may be used. The server name must match the server to which the ruleset belongs.
tpserver-cpp-risk.xml
<?xml version="1.0"?>
<!DOCTYPE tpconfig SYSTEM "../tpconfig.dtd">
<tpconfig>
<server name="tpserver-cpp">
<ruleset name="risk">
<longname>Risk</longname>
<version>0</version>
<description>A modified Risk ruleset for tpserver-cpp.</description>
<parameter name="map" type="S">
<longname>Map</longname>
<description>Map file to load.</description>
<default />
<commandstring>--risk_map %s</commandstring>
</parameter>
<parameter name="rfcstart" type="I">
<longname>Reinforcements</longname>
<description>Starting reinforcement count.</description>
<default />
<commandstring>--risk_rfc_start %d</commandstring>
</parameter>
</ruleset>
</server>
</tpconfig>
AI Clients
AI client files live in the aiclients subdirectory.
daneel-ai.xml
<?xml version="1.0"?>
<!DOCTYPE tpconfig SYSTEM "../tpconfig.dtd">
<tpconfig>
<aiclient name="daneel-ai">
<longname>daneel-ai</longname>
<version>0</version>
<description>An advanced rule based AI.</description>
<commandstring>daneel-ai -f rules-%(rname)s -u tp://%(user)s:password@localhost:%(port)d</commandstring>
<rules>risk</rules>
<rules>rfts</rules>
</aiclient>
</tpconfig>
gencon.xml
<?xml version="1.0"?>
<!DOCTYPE tpconfig SYSTEM "../tpconfig.dtd">
<tpconfig>
<aiclient name="gencon">
<longname>GenCon</longname>
<version>0</version>
<description>An AI based around a genetic algorithm.</description>
<commandstring>java GenConRunner %(rname)s tp://%(user)s:password@localhost:%(port)d</commandstring>
<rules>risk</rules>
<rules>rfts</rules>
<parameter name="difficulty" type="I">
<longname>Difficulty</longname>
<description>Difficulty level (1 - 9).</description>
<default>8</default>
<commandstring>%d n</commandstring>
</parameter>
</aiclient>
</tpconfig>
tpconfig.dtd
<!ELEMENT tpconfig (server?, aiclient?) > <!ELEMENT longname (#PCDATA) > <!ELEMENT version (#PCDATA) > <!ELEMENT description (#PCDATA) > <!ELEMENT forced (#PCDATA) > <!ELEMENT default (#PCDATA) > <!ELEMENT commandstring (#PCDATA) > <!ELEMENT rules (#PCDATA) > <!ELEMENT parameter (longname?, description?, default?, commandstring?) > <!ATTLIST parameter name ID #REQUIRED type NMTOKEN #REQUIRED > <!ELEMENT ruleset (longname?, version?, description?, forced*, parameter*) > <!ATTLIST ruleset name ID #REQUIRED > <!ELEMENT server (longname?, version?, description?, commandstring?, forced*, parameter*, ruleset+) > <!ATTLIST server name ID #REQUIRED > <!ELEMENT aiclient (longname?, description?, commandstring?, forced*, parameter*, rules+) > <!ATTLIST aiclient name ID #REQUIRED >
Testing
You can test single-player mode using the following procedure:
- Clone and compile the 'config' branch of the tpserver-cpp repository.
- Install the 'logilab-constraint' Python package if necessary (dependency for daneel-ai).
- Clone the daneel-ai repository.
- Clone the 'singleplayer' branch of the tpclient-pywx repository.
- Switch the libtpclient-py submodule in tpclient-pywx to the 'singleplayer' branch.
- Modify the commandstring elements in tpserver-cpp.xml and daneel-ai.xml (bundled with libtpclient-py under tp/client/singleplayer) to point to the proper paths (will be done by installers in the future).
- Run tpclient-pywx.
- Click the Single Player button on the connection window, and start a game with the Risk ruleset and daneel-ai opponent(s).
If you find any bugs, please send a detailed description to ezod.

