GPN7:Party Programming Game (Workshop)

aus dem Wiki des Entropia e.V., CCC Karlsruhe
Party5.jpg

Wo ist heute nacht die Party? Wie komme ich da hin? Wie sorge ich dafür, dass die ganzen Spacken nicht kommen? Wie werde ich zur nächsten Party wieder eingeladen? Wir coden Studenten, die alle versuchen, mehr Spaß als die anderen zu haben.

ParTTY ist ein Multiplayer Programming Game. Wie schon die letzten Jahre wird es hier eine kurze Einführung geben. Der Server läuft dann die ganze Zeit. Oder auch nicht. Wir warten noch auf unseren eigenen internen Server. Stay tuned.

Worum geht's?

Party!!1

Jede Spielrunde geht ueber mehrere Tage. Jeder Tag besteht aus der Tagesphase und der Nachtphase. Tagsueber wird kommuniziert und umhergelaufen. Nachts wird gefeiert und es gibt dafuer Punkte. Jeder Mitspieler schreibt sich ein Programm, welches sich mit dem Server verbindet und einen Spieler steuert.

Es gibt 10 unterschiedliche Orte. An einem dieser Orte findet jede Nacht eine Party statt. Ein oder zwei zufaellige Spieler bekommen morgens Bescheid gesagt, wo die Party stattfindet. Sie koennen nun entscheiden, welchen anderen Spieler die davon in Kenntnis setzen. Um mit anderen Spielern zu kommunizieren muessen sich beide am selben Ort befinden oder sich bereits von vorherigen Partys kennen (aka Handynummern getauscht haben). Einem anderen Spieler kann dabei mitgeteilt werden, wo eine Party stattfindet, wo keine Party stattfindet, dass ein anderer Spieler luegt oder ein anderer Spieler die Wahrheit sagt. Natuerlich muss keine der gemachten Aussagen wahr sein.

Damit die nachts stattfindende Party Spass macht und dafuer Punkte verteilt werden, muss sich eine Mindestanzahl von Spielern dort einfinden. Sind es zu wenig Spieler, so bekommt niemand Punkte. Sind es zu viele, bekommt zwar jeder Punkte, allerdings verringert sich dadurch die Punkteanzahl.

Technisches

Manchmal klappt's nicht so.

Der Server ist per TCP erreichbar und spricht ein einfaches Klartextprotokoll. Nachrichten umfassen immer genau eine durch Newline beendete Zeile. Jede Art von Nachricht hat dabei folgendes Format:

<TYP> <#args> [<arg1> <arg2> ...]  : <Human-Readable>

TYP gibt an, um welche Art von Nachricht es sich handelt. #args gibt an, viel Parameter diese Nachricht hat. Anschliessend folgen die einzelnen Parameter. Zu Debuggingzwecken, und um das Protokoll kennen zu lernen folgt am Ende der Zeile jeweils noch eine Menschlesbare Version der Nachricht.

TYP, #args und die Argumente sind immer durch SPACE getrennt. Es gibt zwei Arten von Argumenten: Ganzzahlen und Strings. Ganzzahlen stehen direkt in ihrer Stringrepresentation da, Strings werden durch umschliessende doppelte Anfuehrungszeichen gekennzeichnet.

Hier die Ausgabe nach dem Einloggen und einem kompletten (langweiligen) Tages-Nacht-Zyklus:

71 0                                    : Welcome to the party game. Please log in.
login foo bar
20 1 "foo"                              : player foo has entered the game
70 0                                    : welcome foo
41 1 10                                 : the next game will start in 10 seconds
41 1 9                                  : the next game will start in 9 seconds
41 1 8                                  : the next game will start in 8 seconds
41 1 7                                  : the next game will start in 7 seconds
41 1 6                                  : the next game will start in 6 seconds
41 1 5                                  : the next game will start in 5 seconds
41 1 4                                  : the next game will start in 4 seconds
41 1 3                                  : the next game will start in 3 seconds
41 1 2                                  : the next game will start in 2 seconds
41 1 1                                  : the next game will start in 1 seconds
42 0                                    : starting
43 1 "p58"                              : you are 'p58'
22 2 "p58" 0                            : you spawned in 0
60 1 0                                  : there is a party at 0
65 1 10                                 : you have 10 actions
50 1 1                                  : day 1 started
51 1 5                                  : the day ends in 5 seconds
51 1 4                                  : the day ends in 4 seconds
51 1 3                                  : the day ends in 3 seconds
51 1 2                                  : the day ends in 2 seconds
51 1 1                                  : the day ends in 1 seconds
52 0                                    : the sun is falling. prepare to party
53 1 1                                  : night 1 started
60 1 0                                  : the party in at 0
61 2 "p0" 4                             : p0 in in 4
61 2 "p58" 0                            : p58 in in 0
63 2 1 2                                : this is a boring party. only 1 people. no points tonight.
54 1 5                                  : the night lasts for 5 seconds
54 1 4                                  : the night lasts for 4 seconds
54 1 3                                  : the night lasts for 3 seconds
54 1 2                                  : the night lasts for 2 seconds
54 1 1                                  : the night lasts for 1 seconds
55 0                                    : night ends
65 1 10                                 : you have 10 actions
50 1 2                                  : day 2 started

