Skip to content

Commit

Permalink
Merge branch 'init'
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-rumpf committed Jan 6, 2021
2 parents 8f1d991 + c1fa239 commit 18da2ac
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ _Combat Turtles_ is meant as a learning tool for intermediate-level [Python](htt

The player can create their own turtle AIs by extending the `TurtleParent` class and overwriting a few key methods. The game is run using discrete step events (at a rate of approximately 30 steps/second), with each turtle defining its actions on a per-step basis. Custom AI submodules (in the form of standalone `.py` files) can be dropped into the `ai/` directory to import the player's AI into the game. Several example and template subclasses are included in this directory to get the player started. See also the [documentation below](#instructions) for a detailed guide to writing custom AIs. Python students might enjoy competing against each other to see whom can come up with the best AI, while Python instructors might consider running a class tournament to encourage students to learn more about object-oriented programming.

**This is a work in progress.** I am still in the process of adding features and fixing bugs, but if you are interested in playing with the latest public beta, please see the [releases](https://github.com/adam-rumpf/combat-turtles/releases) page.
The latest release can be found on this project's [itch.io](https://adam-rumpf.itch.io/combat-turtles) page or on the [releases](https://github.com/adam-rumpf/combat-turtles/releases) page.

## Game Overview

Expand Down
128 changes: 128 additions & 0 deletions ai/wall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Built-In Example AI

# Title: WallTurtle
# Author: Adam Rumpf
# Version: 1.0.0
# Date: 1/5/2021

import math
import game.tcturtle

class CombatTurtle(game.tcturtle.TurtleParent):
"""Wall-hugging combat turtle.
A turtle that attempts to navigate around obstacles by "feeling" the walls
around it.
When it has direct line of sight to the opponent, it moves directly
towards it. Otherwise it moves towards the opponent until hitting a wall,
at which point it attempts to turn so that the way ahead is free.
"""

#-------------------------------------------------------------------------

def class_name():
"""CombatTurtle.class_name() -> str
Static method to return the name of the Combat Turtle AI.
"""

return "WallTurtle"

#-------------------------------------------------------------------------

def class_desc():
"""CombatTurtle.class_desc() -> str
Static method to return a description of the Combat Turtle AI.
"""

return "Hugs walls to get around obstacles."

#-------------------------------------------------------------------------

def class_shape():
"""CombatTurtle.class_shape() -> (int or tuple)
Static method to define the Combat Turtle's shape image.
The return value can be either an integer or a tuple of tuples.
Returning an integer index selects one of the following preset shapes:
0 -- arrowhead (also default in case of unrecognized index)
1 -- turtle
2 -- plow
3 -- triangle
4 -- kite
5 -- pentagon
6 -- hexagon
7 -- star
A custom shape can be defined by returning a tuple of the form
(radius, angle), where radius is a tuple of radii and angle is a tuple
of angles (in radians) describing the polar coordinates of a polygon's
vertices. The shape coordinates should be given for a turtle facing
east.
"""

return 2

#=========================================================================

def setup(self):
"""CombatTurtle.setup() -> None
Initialization code for Combat Turtle.
"""

# Define the relative polar coordinates around the turtle to scan
self.nose_rel = (8, 0.0) # just ahead of turtle's front
self.hand_rel = (8, math.pi/2) # to left of turtle

#-------------------------------------------------------------------------

def step(self):
"""CombatTurtle.setup() -> None
Step event code for Combat Turtle.
"""

# Determine behavior based on whether there is line of sight
if (self.line_of_sight() == True):
# If there is line of sight, move directly towards opponent

# Turn towards opponent
self.turn_towards()

# Move towards opponent (or away if too close)
if self.distance() > 4*self.missile_radius:
self.forward()
else:
self.backward()

# Shoot if facing opponent
if (self.can_shoot == True and
abs(self.relative_heading_towards()) <= 10):
self.shoot()

else:
# If no line of sight, attempt to navigate around obstacles

# Calculate Cartesian coordinates of nose and hand
nose = ((self.x + self.nose_rel[0]*
math.cos(math.radians(self.heading)+self.nose_rel[1])),
(self.y - self.nose_rel[0]*
math.sin(math.radians(self.heading)+self.nose_rel[1])))
hand = ((self.x + self.hand_rel[0]*
math.cos(math.radians(self.heading)+self.hand_rel[1])),
(self.y - self.hand_rel[0]*
math.sin(math.radians(self.heading)+self.hand_rel[1])))

# Determine behavior based on whether nose and hand are clear
if self.free_space(nose) == True:
# Move forward when clear ahead
self.forward()
else:
if self.free_space(hand) == True:
# If free to left, turn left
self.left()
self.forward()
else:
# If blocked ahead and to left, turn right
self.right()
self.forward()
63 changes: 62 additions & 1 deletion game/obj/arena.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Defines the arena container class."""

import math
import random
from .block import Block

class Arena:
Expand All @@ -16,6 +17,7 @@ class Arena:
2 -- four columns near corners
3 -- wall with a central passage
4 -- plus sign
5 -- randomized
"""

#-------------------------------------------------------------------------
Expand All @@ -26,7 +28,7 @@ def get_names():
"""

return ["Empty Arena", "Central Column Arena", "Corner Column Arena",
"Doorway Arena", "Plus-Shaped Arena"]
"Doorway Arena", "Plus-Shaped Arena", "Randomized Arena"]

#-------------------------------------------------------------------------

Expand Down Expand Up @@ -117,6 +119,9 @@ def __init__(self, game, size=(800, 800), layout=0):
elif layout == 4:
# Plus sign
self._plus_blocks()
elif layout == 5:
# Randomized
self._random_blocks()

#-------------------------------------------------------------------------

Expand Down Expand Up @@ -186,6 +191,62 @@ def _plus_blocks(self):
self._blocks.append(Block(self.game, math.floor(self.size[0]/3),
math.ceil(2*self.size[0]/3),
(self.size[1]/2)-30, (self.size[1]/2)+30))

#-------------------------------------------------------------------------

def _random_blocks(self):
"""Arena._random_blocks() -> None
Generates a randomized arena.
The randomized layout is made up of 3-7 blocks, arranged to have
2-fold rotational symmetry about the center, and prohibited from
intersecting the turtles' starting coordinates.
"""

# Initialize a random block height and width
h = random.randrange(10, 151)
w = random.randrange(10, 151)

# Decide whether to include a central block
if random.random() < 0.5:

# Add central block
self._blocks.append(Block(self.game, (self.size[0]/2)-w,
(self.size[0]/2)+w, (self.size[1]/2)-h,
(self.size[1]/2)+h))

# Determine number of additional blocks on sides
num = random.randrange(1, 4)

# Generate side blocks
iter = 0 # iteration counter
while iter < num:

# Generate random dimensions and centers
h = random.randrange(10, 121)
w = random.randrange(10, 121)
cx = random.randrange(self.size[0]+1)
cy = random.randrange(self.size[1]+1)

# Generate tentative blocks
self._blocks.append(Block(self.game, cx-w, cx+w, cy-h, cy+h))
self._blocks.append(Block(self.game, self.size[0]-cx-w,
self.size[0]-cx+w, self.size[1]-cy-h,
self.size[1]-cy+h))

# Test whether the starting coordinates are free
if (self.blocked(self.get_p1_coords()) or
self.blocked(self.get_p2_coords())):

# If not, delete the tentative blocks and retry
del self._blocks[-1]
del self._blocks[-1]
continue

else:

# Otherwise increment the counter
iter += 1

#-------------------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion game/obj/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def __init__(self, game, left, right, bottom, top, col="black"):
Requires the following positional arguments:
game (tcgame.TurtleCombatGame) -- game driver object
left (int) -- smallest x-coordinate (px)
right (int) -- lartst x-coordinate (px)
right (int) -- largest x-coordinate (px)
bottom (int) -- smallest y-coordinate (px)
top (int) -- largest y-coordinate (px)
Expand Down

0 comments on commit 18da2ac

Please sign in to comment.