73
28
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.
15Impressive first challenge, by the way! – Martin Ender – 2016-12-28T09:42:03.347
3If you're willing to run some other languages, you could allow a
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. – Martin Ender – 2016-12-28T09:47:17.3005Randomness allowed? (Not completely random turns, just a 50/50 choice of action in a certain situation) – FlipTack – 2016-12-28T10:11:56.083
1@FlipTack Yes, randomness is allowed. For example, a
HumanPlayer
can be fairly random at times. – Frenzy Li – 2016-12-28T10:13:39.130@FrenzyLi Would you mind if I make a translation in Java? I think it might be a little bit more accessible than c++. – flawr – 2016-12-28T10:27:57.583
1Thanks, I have a first question : is there a limit to the number of ammo that your weapon can hold ? And in the question you mention "Load 1 ammunition pack into the gun." Then "Fire a bullet at the opponent; costs 1 loaded ammo.". So you consider 1 ammo pack being one ammunition ? – ColdK – 2016-12-28T10:43:24.837
1@ColdK First, there is no limit to the amount of ammo the futuristic gun can hold. And yes, it all should be called ammo. I'll update my language to make it consistent. – Frenzy Li – 2016-12-28T10:45:01.127
1Also, how do you differenciate the different bots that will be posted here with getOpponent() ? It will return the name of the bot ? – ColdK – 2016-12-28T10:49:02.523
1@ColdK You can refer to the
Source.cpp
file. Also, as an illustration, in each tournament all players will get a random unsigned identifier between[0, N)
that is consistent throughout one tournament, e.g. in a six player tournament, allGunClubPlayers
are identified as3
, allOpportunist
are identified5
. Your opponent is always passed an constructor argument intoPlayer::Player
which is your identifier when you fight each other.Player::getOpponent
will return the opponent identifier through base class. To track things, you could use private static methods and members. – Frenzy Li – 2016-12-28T10:50:26.3071
@FrenzyLi Right I could figure it out, so here is the translation to JAVA if anyone is interested!
– flawr – 2016-12-28T14:47:41.610Edited @flawr's translation into the post (thank you!) and added specification on the commandline interface for all non C++ .exe binary builds. – Frenzy Li – 2016-12-28T15:07:13.233
Where is Pyth or CJam? :( – magic-sudo – 2016-12-28T20:44:58.407
Are we allowed to enter multiple bots? – Blue – 2016-12-28T21:38:12.127
@muddyfish Yes, but I hope your multiple submissions are equally creative, and I would especially encourage submissions that use brand new strategy. – Frenzy Li – 2016-12-29T05:14:12.103
1@FrenzyLi Can i make a submission with a .py file? Or does it have to be an exe? – Keatinge – 2016-12-29T11:21:59.707
@Keatinge Yes you can. I actually have python on my computer and
python file.py arguments
will be ok. Python can set return codes, right? – Frenzy Li – 2016-12-29T13:03:37.263@FrenzyLi I'm pretty sure you can with
sys.exit(code)
. Can you typepython -V
in console so we know what version of python to write for? (2 or 3) – Keatinge – 2016-12-29T13:07:23.947@Keatinge It's Python 3.5.1. If you can return the code I think your script can communicate with the tournament class. – Frenzy Li – 2016-12-29T13:12:10.287
So I can use the "wrapper for an .exe" rules for a Python submission? – FlipTack – 2016-12-29T15:11:32.777
@FlipTack Yes, you can. I'm also thinking of writing a python submission just for the sake of practice as well as designing a bot that uses a new technique (which hasn't been used by anyone so far). Your python script by itself is submittable. – Frenzy Li – 2016-12-29T15:13:35.837
This complicated thing only to at best achieve 50% chance to win? – akostadinov – 2016-12-29T16:52:52.600
It will be funny to see what happens to players facing themselves. BotRobot vs BotRobot and so many others... – Sxntk – 2016-12-29T18:43:58.473
@akostadinov If you do not identify your opponent, then yes. – Frenzy Li – 2016-12-29T19:15:20.697
Is there a way of getting persistence between rounds? I'm using the latest code and I think I'm missing something if there is – Blue – 2016-12-29T20:32:32.140
@muddyfish You can store private static members and invoke private static methods within your derived class. They persist till the end of each tournament. This is an optional feature and as there are different data structures and algorithms people prefer, I choose to leave it to you. – Frenzy Li – 2016-12-29T20:35:05.187
I'm not sure if I understand. I want to store a map of opponent ids to their histories. From a quick google it seems that private static members are indeed static. – Blue – 2016-12-29T20:39:55.547
@muddyfish it's worth mentioning that being static is different from being const. being static means having class scope, and being const means constness (cannot modify once initialized). You can declare your own private data structure, and use it to statically hold arbitrary amount of data within your class scope. I will put up an example later. Private members are destroyed upon object destruction, but not the private static members. You don't want private static const members because they are not modifiable once compiled. – Frenzy Li – 2016-12-29T20:42:21.257
I'm getting
– Blue – 2016-12-29T20:59:50.833error LNK2001: unresolved external symbol "private: static class
if I put it in theprivate
section of my class. This answer on SO says that I should do something with a .cpp file but I'm using a .hpp file?Any time limits per turn? – TheNumberOne – 2016-12-29T23:13:40.883
@FrenzyLi If we want to define additional classes to help our Player, is that allowed? – TheNumberOne – 2016-12-30T02:29:13.730
@TheNumberOne Yes, it's allowed. – Frenzy Li – 2016-12-30T04:04:14.657
@TheNumberOne Well, I do trust that most submissions will finish in an instant. If your submission uses one second per turn, I think we'd better talk, given the large amount of duels it needs to play. – Frenzy Li – 2016-12-30T04:06:42.560
@muddyfish So, you have to do
private: class A{}; static A instanceofA; static void ThisIsAPrivateStaticFunction();
. – Frenzy Li – 2016-12-30T04:07:59.550@FrenzyLi Okay, I'll try to make it pretty quick. – TheNumberOne – 2016-12-30T04:33:16.670
2Technical point; "You must inherit
Player::fight
"/"you can inheritPlayer::perceive
" ...in both cases, the term is override, not inherit. – H Walters – 2016-12-30T04:44:52.5871@FrenzyLi At the moment matches end with a draw at round 2017 rather than 100. – TheNumberOne – 2016-12-30T04:50:12.330
@magic-sudo CJam submission are okay... wait a minute, can CJam set return codes? – Frenzy Li – 2016-12-30T07:19:23.850
@flawr I can handle the repository for you next time I work on it. – Frenzy Li – 2016-12-30T17:25:21.073
3I think you have a bug in
GunDuel.hpp
, bothvalidA
andvalidB
useactionA
– AlexRacer – 2016-12-30T21:07:30.680I'm trying to write a python submission, but I can't seem to run any of the files in the repository, as neither I nor my computer know what a .sln or .hpp file is. Is there an .exe that I can run? – Magenta – 2016-12-31T03:03:58.133
@AlexRacer I'll look into it shortly. – Frenzy Li – 2016-12-31T04:01:39.790
@Magenta Let me know your submission name, e.g.
SomePlayer.py
, i'll add it in there and compile the tournament binary for you. – Frenzy Li – 2016-12-31T04:02:42.1501I'm getting the compile error:
Error C2228 left of '.name' must have class/struct/union Tournament Tournament.hpp 207
– TheNumberOne – 2016-12-31T04:34:30.753Would it be okay if I created a chat room for this challenge? – TheNumberOne – 2016-12-31T04:35:06.640
2Chat room – TheNumberOne – 2016-12-31T05:22:04.227
@FrenzyLi well I have no file yet, but I need to compile it to bug test, and to see if my code works, so I need it on my machine. – Magenta – 2016-12-31T08:49:23.403
@Magenta Well, if you give me the name of the submission, I can create a binary tournament file for you, which when executed, will run your submission. The interface by return codes is too stringent and your script has to be invoked each turn in a duel, so I may consider using stdin and stdout as interfaces where your script is run once per duel. – Frenzy Li – 2016-12-31T08:51:22.480
@FrenzyLi if all you need is the name, then try gemini.py – Magenta – 2016-12-31T21:55:32.377
@Magenta I've provided a python build for windows in
JavaPlayer\
. You can replace the content inPythonPlayer.py
to test your submission. – Frenzy Li – 2017-01-01T04:23:23.850@FrenzyLi Thanks. That works! – Magenta – 2017-01-01T20:32:04.563
The code that was supposed to run the python programs does not actually call any of my code. I would like to make a submission, however. – Magenta – 2017-01-06T19:01:10.397
I'm still having no luck trying to interface a test python program with the controller given. Given that I see that there are no other python submissions, that i'm not the only one who has this issue. – Magenta – 2017-01-16T21:17:40.393
P.S. it's 2017. Duel time? – NoOneIsHere – 2017-02-01T04:24:59.760
@SeeOneRhino Duel time! – Frenzy Li – 2017-02-02T08:23:16.557
@Mangenta I honeslty apologize about the interface since I did not intend this to be an all-language challenge in the first place and the two commandline interfaces didn't turn out well. I'll study ways to host multi-language C++ based tournaments in the future, (and maybe update this one if I have the time). I hope I can get your understanding. – Frenzy Li – 2017-02-02T08:32:17.783
1@FrenzyLi It's fine- just next time, say it's a C++ only challenge. – Magenta – 2017-02-03T21:15:14.290