Die zweite Zeile enthaelt den Loginbefehl, der an den Server geschickt wurde. Alles andere sind Zeilen die der Server an die Client geschickt hat.

Es gibt nur XXX Befehle:

login <user> <pass> Einloggen. Das ist die erste Aktion.
goto <location> Tagsueber zu gegebenem Ort laufen. Verbraucht eine Aktion.
party <playerid> <location> Spieler <playerid> von einer Party an Ort <location> benachrichtigen. Nur moeglich, sofern sich der Spieler am selben Ort befindet oder bereits durch eine fruehere Party bekannt ist.
no_party <playerid> <location> Spieler <playerid> von nicht stattfindender Party an Ort <location> benachrichtigen. Nur moeglich, sofern sich der Spieler am selben Ort befindet oder bereits durch eine fruehere Party bekannt ist.
liar <playerid> <aboutplayerid> Spieler <playerid> ueber einen luegenden Spieler <aboutplayerid> benachrichtigen.
recommand <playerid> <aboutplayerid> Spieler <playerid> ueber einen vertrauenswuerdigen Spieler <aboutplayerid> benachrichtigen.

Natuerlich muessen die gemachten Aussagen nicht stimmen :-)

Statistiken

Ein statistiken-sammelnder Bot von --Nomeata speichert fleißig die Punkte der verschiedenen Bots. Momentan findet man diese auf http://nomeata.de/partty-stats.txt. Mit diesem Skript kann man sich mit

GET http://nomeata.de/partty-stats.txt | runhaskell parseStats.hs

eine einfache Rangliste berechnen lassen.

Libraries und Beispielcode

Lisp

Einen client-rohbau in lisp gibt es hier.

Python

Einen Client-Adapter in Python gibt es hier. Verwendung:

#!/usr/bin/python

from pyParty import *

class PartyClient(PartyAdapter):
	def __init__(self):
		PartyAdapter.__init__(self, "partynator2", 1234)
	
	def messageReceived(self, type, args, comment, line):
		print "%s %s %s" % (type, args, comment)

if __name__ == "__main__":
	runPartyClient(PartyClient)

Haskell

Auf http://darcs.nomeata.de/haskell-partty/ findet ihr ein Haskell-Framework (Partty.hs), das euch die Kommunikation und einiges an Book-Keeping über den aktuellen Zustand abnimmt. Damit ist ein einfacher Bot schnell geschrieben. Die ideale Gelegenheit Haskell zu lernen! Dort findet ihr auch 6 fertige Bots (und zwei nicht ganz sinnvolle) zum herumspielen.

Inzwischen ist das Modul auch vollständig dokumentiert. Wenn ihr wollt dass die Links auf andere Definition stimmen, könnt ihr die Doku mit ./gendoc.hs neu generieren.

Am einfachsten bekommt ihr die Quellen mit darcs get http://darcs.nomeata.de/haskell-partty/.


import Control.Monad.Trans
import Control.Monad.State
import Control.Monad.Reader
import System.Random
import Partty

{- 
 - Stupid player: Goes to a party it knows about for sure, otherwise guesses
 -}

type UserData = ()

dcb :: DayCallback UserData
dcb (DayStarts _) = do
	mbp <- asks psPartyPlace
	room <- case mbp of
		Nothing -> do
			say $ "No idea where to go, guessing..."
			liftIO $ randomRIO (0,9)
		Just room -> do
			say $ "Yay, I know where to go! (" ++ show room ++")"
			return room
	send (Goto room)
dcb e = return ()

ncb :: NightCallback UserData
ncb (NightResult {nrScore = score }) = do
	say $ "Got score: " ++ show score

main = parttyMain dcb ncb ()