| 1 | #include <fstream.hfa>                                                                  // sin/sout
 | 
|---|
| 2 | #include <stdlib.hfa>                                                                   // convert
 | 
|---|
| 3 | #include <string.h>
 | 
|---|
| 4 | #include <coroutine.hfa>
 | 
|---|
| 5 | 
 | 
|---|
| 6 | struct Potato {
 | 
|---|
| 7 |         PRNG & prng;
 | 
|---|
| 8 |         unsigned int deadline;                                                          // when timer goes off
 | 
|---|
| 9 |         unsigned int timer;                                                                     // up counter to deadline
 | 
|---|
| 10 | }; // Potato
 | 
|---|
| 11 | 
 | 
|---|
| 12 | void reset( Potato & potato, unsigned int maxTicks = 10 );
 | 
|---|
| 13 | void ?{}( Potato & potato, PRNG & prng, unsigned int maxTicks = 10 );
 | 
|---|
| 14 | 
 | 
|---|
| 15 | void ?{}( Potato & potato, PRNG & prng, unsigned int maxTicks ) with(potato) {
 | 
|---|
| 16 |         &potato.prng = &prng;
 | 
|---|
| 17 |         reset( potato, maxTicks );
 | 
|---|
| 18 |                 } // Potato
 | 
|---|
| 19 | 
 | 
|---|
| 20 | coroutine 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 | 
 | 
|---|
| 27 | void ?{}( Player & player, PRNG & prng, unsigned int id, Potato & potato ) {
 | 
|---|
| 28 |         &player.prng = &prng;
 | 
|---|
| 29 |         player.id = id;
 | 
|---|
| 30 |         &player.potato = &potato;
 | 
|---|
| 31 |                 } // Player
 | 
|---|
| 32 | 
 | 
|---|
| 33 | Player & umpire;
 | 
|---|
| 34 | 
 | 
|---|
| 35 | ExceptionDecl( Explode );
 | 
|---|
| 36 | ExceptionDecl( Terminate, Player * victim; );
 | 
|---|
| 37 | ExceptionDecl( Election );
 | 
|---|
| 38 | ExceptionDecl( cmd_error ); // convert(...) throws out_of_range or invalid_argument
 | 
|---|
| 39 | 
 | 
|---|
| 40 | void 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 | 
 | 
|---|
| 47 | void countdown( Potato & potato ) with(potato) {
 | 
|---|
| 48 |         timer += 1;
 | 
|---|
| 49 |         if ( timer == deadline ) throwResume ExceptionInst( Explode );
 | 
|---|
| 50 | } // countdown
 | 
|---|
| 51 | 
 | 
|---|
| 52 | static unsigned int rightOf( unsigned int me, unsigned int total ) {
 | 
|---|
| 53 |         return ( me + 1 ) % total;
 | 
|---|
| 54 | } // rightOf
 | 
|---|
| 55 | 
 | 
|---|
| 56 | static unsigned int leftOf( unsigned int me, unsigned int total ) {
 | 
|---|
| 57 |         return ( me + total - 1) % total;
 | 
|---|
| 58 | } // leftOf
 | 
|---|
| 59 | 
 | 
|---|
| 60 | enum { LEFT = 0, RIGHT = 1 };
 | 
|---|
| 61 | 
 | 
|---|
| 62 | static void vote( Player & player, Election & election ) { // cause partner to vote
 | 
|---|
| 63 |         resumeAt( player, election );
 | 
|---|
| 64 |         resume( player );
 | 
|---|
| 65 | } // vote
 | 
|---|
| 66 | 
 | 
|---|
| 67 | void 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 | 
 | 
|---|
| 73 | int getId( Player & player ) {                                                  // player id
 | 
|---|
| 74 |         return player.id;
 | 
|---|
| 75 | } // getId
 | 
|---|
| 76 | 
 | 
|---|
| 77 | void 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 | 
 | 
|---|
| 121 | int 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 | 
 | 
