source: tests/exceptions/hotpotato.cfa @ 7a925a41

Last change on this file since 7a925a41 was 7a925a41, checked in by caparsons <caparson@…>, 5 months ago

final hotpotato cleanup, removed two unneeded routines from explicit polling flavour

  • Property mode set to 100644
File size: 7.1 KB
Line 
1#include <fstream.hfa>                                                                  // sin/sout
2#include <stdlib.hfa>                                                                   // convert
3#include <string.h>
4#include <coroutine.hfa>
5
6struct Potato {
7        PRNG & prng;
8        unsigned int deadline;                                                          // when timer goes off
9        unsigned int timer;                                                                     // up counter to deadline
10}; // Potato
11
12void reset( Potato & potato, unsigned int maxTicks = 10 );
13void ?{}( Potato & potato, PRNG & prng, unsigned int maxTicks = 10 );
14
15void ?{}( Potato & potato, PRNG & prng, unsigned int maxTicks ) with(potato) {
16        &potato.prng = &prng;
17        reset( potato, maxTicks );
18} // Potato
19
20coroutine Player {
21        PRNG & prng;
22        int id;                                                                                         // player identity
23        Potato & potato;                                                                        // potato being tossed
24        Player * partner[2];                                                            // left and right player
25}; // Player
26
27void ?{}( Player & player, PRNG & prng, unsigned int id, Potato & potato ) {
28        &player.prng = &prng;
29        player.id = id;
30        &player.potato = &potato;
31} // Player
32
33Player & umpire;
34
35ExceptionDecl( Explode );
36ExceptionDecl( Terminate, Player * victim; );
37ExceptionDecl( Election );
38ExceptionDecl( cmd_error ); // convert(...) throws out_of_range or invalid_argument
39
40void reset( Potato & potato, unsigned int maxTicks ) with(potato) {
41  if ( maxTicks < 2 ) abort( "Hot Potato initialized with less than 2 ticks" ); // optional
42        deadline = prng( prng, 1, maxTicks );
43        timer = 0;
44        sout | "  POTATO goes off after " | deadline | " tick" | nosep | (deadline > 1 ? "s" : "");
45} // reset
46
47void countdown( Potato & potato ) with(potato) {
48        timer += 1;
49        if ( timer == deadline ) throwResume ExceptionInst( Explode );
50} // countdown
51
52static unsigned int rightOf( unsigned int me, unsigned int total ) {
53        return ( me + 1 ) % total;
54} // rightOf
55
56static unsigned int leftOf( unsigned int me, unsigned int total ) {
57        return ( me + total - 1) % total;
58} // leftOf
59
60enum { LEFT = 0, RIGHT = 1 };
61
62static void vote( Player & player, Election & election ) {                                      // cause partner to vote
63    resumeAt( player, election );
64        resume( player );
65} // vote
66
67void init( Player & player, Player & lp, Player & rp ) with(player) { // supply partners
68        partner[LEFT] = &lp;
69        partner[RIGHT] = &rp;
70        resume( player );                                                                       // establish main as starter for termination
71} // init
72
73int getId( Player & player ) {                                                  // player id
74        return player.id;
75} // getId
76
77void main( Player & player ) with(player) {
78    suspend;                                                                    // return immediately after establishing starter
79        try {
80        for ( ;; ) {
81            poll();                                     // check for non-local exceptions before proceeding
82
83            if ( partner[LEFT] == &player ) {                   // stop when only one player
84                sout | id | " wins the Match!";
85                return;
86            } // exit
87           
88            countdown( potato );                                // player is eliminated if countdown() returned true
89           
90            size_t side = prng( prng, 2 );
91            sout | id | " -> " | nonl;
92            resume( *partner[ side ] );                 // random toss left/right
93        } // for
94        } catchResume( Terminate * v ) {
95                v->victim->partner[LEFT]->partner[RIGHT] = v->victim->partner[RIGHT]; // unlink node
96                v->victim->partner[RIGHT]->partner[LEFT] = v->victim->partner[LEFT];
97        delete( v->victim );
98                reset( potato );
99                sout | "U " | nonl;                                                             // start new game
100                flush( sout );
101        } catchResume( Election * election ) {
102                sout | "election";
103                sout | " -> " | id | nonl;
104                if ( id > getId( umpire ) ) &umpire = &player; // set umpire to highest id so far
105                vote( *partner[RIGHT], *election );
106        } catchResume ( Explode * ) {
107        sout | id | " is eliminated";
108        if ( &player == &umpire ) {
109            id = -1;                                    // remove from election
110            vote( *partner[RIGHT], ExceptionInst( Election ) );         // start election
111            try { poll(); } catchResume( Election * election ) {} // handle end of election
112            sout | " : umpire " | getId( umpire );
113        } // if
114        resumeAt( umpire, ExceptionInst( Terminate, &player ) );
115        resume( umpire );                   // resume umpire to terminate this player
116        assert( false );                                        // no return
117    } // try
118} // main
119
120
121int main( int argc, char * argv[] ) {
122        enum {
123                MinNoPlayers = 2,                                                               // minimum players in the game
124                MaxNoPlayers = 10,                                                              // maximum players in the game
125                DefaultGames = 5,                                                               // default games to play
126        };
127        intmax_t numGames = DefaultGames;                                       // games to play
128        intmax_t numPlayers = 0;                                                        // players for a particular game
129        intmax_t seed = 42;                                                                     // random-number seed
130        bool playersSet = false;
131        char * nosummary = getenv( "NOSUMMARY" );                       // print extra output
132
133        try {
134                choose ( argc ) {
135                  case 4:
136                        if ( strcmp( argv[3], "d" ) != 0 ) {            // default ?
137                                seed = convert( argv[3] ); if ( seed < 1 ) throw ExceptionInst( cmd_error ); // invalid ?
138                        } // if
139                        fallthrough;
140                  case 3:
141                        if ( strcmp( argv[2], "d" ) != 0 ) {            // default ?
142                                numPlayers = convert( argv[2] ); if ( numPlayers < 2 ) throw ExceptionInst( cmd_error ); // invalid ?
143                                playersSet = true;
144                        } // if
145                        fallthrough;
146                  case 2:
147                        if ( strcmp( argv[1], "d" ) != 0 ) {            // default ?
148                                numGames = convert( argv[1] ); if ( numGames < 0 ) throw ExceptionInst( cmd_error ); // invalid ?
149                        } // if
150                        fallthrough;
151                  case 1: ;                                                                             // defaults
152                  default:                                                                              // too many arguments
153                        throw ExceptionInst( cmd_error );
154                } // choose
155        } catch( exception_t * ) {                                                      // catch any
156                exit | "Usage: " | argv[0]
157                         | " [ games (>=0) | 'd' (default " | DefaultGames
158                         | ") [ players (>=2) | 'd' (random " | MinNoPlayers | "-" | MaxNoPlayers
159                         | ") [ seed (>0) | 'd' (random) ] ] ]";
160        } // try
161        sout | numGames | numPlayers | seed;
162
163        PRNG mprng, hprng, pprng;
164        if ( seed != 0 ) {                                                                      // specified on command line ?
165                set_seed( mprng, seed );  set_seed( hprng, seed );  set_seed( pprng, seed );
166        } // if
167
168        for ( game; 1 ~= numGames ) {
169                if ( ! playersSet ) numPlayers = prng( mprng, MinNoPlayers, MaxNoPlayers );
170                sout | numPlayers | " players in the game";
171                {
172                        Potato potato{ hprng };                                         // hot potato to be tossed
173                        Player * players[numPlayers];
174
175                        for ( unsigned int i = 0; i < (unsigned int)numPlayers; i += 1 ) { // start the players
176                                players[i] = malloc();
177                                ?{}( *players[i], pprng, i, potato );
178                        } // for
179               
180                        // Do not swap player[0] with itself.
181                        unsigned int rposn = prng( mprng, 1, numPlayers - 1 ); // swap random position with 0
182                        swap( players[0], players[rposn] );
183
184                        // Tell each player its partner.
185                        for ( unsigned int i = 0; i < (unsigned int)numPlayers; i += 1 ) {
186                                init( *players[i], *players[leftOf(i, numPlayers)], *players[rightOf(i, numPlayers)] );
187                        } // for
188
189                        &umpire = players[rposn];                                       // designate umpire and start game
190                        sout | "U " | nonl;
191                        resume( *players[rposn] );
192                        delete( &umpire );
193                }
194                if ( game < (unsigned int)numGames ) sout | nl | nl; // whitespace between games
195        } // for
196} // main
197
198// Local Variables: //
199// compile-command: "make hotpotato" //
200// End: //
Note: See TracBrowser for help on using the repository browser.