The Background Future
In the year 2017, you and your opponent will face off in a futuristic gun battle where only one may survive. Are you experienced enough to defeat your opponent? Now is the time to polish your guns skills in your favorite programming language and fight against all odds!
Tournament Results
This tournament ended on the UTC morning of Feburary 2nd, 2017. Thanks to our contestants, we have had an exciting futuristic tournament!
MontePlayer is the final winner after a close battles with CBetaPlayer and StudiousPlayer. The three top guen duelers have taken a commemorative photograph:
MontePlayer - by TheNumberOne
+------------+
CBetaPlayer | | - by George V. Williams
+------------+ # 1 | StudiousPlayer - by H Walters
| +----------------+
| # 2 # 3 |
+------------------------------------------+
The Futurustic Gun Duel @ PPCG.SE 2017
Congratulations to the winners! Detailed leaderboard is seen near the end of this post.
General Guidance
- Visit the official repository for the source code used in this tournament.
- C++ entries: please inherit the
Player
class. - Non C++ entries: select one interface in section Interface for Non-C++ submissions.
- Currently allowed non C++ languages: Python 3, Java.
The Duel
- Each player starts with an unloaded gun that can load an infinite amount of ammo.
- Each turn, players will simultaneously choose from one of the following actions:
0
-- Load 1 ammo into the gun.1
-- Fire a bullet at the opponent; costs 1 loaded ammo.2
-- Fire a plasma beam at the opponent; costs 2 loaded ammo.-
-- Defend incoming bullet using a metal shield.=
-- Defend incoming plasma beam using a thermal deflector.
- If both players survive after the 100th turn, they both exhaust to death, which results in a draw.
A player loses the gun duel if they
- Did NOT use the metal shield to defend an incoming bullet.
- Did NOT use the thermal deflector to defend an incoming plasma.
- Fire a gun without loading enough ammo, in which their gun will self-explode and kill the owner.
Caveats
According to the Manual for Futuristic Gun Owners:
- A metal shield CANNOT defend from incoming plasma beam. Likewise, a thermal deflector CANNOT defend from incoming bullet.
- Plasma beam overpowers the bullet (because the former requires more loaded ammo). Therefore, if a player fires a plasma beam at the opponent who fires a bullet in the same turn, the opponent is killed.
- If both players fire a bullet at each other in the same turn, the bullets cancel out and both players survive. Likewise, if both players fire a plasma beam at each other in the same turn, both players survive.
It's also noteworthy that:
- You will NOT know your opponent's action in a turn until it ends.
- Deflecting plasma beams and shielding bullets will NOT harm your opponent.
Therefore, there are a total of 25 valid action combinations each turn:
+-------------+---------------------------------------------+
| Outcome | P L A Y E R B |
| Table +--------+-----------------+------------------+
| for Players | Load | Bullet Plasma | Metal Thermal |
+---+---------+--------+--------+--------+--------+---------+
| P | Load | | B wins | B wins | | |
| L +---------+--------+--------+--------+--------+---------+
| A | Bullet | A wins | | B wins | | A wins |
| Y | +--------+--------+--------+--------+---------+
| E | Plasma | A wins | A wins | | A wins | |
| R +---------+--------+--------+--------+--------+---------+
| | Metal | | | B wins | | |
| | +--------+--------+--------+--------+---------+
| A | Thermal | | B wins | | | |
+---+---------+--------+--------+---------------------------+
Note: Blank cells indicate that both players survive to the next turn.
Example Duel
Here's a duel I once had with a friend. Back then, we didn't know much about programming, so we used hand gestures and signalled at the speed of two turns per second. From left to right, our actions were in turn:
Me: 001-000-1201101001----2
Friend: 00-10-=1-==--0100-1---1
As per the rules above, I lost. Do you see why? It's because I fired the final plasma beam when I had only 1 loaded ammo, causing my gun to explode.
The C++ Player
You, as a civilized futuristic programmer, won't directly handle the guns. Instead, you code a Player
that fights against others'. By publicly inheriting the c++ class in the GitHub project, you can start writing your urban legend.
Player.hpp can be found in Tournament\Player.hpp
An example of a derived class can be found in Tournament\CustomPlayer.hpp
What you must or can do
- You must inherit
Player
class through public inheritance and declare your class final. - You must override
Player::fight
, which returns a validPlayer::Action
every time it is called. - Optionally, override
Player::perceive
andPlayer::declared
to keep an eye on your opponent's actions and keep track of your victories. - Optionally, use private static members and methods in your derived class to perform more complex calculations.
- Optionally, use other C++ standard libraries.
What you must NOT do
- You must NOT use any direct method to recognize your opponent other than the given opponent identifier, which is shuffled at the beginning of each tournament. You're only allowed to guess who a player is through their game-play within a tournament.
- You must NOT override any methods in
Player
class that is not declared virtual. - You must NOT declare or initialize anything in the global scope.
- Since the debut of (now disqualified)
BlackHatPlayer
, players are NOT allowed to peek at or modify the state of your opponent.
An example duel
The process of a gun duel is performed using the GunDuel
class. For an example fight, see the Source.cpp
in section Initiating a duel.
We showcase GunClubPlayer
, HumanPlayer
and the GunDuel
class, which can be found in the Tournament\
directory of the repository.
In each duel, GunClubPlayer
will load a bullet; fire it; rinse and repeat. During every turn, HumanPlayer
will prompt you for an action to play against your opponent. Your keyboard controls are the characters 0
, 1
, 2
, -
and =
. On Windows, you can use HumanPlayer
to debug your submission.
Initiating a duel
This is how you can debug your player through console.
// Source.cpp
// An example duel between a HumanPlayer and GunClubPlayer.
#include "HumanPlayer.hpp"
#include "GunClubPlayer.hpp"
#include "GunDuel.hpp"
int main()
{
// Total number of turns per duel.
size_t duelLength = 100;
// Player identifier 1: HumanPlayer.
HumanPlayer human(2);
// Player identifier 2: GunClubPlayer.
GunClubPlayer gunClub(1);
// Prepares a duel.
GunDuel duel(human, gunClub, duelLength);
// Start a duel.
duel.fight();
}
Example Games
The least amount of turns you need to defeat GunClubPlayer
is 3. Here's the replay from playing 0-1
against GunClubPlayer
. The number in the paranthesis is the number of loaded ammo for each player when the turn ends.
:: Turn 0
You [0/12/-=] >> [0] load ammo (1 ammo)
Opponent selects [0] load ammo (1 ammo)
:: Turn 1
You [0/12/-=] >> [-] defend using metal shield (1 ammo)
Opponent selects [1] fire a bullet (0 ammo)
:: Turn 2
You [0/12/-=] >> [1] fire a bullet (0 ammo)
Opponent selects [0] load ammo (1 ammo)
:: You won after 3 turns!
:: Replay
YOU 0-1
FOE 010
Press any key to continue . . .
The quickest way to be defeated by GunClubPlayer
without making invalid moves is the sequence 0=
, because the bullet shoots right through the thermal deflector. The replay is
:: Turn 0
You [0/12/-=] >> [0] load ammo (1 ammo)
Opponent selects [0] load ammo (1 ammo)
:: Turn 1
You [0/12/-=] >> [=] defend using thermal deflector (1 ammo)
Opponent selects [1] fire a bullet (0 ammo)
:: You lost after 2 turns!
:: Replay
YOU 0=
FOE 01
Press any key to continue . . .
The Tournament
The tournament follows the "Last Player Standing" format. In a tournament, all valid submissions (including the GunClubPlayer
) are placed in a pool. Each submission is assigned a randomized yet unique identifier that will stay the same during the whole tournament. During each round:
- Each submission begins with 0 points and will play 100 duels against every other submission.
- Each victorious duel will grant 1 point; drawing and losing give 0 points.
- At the end of the round, submissions with the minimum points leave the tournament. In case of a tie, the player with the least amount of points earned since the beginning of the tournament will leave.
- If more than one player is left, the next round shall begin.
- Points do NOT carry over to the next round.
Submission
You'll submit one player per answer. You can submit multiple files for a player, as long as they do NOT interfere with other submissions. To keep things flowing, please:
- Name your main header file as
<Custom>Player.hpp
, - Name your other files as
<Custom>Player*.*
, e.g.MyLittlePlayer.txt
if your class name isMyLittlePlayer
, orEmoPlayerHates.cpp
if your class name isEmoPlayer
. - If your name contains
Shooter
or similar words that fit the context of this tournament, you need not addPlayer
at the end. If you feel strongly that your submission name works better without the suffixPlayer
, you also don't need to addPlayer
. - Make sure that your code can be compiled and linked under Windows.
You can comment to ask for clarification or to spot loopholes. Hope you enjoy this Futuristic Gun Duel and wish you a Happy New Year!
Clarification
- You are allowed to have randomized behavior.
- Invalid actions (firing when loaded ammo isn't enough) are allowed.
- If a player makes an invalid input, their gun will explode immediately.
- You are allowed to study the answers.
- You are explicitly allowed to record opponent behavior within each tournament.
- Each round, you will play 100 duels against each opponent; the order of the 100 duels, however, is randomized-- you're not guaranteed to fight the same opponent 100 duels in a row.
Additional Resources
@flawr has translated the provided C++ source into Java as a reference if you want to submit C++ entries.
Interface for Non-C++ Submissions
Currently accepted: Python 3, Java.
Please follow one of the specifications below:
Interface specification 1: exit code
Your submission will run once per turn.
Expected Command Line Argument Format:
<opponent-id> <turn> <status> <ammo> <ammo-opponent> <history> <history-opponent>
Expected Return Code: The ASCII value of a valid action character.
'0' = 48, '1' = 49, '2' = 50, '-' = 45, '=' = 61
<opponent-id> is an integer in [0, N), where N is size of tournament.
<turn> is 0-based.
If duel is in progress, <status> is 3.
If duel is draw / won / lost, <status> is 0 / 1 / 2.
<history> and <history-opponent> are strings of actions, e.g. 002 0-=
If turn is 0, <history> and <history-opponent> are not provided.
You can ignore arguments you don't particularly need.
You can test your submission in PythonPlayer\
and JavaPlayer\
directories.
Interface specification 2: stdin/stdout
(Credit to H Walters)
Your submission will run once per tournament.
There's a fixed requirement for all entries on how to do I/O, since both stdin and stdout are connected to the tournament driver. Violating this could lead to a deadlock. All entries MUST follow this EXACT algorithm (in pseudo-code):
LOOP FOREVER
READ LINE INTO L
IF (LEFT(L,1) == 'I')
INITIALIZE ROUND
// i.e., set your/opponent ammo to 0, if tracking them
// Note: The entire line at this point is a unique id per opponent;
// optionally track this as well.
CONTINUE LOOP
ELSE IF (LEFT(L,1) == 'F')
WRITELN F // where F is your move
ELSE IF (LEFT(L,1) == 'P')
PROCESS MID(L,2,1) // optionally perceive your opponent's action.
END IF
CONTINUE LOOP
QUIT
Here, F is one of 0
, 1
, 2
, -
, or =
for load / bullet / plasma / metal / thermal
. PROCESS means to optionally respond to what your opponent did (including tracking your opponent's ammo if you're doing this). Note that the opponent's action is also one of '0', '1', '2', '-', or '=', and is in the second character.
Final Scoreboard
08:02 AM Tuesday, February 2, 2017 Coordinated Universal Time (UTC)
| Player | Language | Points | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|:------------------ |:---------- | ------:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:|
| MontePlayer | C++ | 11413 | 1415 | 1326 | 1247 | 1106 | 1049 | 942 | 845 | 754 | 685 | 555 | 482 | 381 | 287 | 163 | 115 | 61 |
| CBetaPlayer | C++ | 7014 | 855 | 755 | 706 | 683 | 611 | 593 | 513 | 470 | 414 | 371 | 309 | 251 | 192 | 143 | 109 | 39 |
| StudiousPlayer | C++ | 10014 | 1324 | 1233 | 1125 | 1015 | 907 | 843 | 763 | 635 | 555 | 478 | 403 | 300 | 201 | 156 | 76 |
| FatedPlayer | C++ | 6222 | 745 | 683 | 621 | 655 | 605 | 508 | 494 | 456 | 395 | 317 | 241 | 197 | 167 | 138 |
| HanSoloPlayer | C++ | 5524 | 748 | 668 | 584 | 523 | 490 | 477 | 455 | 403 | 335 | 293 | 209 | 186 | 153 |
| SurvivorPlayer | C++ | 5384 | 769 | 790 | 667 | 574 | 465 | 402 | 354 | 338 | 294 | 290 | 256 | 185 |
| SpecificPlayer | C++ | 5316 | 845 | 752 | 669 | 559 | 488 | 427 | 387 | 386 | 340 | 263 | 200 |
| DeceptivePlayer | C++ | 4187 | 559 | 445 | 464 | 474 | 462 | 442 | 438 | 369 | 301 | 233 |
| NotSoPatientPlayer | C++ | 5105 | 931 | 832 | 742 | 626 | 515 | 469 | 352 | 357 | 281 |
| BarricadePlayer | C++ | 4171 | 661 | 677 | 614 | 567 | 527 | 415 | 378 | 332 |
| BotRobotPlayer | C++ | 3381 | 607 | 510 | 523 | 499 | 496 | 425 | 321 |
| SadisticShooter | C++ | 3826 | 905 | 780 | 686 | 590 | 475 | 390 |
| TurtlePlayer | C++ | 3047 | 754 | 722 | 608 | 539 | 424 |
| CamtoPlayer | C++ | 2308 | 725 | 641 | 537 | 405 |
| OpportunistPlayer | C++ | 1173 | 426 | 420 | 327 |
| GunClubPlayer | C++ | 888 | 500 | 388 |
| PlasmaPlayer | C++ | 399 | 399 |
The tournament will last till February 1, 2017 unless otherwise noted.
Player
implementation which invokes another process to compute the current turn. That would allow people to participate in any language you're happy to run on your machine. \$\endgroup\$Player::fight
"/"you can inheritPlayer::perceive
" ...in both cases, the term is override, not inherit. \$\endgroup\$GunDuel.hpp
, bothvalidA
andvalidB
useactionA
\$\endgroup\$