-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop'. Adding RoboRally.
- Loading branch information
Showing
60 changed files
with
2,976 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
Programmer's Guide: | ||
To program you AI and test your code, you will need Python 3. The version on my machine is Python 3.5.2. | ||
|
||
Create a python file (like my_robot.py) and drop it in the 'roborally/robots' directory. Your file should look like: | ||
from roborally.api import * | ||
def move(): | ||
# Your AI code here | ||
# return one of the moves from the MOVES list | ||
|
||
The API (at roborally.api) contains a bunch of helpful functions for writing your bot. The most important function | ||
here is sight() which will give you everything your robot currently knows. All the other functions in the API are | ||
helper functions to process that information. The API is heavily documented and you can either read the code, or use | ||
pydoc to read through my documentation. On my system, I type: | ||
pydoc3 roborally/api.py | ||
|
||
Once you've written some of your AI code, you'll want to test it! In order to run simulations of the arena, run | ||
roborally.py while in the robotarena directory. To do this with the default configuration, you would use the | ||
following command: | ||
python3 roborally.py | ||
|
||
Most likely, you'll want to create your own config file so you can test different scenarios. To do this, you would: | ||
cp roborally/config.py ./my_config.py | ||
vim my_config.py (or whatever text editor you like to use) | ||
python3 roborally.py my_config.py | ||
|
||
The comments in the config file should guide your edits, but I will suggest two in particular: | ||
1. debug_bots = True | ||
This will make the game exit with a stacktrace if a bot raises an exception. Good for debugging your AI. | ||
2. iteractive = True OR save_replay = True | ||
If you want to see what your robots are doing iteration-by-iteration, one of these configurations is essential. | ||
The default configuration will use every module in the roborally/robots as an AI in the game with crappy names on a | ||
rather normal map. As you test your robot, you'll probably want to customize which map to use and which robots are | ||
included. | ||
|
||
If you've chosen to use the save_replay configuration, every execution of roborally.py will create a pickle file with | ||
a copy of every iteration encountered during the game. By executing the create_web_replay.py script, the contents of | ||
this pickle file will be used to create HTML pages for every iteration of the game so that you can watch a visual | ||
replay of what went down in robot town. | ||
python3 create_web_replay.py roborally/replays/<replay_name>.pickle | ||
Open common/replays/<replay_name>.html in your browser of choice. | ||
|
||
If you discover any bugs in the game or would like to suggest an enhancement (such as a new function in the api), | ||
you can submit pull requests on github and/or email me about it. | ||
|
||
Once you are happy with your AI, remember to submit your code with a pithy name and description of its strategy. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
The factory robots are competing in a race. Flags have been set out and each robot will need to touch each flag in | ||
numerical order to win. Each robot is equipped with industrial grade lasers to make the race more interesting. | ||
|
||
In this Robot Arena, you control the robots on the factory floor and win by either one of your robots touching each | ||
of the numbered flags in order, or by being the last robot left alive. Armed with your laser, you can destroy anything | ||
in the arena that gets in your way, just don't take too many laser hits yourself! | ||
|
||
Robot Arena: | ||
If you are new to robot arenas, this paragraph is for you! In robot arenas, I am designing a simple game supporting | ||
lots of players. This game functions like board game (usually on a very large board) but is entirely virtual. | ||
All the players are computer-controlled and you write the AI via simple scripts (usually small python modules). | ||
You win if players controlled by your AI win. You don't have to play to win...maybe your AI just creates havoc. | ||
As the game master, I will run the official game. However, I am sharing all of my code to run the game as well as all | ||
the AI submissions by other contributors, so you can run your own simulations to test your AI or just for fun. | ||
|
||
Submission: | ||
What I need from YOU are two AI submissions, a single python file each which will act as the AI for robots. | ||
Along with your python file, please provide a pithy name for your AI and a short description of its strategy. | ||
I need your first submission by 2017-02-26. Just respond to this email with your python file attached. | ||
On 2017-02-28, I will share everyone's first submission to the group. | ||
I need your second submission by 2017-03-09. You can borrow ideas from other people's first submissions. | ||
On 2017-03-11, I will run the official game and report the results. I will be hosting a get together at my home for | ||
anyone who would like to watch the results unfold! | ||
Please ask any questions not answered in the following rules and descriptions. | ||
|
||
Order of the Match: | ||
1. An arena designed by the game master is created. | ||
2. 30 robots of each robot type will be created and added to random locations in the arena. | ||
3. Turns are taken until an end-game situation is reached | ||
A: A robot has touched all 6 flags in order | ||
B: Only 1 robot type survives | ||
C: All robots have died | ||
4. The winner and runner-up are determined. | ||
|
||
Order of the Turn: | ||
1. All active robots decide their moves | ||
2. All robots with priority moves perform their moves | ||
3. All active robots with laser moves fire their lasers | ||
4. All active robots perform normal moves | ||
5. All spinners turn | ||
6. All active robots and mounted lasers fire their lasers | ||
7. Each active robot on top of its next flag scores the flag | ||
8. Every 10th turn, robots receive a charge counter | ||
9. (starting at turn 300) Every 30th turn, an unseen source damages a random robot | ||
10. Check for end-game situation | ||
|
||
Arena: | ||
The arena is square-shaped with square tiles. The arena is surrounded by pits. | ||
The arena that will be used during the official game is secret and to be designed by the game master. However, there | ||
are example arena maps available in the maps directory (https://github.com/kenganong/robot-arena/tree/master/robotarena/roborally/maps). | ||
Since there are 30 copies of each robot type, the arena will be very large. You can expect that around 5% to 6% of | ||
the arena will be populated with robots at the beginning. | ||
Each square of the arena has a floor (which can be empty, a spinner, a flag, or a pit) and content (which can be | ||
empty, a robot, a robot corpse, a mounted laser, or a wall). | ||
When reading maps, these are the symbols and what they mean: | ||
* <space> - Empty | ||
* <number> - A flag with that number on it | ||
* # - A wall | ||
* <,>,^,v - A mounted laser which fires in one of the four directions. | ||
* {,} - A spinner which turns either clockwise or counter-clockwise | ||
* & - A pit | ||
Feel free to design your own maps when testing your AI. | ||
No robot can see the entire arena all the time. Robots can only see an 11x11 square around themselves (5 tiles away | ||
in each direction). | ||
|
||
Lasers: | ||
Robots and Mounted Lasers fire a laser each turn. When a laser is fired, it will hit the first Robot, Robot Corpse, | ||
Wall, or Mounted Laser it encounters in the direction it was fired dealing its target 1 damage. Lasers have infinite | ||
range and can therefore hit a robot even if the robot cannot see the source of the laser. | ||
|
||
Pits: | ||
Pits are represented by a floor of None. Anything that enters a pit square falls into the pit and is gone forever. | ||
|
||
Walls: | ||
Walls are impassable and cannot be pushed. A robot that moves into a wall, will bounce back and not move. | ||
Walls have 50 life and can therefore be shot with lasers until they crumble into nothing. | ||
|
||
Mounted Lasers: | ||
Mounted Lasers are basically walls that fire lasers. They are impassable and cannot be pushed. Each Mounted Laser | ||
fires its laser in exactly one direction. | ||
Mounted Lasers have 50 life and can be shot with lasers until they crumble into nothing. | ||
|
||
Spinners: | ||
When spinners activate, they turn any robot on top of the spinner 90 degrees. When looking down at the arena, the | ||
LEFT_SPINNER turns counter-clockwise and the RIGHT_SPINNER turns clockwise. | ||
Spinners are floor elements and are immovable and indestructible. | ||
|
||
Flags: | ||
There are a total of 6 flags in the arena and each has a number associated with it. Robots score flags only | ||
if they have already touched all previously numbered flags. To score a flag, a robot must end the turn on the | ||
flag tile, this means that each turn a maximum of one robot can be awarded each flag. When a robot scores a flag, | ||
that robot also gains 10 life. | ||
Robots can only see the next flag, all other flags (including previous flags) are invisible to that robot. | ||
Robots can always sense the direction the next flag is in, even if it is outside their sight range. | ||
Flags are floor elements and are immovable and indestructible. | ||
|
||
Corpses: | ||
After a robot has taken 10 or more damage, it deactivates and becomes a corpse. | ||
Corpses can be pushed by robots. They do not fire lasers or move aside from being pushed. | ||
Corpses start with 10 life. After taking 10 or more damage, they explode dealing 1 damage to everything in the | ||
8 adjacent tiles. After exploding, the corpse no longer exists on the arena. | ||
|
||
Robots: | ||
Robots are the players in the game. There are 30 robots of each robot type, meaning each AI controls 30 robots. | ||
Robots have the following attributes: | ||
* life - Each robot starts with 20 life. | ||
* flags - The number of flags the robot has scored. | ||
* charges - The number of charges the robot currently has. Each robot starts with 1 charge. | ||
Charges allow robots to use priority moves without taking damage. | ||
* name - The pithy name for the AI controlling the robot. | ||
* facing - The direction the robot is facing. | ||
* memory - This robot's memory as populated by its AI. | ||
Your AI will have access to all of this information for your robot when deciding the robot's move. However, other | ||
robots you see will only have life, name, and facing available to you. | ||
|
||
Moves: | ||
Each turn, every active robot chooses one move to make. | ||
Normal moves: | ||
* FORWARD - your robot moves forward one tile. | ||
* REVERSE - your robot moves backward one tile. | ||
* TURN_LEFT - your robot turns counter-clockwise 90 degrees. | ||
* TURN_RIGHT - your robot turns clockwise 90 degrees. | ||
* U_TURN - your robot turns 180 degrees. | ||
Attack moves: | ||
Attack moves happen before all normal moves and can therefore be used to hit a robot before it moves. | ||
* Laser - your robot fires its laser instead of moving. This will cause your robot to fire its laser twice during the | ||
turn; once before normal moves and once after all moves. | ||
Priority moves: | ||
Priority moves happen before all attack and normal moves which means they can be used to dodge attack moves. | ||
Performing a priority move will use 1 charge. If your robot has no charges left, it will take 1 damage. | ||
* FORWARD_TWO - your robot moves forward two tiles. This move will not 'jump' over anything. | ||
* SIDESTEP_LEFT - your robot moves one tile to its left without turning. | ||
* SIDESTEP_RIGHT - your robot moves one tile to its right without turning. | ||
When two or more robots would move onto the same tile or otherwise conflict with one another, the robots will perform | ||
their moves in a random order. | ||
|
||
Sight: | ||
Your robot's sight is represented as a list of lists, 11 rows and 11 columns fill with cell objects each with two | ||
attributes, 'floor' and 'content'. Your robot will always be in the center (5, 5) of your sight. | ||
cell.floor will contain one of the following constants (from roborally/api.py) | ||
* PIT - A pit. This constant is equal to None. | ||
* EMPTY - No floor element present. There may still be content. | ||
* LEFT_SPINNER - A counter-clockwise spinner. | ||
* RIGHT_SPINNER - A clockwise spinner. | ||
* FLAG - Your robot's next flag. | ||
cell.content will contain either None or a dict with the following keys: | ||
* TYPE - One of the constants (from roborally/api), WALL, MOUNTED_LASER, CORPSE, ROBOT | ||
* POSITION - A coordinate tuple in your sight. | ||
* LIFE - Remaining life. | ||
* FACING - One of the constants (from roborally/api), AHEAD, BEHIND, LEFT, RIGHT representing the direction the | ||
Robot or Mounted Laser is pointing relative to your robot. Meaningless for Walls and Corpses. | ||
* NAME - Pithy name for the AI controlling the Robot. Meaningless for non-Robots. | ||
* FLAGS_SCORED - Only available for your robot. Number of flags your robot has scored. | ||
* CHARGES - Only available for your robot. Number of charges your robot has. | ||
* MEMORY - Only available for your robot. A dict representing your robot's memory. Nothing is automatically entered | ||
into this dict. Your AI controls its entries. | ||
* FLAG_SENSE - Only available for your robot. A list of directions toward your robot's next flag. | ||
|
||
Random Damage: | ||
Starting at turn 300, some invisible force will start dealing damage to robots. Every 30 turns, one random robot will | ||
take this damage. On turn 300, the randomly selected robot will take 1 damage. On turn 330 another randomly selected | ||
robot will take 2 damage. On turn 360 it will be 3 damage and so on. | ||
Although any active robot can be hit, robots which have collected less flags are more likely to be selected. | ||
|
||
Winner and Rankings: | ||
The winner will be the AI which collects all 6 flags. If no AI does this, the winner will be the AI which survives the | ||
longest. Complete rankings are essentially decided by determining the rankings for best flag gatherers and best | ||
survivors then interleaving the two lists. | ||
Your AI will rank highly if it is the best at one of those two determinations. | ||
Tie-breakers for best flag gatherers: Maximum flag number scored, total flags scored, turns survived, and surviving | ||
robots at the end. | ||
Tie-breakers for best survivors: turns survived, surviving robots at the end, maximum flag number scored, and total | ||
flags scored. | ||
|
||
Special Rules (for special people): | ||
|
||
1. Do not share memory | ||
Your robots are individuals and prefer to be treated as such. Do not write to memory in a place from which other | ||
robots, even of the same type, read. Just use your personal memory dict. | ||
2. Do not raise exceptions | ||
If a robot raises an exception when asked for a move, that robot will be considered to overheat, taking 1 damage. | ||
3. Do not actually overheat | ||
Infinite loops, unbounded memory, or other such issues will cause the game manager to retroactively erase the | ||
existence of offending robot types. Your robots are not Fixed Points and history will forget them. | ||
4. Do not break the fourth wall | ||
Python is a hackable language, but please do not attempt have your robots discover more knowledge about the arena | ||
or life outside than the API provides. Doing so would admit to being a machine and therefore against robot protocol. | ||
5. Write simply | ||
Once your robot easily trashes all the others in the arena, the other contestants will be more amazed to find your | ||
code elegant and simple than a complex beast they could never hope to understand. | ||
There are many dangers and twists to the arena and it may be best to ignore some, moving with simple purpose. Feel | ||
free to write concise strategies that may not have a chance to win. | ||
6. You may show discernment | ||
When a robot examines their surroundings in the arena, they are able to see the names of other robots nearby. It is | ||
acceptable to act differently to different robots such as attempting not to shoot robots with particular names. | ||
It is also acceptable to have one robot's purpose be to help a particular other robot to victory. |
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import itertools | ||
|
||
RECTANGLE = 0 | ||
SQUARE = 1 | ||
HEX = 2 | ||
|
||
NONE = 0 | ||
ALL = 1 | ||
|
||
def init_board(size, fill=' ', shape=RECTANGLE, cell_shape=SQUARE, wrap=NONE): | ||
if shape == RECTANGLE and cell_shape == SQUARE: | ||
return RectangleSquareBoard(size, fill, wrap) | ||
elif shape == RECTANGLE and cell_shape == HEX: | ||
return ToroidalHexBoard(size, fill, wrap) | ||
|
||
class RectangleBoard: | ||
def __init__(self, size, fill, wrap): | ||
self.rows = size[0] | ||
self.cols = size[1] | ||
self.empty = fill | ||
self.wrap = wrap | ||
self.clear() | ||
def clear(self): | ||
self.clear_fill(self.empty) | ||
def clear_fill(self, fill): | ||
self.contents = [[(fill() if callable(fill) else fill) for j in range(self.cols)] for i in range(self.rows)] | ||
def get_item(self, pos): | ||
if self.off_board(pos): | ||
if self.wrap == NONE: | ||
return None | ||
else: | ||
pos = self.wrap_pos(pos) | ||
return self.contents[pos[0]][pos[1]] | ||
def take_item(self, pos): | ||
fill_item = self.empty() if callable(self.empty) else self.empty | ||
return exchange_item(pos, fill_item) | ||
def exchange_item(self, pos, exchange): | ||
if self.off_board(pos): | ||
if self.wrap == NONE: | ||
return None | ||
else: | ||
pos = self.wrap_pos(pos) | ||
item = self.contents[pos[0]][pos[1]] | ||
self.contents[pos[0]][pos[1]] = exchange | ||
return item | ||
def put_item(self, pos, item): | ||
if self.off_board(pos): | ||
if self.wrap == NONE: | ||
raise IndexError('Position out-of-bounds') | ||
else: | ||
pos = self.wrap_pos(pos) | ||
self.contents[pos[0]][pos[1]] = item | ||
def off_board(self, pos): | ||
return pos[0] < 0 or pos[0] >= self.rows or pos[1] < 0 or pos[1] >= self.cols | ||
def wrap_pos(self, pos): | ||
row, col = pos | ||
if pos[0] >= self.rows: | ||
row -= self.rows | ||
if pos[1] >= self.cols: | ||
col -= self.cols | ||
return row, col | ||
def traverse(self): | ||
return ((self.contents[pos[0]][pos[1]], pos) for pos in itertools.product(range(self.rows), range(self.cols))) | ||
|
||
class RectangleSquareBoard(RectangleBoard): | ||
def get_surroundings(self, pos, distance): | ||
# TODO: read wrap | ||
if self.off_board(pos): | ||
raise IndexError('Position out-of-bounds') | ||
surroundings = [] | ||
for row in range(pos[0] - distance, pos[0] + distance + 1): | ||
row_content = [] | ||
for col in range(pos[1] - distance, pos[1] + distance + 1): | ||
row_content.append(self.get_item((row, col))) | ||
surroundings.append(row_content) | ||
return surroundings | ||
|
||
class ToroidalHexBoard(RectangleBoard): | ||
def get_surroundings(self, pos, distance): | ||
surroundings = [] | ||
for row in range(pos[0] - distance, pos[0] + distance + 1): | ||
current_row = [] | ||
rows_away = abs(pos[0] - row) | ||
offset = pos[0] % 2 | ||
start_col = pos[1] - (distance - ((rows_away + offset) // 2)) | ||
num_cols = distance * 2 + 1 - rows_away | ||
for col in range(start_col, start_col + num_cols): | ||
current_row.append(self.get_item((row, col))) | ||
surroundings.append(current_row) | ||
return surroundings |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
.rotateimg90 { | ||
-webkit-transform:rotate(90deg); | ||
-moz-transform: rotate(90deg); | ||
-ms-transform: rotate(90deg); | ||
-o-transform: rotate(90deg); | ||
transform: rotate(90deg); | ||
} | ||
|
||
.rotateimg180 { | ||
-webkit-transform:rotate(180deg); | ||
-moz-transform: rotate(180deg); | ||
-ms-transform: rotate(180deg); | ||
-o-transform: rotate(180deg); | ||
transform: rotate(180deg); | ||
} | ||
|
||
.rotateimg270 { | ||
-webkit-transform:rotate(270deg); | ||
-moz-transform: rotate(270deg); | ||
-ms-transform: rotate(270deg); | ||
-o-transform: rotate(270deg); | ||
transform: rotate(270deg); | ||
} | ||
|
||
.fliphorizontal { | ||
-webkit-transform: scale(-1, 1); | ||
-moz-transform: scale(-1, 1); | ||
-ms-transform: scale(-1, 1); | ||
-o-transform: scale(-1, 1); | ||
transform: scale(-1, 1); | ||
} |
Oops, something went wrong.