Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 63 commits into
base: feedback
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
81b45fe
Initial commit
dgomes Aug 26, 2022
97cb582
playable already
dgomes Aug 27, 2022
c5daedd
v1
dgomes Sep 2, 2022
8ddd2a6
lint + typing
dgomes Sep 2, 2022
9fe258c
data
dgomes Sep 5, 2022
d294185
crazy drivers
dgomes Sep 26, 2022
8ca7e3e
fix for <3.10
dgomes Sep 30, 2022
bb69e93
Setting up GitHub Classroom Feedback
github-classroom[bot] Oct 10, 2022
5ad26c2
don't send stale game_info, wait for new game
dgomes Oct 15, 2022
4dbdae7
crazy car can win alone
dgomes Oct 15, 2022
31534eb
move cursor together with crazy car
dgomes Oct 15, 2022
c3d4aa5
Update README.md
dgomes Oct 15, 2022
9b78a1b
Added a message so the user knows that he needds to connect the clien…
LukasCoder Oct 16, 2022
0ae2692
Some requested changes, unable to remove the for loop because we need…
LukasCoder Oct 16, 2022
a625afe
Removed pygame pump call
LukasCoder Oct 16, 2022
7cd89f0
Changed from stopping the asyncio to just returning the function
LukasCoder Oct 16, 2022
db2c80c
Waiting for game to start + proper exit
dgomes Oct 16, 2022
77acb70
Merge remote-tracking branch 'upstream/main'
danielfcarvalho Oct 19, 2022
cc1a451
created student.py, treesearch.py and domain.py
danielfcarvalho Oct 19, 2022
13fe13e
add score server
dgomes Oct 19, 2022
aee4a92
Thinking about Domain
Oct 23, 2022
80600df
Domain actions
Oct 26, 2022
5309a55
Domain actions done
Oct 26, 2022
ee084d4
Domain actions done v2
Oct 26, 2022
c2ff6ff
finished domain actions, result and satisfies
danielfcarvalho Oct 29, 2022
2a4f188
Started Refactoring Student.py
Nov 1, 2022
cf64455
logic to move the cursor
danielfcarvalho Nov 1, 2022
e350ead
Some updates for it to work
Nov 2, 2022
33ea683
tree search update
danielfcarvalho Nov 7, 2022
56f9c18
Refactoring
Nov 9, 2022
b607635
a*
danielfcarvalho Nov 10, 2022
d58ef4d
comparing string states in IBA
danielfcarvalho Nov 14, 2022
853d38b
actions refactoring
danielfcarvalho Nov 15, 2022
770b1f5
last_moved_piece
Nov 18, 2022
4a5bd05
refactoring get commands function
danielfcarvalho Nov 18, 2022
394839a
dealt with crazy cars
danielfcarvalho Nov 18, 2022
3dd4c6d
Entrega Intermédia
Nov 18, 2022
01da46d
1a entrega
dgomes Nov 19, 2022
b381feb
Merge remote-tracking branch 'upstream/main'
Nov 26, 2022
07ddc8f
fix tree search
Nov 26, 2022
8a5bf34
Add 7x7 levels
dgomes Nov 28, 2022
cc188f1
Update levels.txt
dgomes Nov 28, 2022
858a600
test tree time
Nov 28, 2022
4dad4a5
car info
Nov 28, 2022
7dd300e
support larger maps
dgomes Nov 28, 2022
f784768
actions, results and satisfies
Nov 29, 2022
a068f75
car that moved and its movement
Nov 29, 2022
8690085
satisfies for par and impar maps
Nov 29, 2022
f0021ba
cursor movement
Nov 30, 2022
73503c4
remove stupid try
Nov 30, 2022
7152b44
Merge remote-tracking branch 'upstream/main' into refactor_solution
Nov 30, 2022
f9c3adb
cursor tests
Dec 1, 2022
f2a2c6c
finished function definitions
danielfcarvalho Dec 4, 2022
276ff18
first version of cost and heuristics for new solution
danielfcarvalho Dec 5, 2022
5d4f1b4
heuristic changes
Dec 5, 2022
c7bf577
good heuristic
Dec 7, 2022
14ef057
Merge pull request #2 from detiuaveiro/new_heuristic
danielfcarvalho Dec 7, 2022
3a28bfa
minor updates
danielfcarvalho Dec 8, 2022
262d8ec
deprecated cost function, minor corrections
danielfcarvalho Dec 8, 2022
0f2cc21
benchmark
Dec 9, 2022
afb32c5
Entrega Final
danielfcarvalho Dec 9, 2022
cc6d152
Update README.md
afarturc Jul 29, 2023
60a1710
Update README.md
afarturc Jul 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/ia-tpg-rush-hour-77036_102477_ia.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# ia-tetris
# ia-rush
Projecto de Inteligência Artificial 2022 - Rush Hour