|---|
| 132 |         try {
 | 
|---|
| 133 |                 choose ( argc ) {
 | 
|---|
| 134 |                   case 4:
 | 
|---|
| 135 |                         if ( strcmp( argv[3], "d" ) != 0 ) {            // default ?
 | 
|---|
| 136 |                                 seed = convert( argv[3] ); if ( seed < 1 ) throw ExceptionInst( cmd_error ); // invalid ?
 | 
|---|
| 137 |                         } // if
 | 
|---|
| 138 |                         fallthrough;
 | 
|---|
| 139 |                   case 3:
 | 
|---|
| 140 |                         if ( strcmp( argv[2], "d" ) != 0 ) {            // default ?
 | 
|---|
| 141 |                                 numPlayers = convert( argv[2] ); if ( numPlayers < 2 ) throw ExceptionInst( cmd_error ); // invalid ?
 | 
|---|
| 142 |                                 playersSet = true;
 | 
|---|
| 143 |                         } // if
 | 
|---|
| 144 |                         fallthrough;
 | 
|---|
| 145 |                   case 2:
 | 
|---|
| 146 |                         if ( strcmp( argv[1], "d" ) != 0 ) {            // default ?
 | 
|---|
| 147 |                                 numGames = convert( argv[1] ); if ( numGames < 0 ) throw ExceptionInst( cmd_error ); // invalid ?
 | 
|---|
| 148 |                         } // if
 | 
|---|
| 149 |                         fallthrough;
 | 
|---|
| 150 |                   case 1: ;                                                                             // defaults
 | 
|---|
| 151 |                   default:                                                                              // too many arguments
 | 
|---|
| 152 |                           throw ExceptionInst( cmd_error );
 | 
|---|
| 153 |                 } // choose
 | 
|---|
| 154 |         } catch( exception_t * ) {                                                      // catch any
 | 
|---|
| 155 |                 exit | "Usage: " | argv[0]
 | 
|---|
| 156 |                         | " [ games (>=0) | 'd' (default " | DefaultGames
 | 
|---|
| 157 |                         | ") [ players (>=2) | 'd' (random " | MinNoPlayers | "-" | MaxNoPlayers
 | 
|---|
| 158 |                         | ") [ seed (>0) | 'd' (random) ] ] ]";
 | 
|---|
| 159 |         } // try
 | 
|---|
| 160 |         sout | numGames | numPlayers | seed;
 | 
|---|
| 161 | 
 | 
|---|
| 162 |         PRNG mprng, hprng, pprng;
 | 
|---|
| 163 |         if ( seed != 0 ) {                                                                      // specified on command line ?
 | 
|---|
| 164 |                 set_seed( mprng, seed );  set_seed( hprng, seed );  set_seed( pprng, seed );
 | 
|---|
| 165 |         } // if
 | 
|---|
| 166 | 
 | 
|---|
| 167 |         for ( game; 1 ~= numGames ) {
 | 
|---|
| 168 |                 if ( ! playersSet ) numPlayers = prng( mprng, MinNoPlayers, MaxNoPlayers ); 
 | 
|---|
| 169 |                 sout | numPlayers | " players in the game";
 | 
|---|
| 170 |                 {
 | 
|---|
| 171 |                         Potato potato{ hprng };                                         // hot potato to be tossed
 | 
|---|
| 172 |                         Player * players[numPlayers];
 | 
|---|
| 173 | 
 | 
|---|
| 174 |                         for ( unsigned int i = 0; i < (unsigned int)numPlayers; i += 1 ) { // start the players
 | 
|---|
| 175 |                                 players[i] = malloc();
 | 
|---|
| 176 |                                 ?{}( *players[i], pprng, i, potato );
 | 
|---|
| 177 |                         } // for
 | 
|---|
| 178 |                 
 | 
|---|
| 179 |                         // Do not swap player[0] with itself.
 | 
|---|
| 180 |                         unsigned int rposn = prng( mprng, 1, numPlayers - 1 ); // swap random position with 0
 | 
|---|
| 181 |                         swap( players[0], players[rposn] );
 | 
|---|
| 182 | 
 | 
|---|
| 183 |                         // Tell each player its partner.
 | 
|---|
| 184 |                         for ( unsigned int i = 0; i < (unsigned int)numPlayers; i += 1 ) {
 | 
|---|
| 185 |                                 init( *players[i], *players[leftOf(i, numPlayers)], *players[rightOf(i, numPlayers)] );
 | 
|---|
| 186 |                         } // for
 | 
|---|
| 187 | 
 | 
|---|
| 188 |                         &umpire = players[rposn];                                       // designate umpire and start game
 | 
|---|
| 189 |                         sout | "U " | nonl;
 | 
|---|
| 190 |                         resume( *players[rposn] );
 | 
|---|
| 191 |                         delete( &umpire );
 | 
|---|
| 192 |                 }
 | 
|---|
| 193 |                 if ( game < (unsigned int)numGames ) sout | nl | nl; // whitespace between games
 | 
|---|
| 194 |         } // for
 | 
|---|
| 195 | } // main
 | 
|---|
| 196 | 
 | 
|---|
| 197 | // Local Variables: //
 | 
|---|
| 198 | // compile-command: "make hotpotato" //
 | 
|---|
| 199 | // End: //
 | 
|---|