## Context

The goal of this project, done for the subject Inteligência Artificial at Universidade de Aveiro, was to program an AI Bot with tree search algorithms to solve the Rush Hour game. More details with the algorithms used and results can be found in **report.pdf**.

## Development Team

- [Artur Correia](https://github.com/afarturc) ([email protected])
- [Daniel Carvalho](https://github.com/danielfcarvalho) ([email protected])

## How to install

Make sure you are running Python 3.7 or higher
Expand Down Expand Up @@ -32,4 +41,4 @@ Make sure pygame is properly installed:
python -m pygame.examples.aliens

# Tested on:
- OSX Monterey 12.5.1
- OSX Monterey 12.5.1
7 changes: 7 additions & 0 deletions benchmarks.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
LVL 11 (8x8)
Greedy: 0.010278952999897228
Breadth: 14.892354696999973

LVL 38 (6x6)
Greedy: 4.400760039000033
Breadth: 1.1460867919997781
2 changes: 1 addition & 1 deletion client.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@ async def agent_loop(server_address="localhost:8000", agent_name="student"):
# $ NAME='arrumador' python3 client.py
loop = asyncio.get_event_loop()
SERVER = os.environ.get("SERVER", "localhost")
PORT = os.environ.get("PORT", "8000")
PORT = os.environ.get("PORT", "8080")
NAME = os.environ.get("NAME", getpass.getuser())
loop.run_until_complete(agent_loop(f"{SERVER}:{PORT}", NAME))
192 changes: 192 additions & 0 deletions domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
from grid_methods import *

'''
state = (grid, grid_size)
car_info = (car_id, car_index, car_length, car_orientation)
'''

def func_actions(state):
"""
This function will go through the list of all cars in the current Map, with the goal of determining whether it's
possible to move them 1 coordinate up/down (in case they're vertical) or 1 coordinate to the sides
(in case they're horizontal). The move is only considered possible if these coordinates are currently free.

Parameters:
- State: (grid, grid_size) -> Current state of the map

Returns:
A list of all the possible actions. Each action is represented by a tuple containing the info of the car to be
moved, and the key corresponding to the direction in which the move will be made (car_info, key).
"""
grid = state[0]
grid_size = state[1]
actlist = []

# Determining the cars present in the Map
cars = set(grid)
cars.remove('o')
cars.discard('x')

# Checking the possible actions for each car
for car in cars:
car_info = get_car_info(state, car)
car_index = car_info[1]
car_size = car_info[2]
car_orientation = car_info[3]

if car_orientation == 'H':
if car_index % grid_size != 0 and grid[car_index - 1] == 'o':
actlist.append((car_info, 'a'))
if car_index % grid_size + car_size < grid_size and grid[car_index + car_size] == 'o':
actlist.append((car_info, 'd'))
else:
if car_index >= grid_size and grid[car_index - grid_size] == 'o':
actlist.append((car_info, 'w'))
if car_index + car_size * grid_size < grid_size * grid_size and grid[car_index + car_size * grid_size] == 'o':
actlist.append((car_info, 's'))

return actlist


def func_result(state, action):
"""
This function receives the current state of the Map, as well as an action to be applied to a car in the map, and
returns the state of the Map after this action is performed.

Parameters:
- State: (grid, grid_size) -> Current state of the map
- Action: (car_info, key) -> Car in which the action will be applied, as well as the key describing the direction
of the movement.

Returns:
A string representing the new State of the map after the move.
"""
grid = state[0]
grid_size = state[1]
car_info = action[0]
movement = action[1]

car_id = car_info[0]
car_index = car_info[1]
car_size = car_info[2]

grid_after_move = list(grid)

if movement == 'w':
grid_after_move[car_index - grid_size] = car_id
grid_after_move[car_index + car_size * grid_size - grid_size] = 'o'
elif movement == 'a':
grid_after_move[car_index - 1] = car_id
grid_after_move[car_index + car_size - 1] = 'o'
elif movement == 's':
grid_after_move[car_index + car_size * grid_size] = car_id
grid_after_move[car_index] = 'o'
else:
grid_after_move[car_index + car_size] = car_id
grid_after_move[car_index] = 'o'

return (''.join(grid_after_move), grid_size)


def func_cost(state, parent_state):
"""
This function is used to calculate the cost of an action, by considering two sub-costs: the cost of moving the cursor
from its previous position to the piece moved in the current action, and the cost of moving the piece itself.

This function is deprecated in the current version of the program, which uses the breadth and the greedy algorithms to
search for a solution, as it was verified that considering the cost always increases the speed of the tree search by
0.1-0.5 seconds. Thus, this function considers a different deprecated representation of state, in which the ID of the
moved car is saved.

Parameters:
- State: (grid, grid_size, moved_car) -> Current state of the map
- Parent_State: (grid, grid_size, moved_car) -> Previous state of the map. The moved_car is thus the car that was
moved in the previous move of the agent.

Returns:
The cost of the current action by the Agent
"""
moved_car = state[-1]
prev_car = parent_state[-1]

grid_before_move = parent_state[0]
grid_after_move = state[0]

grid_size = state[1]

# Map of the previous state - used to determine the initial coordinates of the moved car, and to determine the
# cost of moving the cursor from its position at the end of the previous move, to the car to be moved in the current
# move
moved_car_index = grid_before_move.index(moved_car)
x_moved_car = moved_car_index % grid_size
y_moved_car = moved_car_index // grid_size

if prev_car is not None:
prev_car_index = grid_before_move.index(prev_car)
x_prev_car = prev_car_index % grid_size
y_prev_car = prev_car_index // grid_size
cursor_cost = abs(x_prev_car - x_moved_car) + (y_moved_car - y_prev_car)
else:
cursor_cost = 0

# Current map - used to determine the final coordinates of the moved car after the move, and thus to determine the
# cost of moving it from its initial position to the final position
moved_car_new_index = grid_after_move.index(moved_car)

new_x_moved_car = moved_car_new_index % grid_size
new_y_moved_car = moved_car_new_index // grid_size

car_movement_cost = abs(x_moved_car - new_x_moved_car) + (y_moved_car - new_y_moved_car)

return cursor_cost + car_movement_cost


def func_heuristic(state):
"""
This function implements the heuristic used in the Greedy and A* search algorithms applied on the project.
In this heuristic, the process starts by checking the distance the player car is from the edge of the map, with
each tile representing 1 heuristic cost. Afterwards, the function verifies how many cars are in the tiles corresponding
to the line the player car is in, with each car counting as 1 additional heuristic cost.

Parameters:
- State: (grid, grid_size) -> Current state of the map

Returns:
The heuristic cost of the current state.
"""
grid = state[0]
grid_size = state[1]

# Determining the line of the player car ('A')
A_index = grid.index('A')

# calculate the distance from the player car to the right edge of the map
h = grid_size - (A_index % grid_size + 1)

# subtract the number of empty cells in the line of the player car
h -= grid[A_index - A_index % grid_size: A_index - A_index % grid_size + grid_size].count('o')

return h



def func_satisfies(state):
"""
This function will verify if the current State of the map already satisfies the conditions to move on to the next
level, that is, if the player car ('A') is at the right edge of the map.

Parameters:
- State: (grid, grid_size) -> Current state of the map

Returns:
True if the car is on the edge of the map, False if not.
"""
grid = state[0]
grid_size = state[1]

car_index = grid.index('A')

# Determining the X coordinate of the player car
car_x = car_index % grid_size + 1

return car_x == grid_size - 1
20 changes: 12 additions & 8 deletions game.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class Game:
"""Main Class."""

def __init__(self, x: int = 6, y: int = 6) -> None:
def __init__(self, x: int = 8, y: int = 8) -> None:
"""Initialize Game."""
logger.info("Game")

Expand Down Expand Up @@ -69,6 +69,9 @@ def next_level(self):
try:
self.grid = self.levels[self.level]
logger.info("NEXT LEVEL: %s", self.level)
self.dimensions = Coordinates(self.grid.grid_size, self.grid.grid_size)
self.cursor = Coordinates(self.dimensions.x // 2, self.dimensions.y // 2)

except KeyError:
logger.info("No more levels... You WIN!")
self.stop()
Expand Down Expand Up @@ -114,6 +117,9 @@ async def loop(self):
random_direction = random.choice([Coordinates(0,-1), Coordinates(0, 1), Coordinates(-1, 0), Coordinates(1, 0)])
self.grid.move(random_piece, random_direction)
logger.debug("Crazy driver: %s moved %s", random_piece, random_direction)
if random_piece == self._selected:
self.cursor.x += random_direction.x
self.cursor.y += random_direction.y
except MapException:
pass

Expand Down Expand Up @@ -149,13 +155,6 @@ async def loop(self):
):
self.grid.move(self._selected, Coordinates(1, 0))
self.cursor.x += 1
# Test victory:
if (
self._selected == self.grid.player_car
and self.grid.test_win()
):
logger.info("Level %s COMPLETED", self.level)
self.next_level()
except MapException as exc:
logger.error("Can't move %s: %s", self._selected, exc)
else:
Expand All @@ -172,6 +171,11 @@ async def loop(self):
):
self.cursor.x += 1

# Test victory:
if self.grid.test_win():
logger.info("Level %s COMPLETED", self.level)
self.next_level()

self._lastkeypress = "-"

return self.info()
Loading