In order to implement a 'maze game' I need to move a character 'X' around a pre-built maze that exists as a .txt file. I have been trying to achieve this via various read/write/append methods but thus far have had no luck. The best I've been able to do is to read and print the coordinates of the 'X' in the text, but not manipulate it in any way. Any help in sorting out a means by which to move it would be greatly appreciated.
class game():
def __init__(self):
self.score = 0
self.exit = False
self.board = []
filepath = 'maze.txt'
with open(filepath,'r') as fp:
line = fp.readline()
while(line):
self.board.append(list(line))
line = fp.readline()
def show_board (self):
CSI="\x1b["
print ("----------")
print(CSI+"38;40m" + 'score:'+ str(self.score) + CSI + "0m")
for i in range(len(self.board)):
for j in range(len(self.board[i])):
if self.board[i][j] == "X":
CSI="\x1b["
print(CSI+"33;91m" + ''.join(self.board[i][j])+ CSI + "0m", end='', flush=True)
elif self.board[i][j] == "*":
CSI="\x1b["
print(CSI+"36;40m" + ''.join(self.board[i][j])+ CSI + "0m", end='', flush=True)
elif self.board[i][j] == "#":
CSI="\x1b["
print(CSI+"32;40m" + ''.join(self.board[i][j])+ CSI + "0m", end='', flush=True)
else:
CSI="\x1B["
print(CSI+"31;40m" + ''.join(self.board[i][j])+ CSI + "0m", end='', flush=True)
def detect_start_point(self):
for x in range(len(self.board)):
for y in range(len(self.board[x])):
if self.board[x][y] == 'X':
self.i,self.j = x,y
def move_player(self, move):
return
#you may remove return (if it is not required)
board = game()
board.detect_start_point()
board.show_board()
while(True): # you may need to change the condition
# get the selected action from the user here
move=input()
# implement
board.move_player(move)
board.show_board()
In theory I should only need to finish the:
def move_player(self, move):
return
#you may remove return (if it is not required)
But my attempts to produce even basic movement have failed.
For anyone interested in the particulars, the text file (referred to in the code as 'maze.txt') contains:
##############################
# X * # #
# ############ # #
# * # # #
# # * # #
# ###### #
# #
########## # #
#* # #
# # ##############
# # * # * #
# # #
# * #
##############################
Where the * are items to collect, and # is a gate to finish the level. I have no issue coding the conditions for those however.
You don't have to change in file but self.board.
First: you should remeber that self.board keeps data in [row][column] which means [y][x] instead of [x][y]
First you should check if new position is empty ie.
# move down (y+1)
if self.board[self.y+1][self.x] in (' ', "#', '*'):
Next you have to remove player from old place, move its x,y and put in new place
self.board[self.y][self.x] = ' ' # clear old place
self.y += 1
self.board[self.y][self.x] = 'X' # put in new place
Before move it could be good to keep old value in some variable - to use it later to receognize if player is standing on # or *
self.current = self.board[self.y][self.x]
I used io.StringIO to simulate file in memory so I could keep code and data in one file for test.
Only one direction: d moves down
text = """##############################
# X * # #
# ############ # #
# * # # #
# # * # #
# ###### #
# #
########## # #
#* # #
# # ##############
# # * # * #
# # #
# * #
##############################"""
import io
# --- constants ---
CSI = "\x1b["
C0 = "\x1b[0m" # color 0 (zero)
CX = "\x1b[33;91m" # color X (player)
CS = "\x1b[36;40m" # color * (star)
CA = "\x1b[32;40m" # color # (at)
C_ = "\x1b[31;40m" # color _ (floor)
# ---
class Game():
def __init__(self):
self.score = 0
self.exit = False
self.board = []
filepath = 'maze.txt'
#with open(filepath,'r') as fp:
with io.StringIO(text) as fp:
for line in fp:
self.board.append(list(line))
def show_board (self):
print ("----------")
print(CSI+"38;40m" + 'score:'+ str(self.score) + C0)
for row in self.board:
for item in row:
if item == "X":
print(CX + item + C0, end='', flush=True)
elif item == "*":
print(CS + item + C0, end='', flush=True)
elif item == "#":
print(CA + item + C0, end='', flush=True)
else:
print(C_ + item + C0, end='', flush=True)
def detect_start_point(self):
for y, row in enumerate(self.board):
for x, item in enumerate(row):
if item == 'X':
self.x, self.y = x, y
def move_player(self, move):
if move == 'd': # down
#if self.board[self.y+1][self.x] in (' ', "#', '*'):
if self.board[self.y+1][self.x] == ' ':
self.board[self.y][self.x] = ' ' # clear old place
self.y += 1
self.board[self.y][self.x] = 'X' # put in new place
return
#you may remove return (if it is not required)
board = Game()
board.detect_start_point()
board.show_board()
while(True): # you may need to change the condition
# get the selected action from the user here
move = input()
# implement
board.move_player(move)
board.show_board()
EDIT: version more univeral. Player can move u, d, l, r (up/down/left/right) but code is shorter.
text = """##############################
# X * # #
# ############ # #
# * # # #
# # * # #
# ###### #
# #
########## # #
#* # #
# # ##############
# # * # * #
# # #
# * #
##############################"""
import io
# --- constants ---
CSI = "\x1b["
C0 = "\x1b[0m" # color 0 (zero)
CX = "\x1b[33;91m" # color X (player)
CS = "\x1b[36;40m" # color * (star)
CA = "\x1b[32;40m" # color # (at)
C_ = "\x1b[31;40m" # color _ (floor)
# ---
class Game():
def __init__(self):
self.score = 0
self.exit = False
self.board = []
filepath = 'maze.txt'
#with open(filepath,'r') as fp:
with io.StringIO(text) as fp:
for line in fp:
self.board.append(list(line))
def show_board (self):
print ("----------")
print(CSI+"38;40m" + 'score:'+ str(self.score) + C0)
for row in self.board:
for item in row:
if item == "X":
print(CX + item + C0, end='', flush=True)
elif item == "*":
print(CS + item + C0, end='', flush=True)
elif item == "#":
print(CA + item + C0, end='', flush=True)
else:
print(C_ + item + C0, end='', flush=True)
def detect_start_point(self):
for y, row in enumerate(self.board):
for x, item in enumerate(row):
if item == 'X':
self.x, self.y = x, y
def move_player(self, move):
moved = False
if move == 'r': # right
new_x = self.x + 1
new_y = self.y
moved = True
if move == 'l': # left
new_x = self.x - 1
new_y = self.y
moved = True
if move == 'd': # down
new_x = self.x
new_y = self.y + 1
moved = True
if move == 'u': # up
new_x = self.x
new_y = self.y - 1
moved = True
if moved:
if self.board[new_y][new_x] in (' ', '#', '*'):
self.current = self.board[new_y][new_x]
self.board[self.y][self.x] = ' ' # clear old place
self.x = new_x
self.y = new_y
self.board[self.y][self.x] = 'X' # put in new place
if self.current == '*':
self.score += 10
self.current = ' ' # no more gold in current place
return
#you may remove return (if it is not required)
board = Game()
board.detect_start_point()
board.show_board()
while(True): # you may need to change the condition
# get the selected action from the user here
move = input()
# implement
board.move_player(move)
board.show_board()
Related
I am trying to create a Python program using Jupiter Notebook and the A* algorithm, which finds the shortest path out of a hashtag maze. I keep getting the error that the 'NoneType' object has no attribute 'path'. Does anyone have any ideas as to why this could be occurring? I did override the result method, so I am not sure whether that may be causing the issue, but I am uncertain how I could fix this.
The error is-
AttributeError Traceback (most recent call last)
Input In [26], in <cell line: 57>()
88 problem = MazeSolver(MAP)
90 result = astar(problem, graph_search=True)
---> 92 path = [x[1] for x in result.path()]
94 print()
95 for y in range(len(MAP)):
AttributeError: 'NoneType' object has no attribute 'path'
import math
from simpleai.search import SearchProblem, astar
class MazeSolver(SearchProblem):
def __init__(self, board):
self.board = board
self.goal = (0,0)
for y in range(len(self.board)):
for x in range(len(self.board[y])):
if self.board[y][x].lower() == "o":
self.initial = (x, y)
elif self.board[y][x].lower() == "x":
self.goal = (x, y)
super(MazeSolver, self).__init__(initial_state = self.initial)
def actions(self, state):
actions = []
for action in COSTS.keys():
newx, newy = self.result(state, actions)
if self.board[newy][newx] != "#":
actions.append(action)
return actions
def result(self, state, action):
x, y = state
if action.count("up"):
y -= 1
if action.count("down"):
y += 1
if action.count("left"):
x -= 1
if action.count("right"):
x += 1
new_state = (x, y)
return new_state
def is_goal(self, state):
return state == self.goal
def cost(self, state, action, state2):
return COSTS[action]
def heuristic(self, state):
x, y = state
gx, gy = self.goal
return math.sqrt((x - gx) ** 2 + (y - gy) ** 2)
if __name__ == "__main__":
MAP = """
#############################
# # # #
# #### ######## # #
# o # # # #
# ### ##### ###### #
# # ### # #
# # # # # # ###
# ##### # # # x #
# # # #
#############################
"""
print(MAP)
MAP = [list(x) for x in MAP.split("\n") if x]
cost_regular = 1.0
cost_diagonal = 1.7
COSTS = {
"up": cost_regular,
"down": cost_regular,
"left": cost_regular,
"right": cost_regular,
"up left": cost_diagonal,
"up right": cost_diagonal,
"down left": cost_diagonal,
"down right": cost_diagonal
}
problem = MazeSolver(MAP)
result = astar(problem, graph_search=True)
path = [x[1] for x in result.path()]
print()
for y in range(len(MAP)):
for x in range(len(MAP[y])):
if (x, y) == problem.initial:
print('o', end='')
elif(x, y) == problem.goal:
print('x', end='')
elif (x, y) in path:
print('.', end='')
else:
print(MAP[y][x], end='')
print()
class board:
def __init__(self, length, height, letter):
self.layout = ((' ' + letter) * length + ' \n') * height
self.length = length
self.height = height
self.objects = {}
def draw(self, length=None, height=None, letter=None, double=False, show=True):
#adds a line every time
if length == None or height == None or letter == None:
print(self.layout)
else:
if letter != '':
letter = letter[0]
layout = self.layout.split('\n')[:-1]
if layout[0] == '':
layout = layout[1:]
old = ''
for num in range(height):
if num != height:
num = num - 1
old = old + layout[num] + '\n'
new = old
num = length * 2
n = 0
old = ''
for item in layout[height]:
n = n + 1
if n == num:
old_block = item
old = old + letter
else:
old = old + item
string = new + old + '\n'
print(len(layout[height-1:len(layout) -1]))
print(len(layout))
string = string + '\n'.join(layout[height-1:len(layout) -1]) + '\n'
self.layout = string
self.objects[letter] = (length, height, old_block)
if show:
if double:
print(string.replace(' ', ' '))
else:
print(string)
I am trying to create a module which a function that draws a board and you can also add an object at a specific space, but sadly my function called draw adds an unwanted line.
I would be very grateful if you could help me!
self.layout = (((' ' + letter) * length + ' \n') * height)[:-2]
also
self.objects[letter] = (length, height, old_block)
string = string[:-2]
if show:
When I put the snake to start going to the "►" direction, it goes faster than when I do it for "◄" and the same result for "▲" and "▼".
Actually, it literally teleports. Even if I see that it's doing the right way correctly by going from key to key.
Maybe it's doing the process without showing it, until the end.
Do you see any reasons why does that happen? Because I really don't see why.
import numpy as np
from random import randint
import keyboard
from time import sleep
import os
#Get to work
game_area = {}
#Random snake and apple start positions
apple = randint(0, 540)
snake_pos = randint(0, 540)
#Define positions
for x in np.arange(0,540,1):
game_area[x] = '.'
#Insert Apple
game_area[apple] = 'Ó'
#Limit areas
game_area[0], game_area[59], game_area[60], game_area[119],game_area[120], game_area[179],game_area[180], game_area[239],game_area[240],game_area[299],game_area[300],game_area[359], game_area[360],game_area[419],game_area[420],game_area[479],game_area[480], game_area[539] = '╔','╗','║','║','║','║','║','║','║','║','║','║','║','║','║','║','╚','╝'
for x in range(1,59):
game_area[x] = '═'
for x in range(481,539):
game_area[x] = '═'
#Snake Class
class Snake:
def __init__(self, length, position):
self.length = length
self.position = position
def printar(self):
print(self.length,self.position)
snake = Snake(1,snake_pos)
game_area[snake.position] = '►'
#Functions
def prepare_game():
#Create game area
for number, instance in zip(list(game_area.keys()), list(game_area.values())):
if number not in [60,120,180,240,300,360,420,480]:
print(instance, end='')
else:
print('\n' + instance, end='')
print('')
def start_game():
global game_area
game_over = False
while True:
prepare_game()
if game_over == False:
#Keyboard Detect / Automatic Move
if '◄' in list(game_area.values()):
for x,y in game_area.items():
if y == '◄':
if game_area[x - 1] != '║':
game_area[x - 1] = y
game_area[x] = ' '
else:
game_over = True
break
if '►' in list(game_area.values()):
for x,y in game_area.items():
if y == '►':
if game_area[x + 1] != '║':
game_area[x + 1] = y
game_area[x] = ' '
else:
game_over = True
break
if '▲' in list(game_area.values()):
for x,y in game_area.items():
if y == '▲':
if game_area[x - 60] != '═':
game_area[x - 60] = y
game_area[x] = ' '
else:
game_over = True
break
if '▼' in list(game_area.values()):
for x,y in game_area.items():
if y == '▼':
if game_area[x + 60] != '═':
game_area[x + 60] = y
game_area[x] = ' '
else:
game_over = True
break
if keyboard.is_pressed('up arrow'):
pass
if keyboard.is_pressed('left arrow'):
pass
if keyboard.is_pressed('right arrow'):
pass
if keyboard.is_pressed('down arrow'):
pass
if keyboard.is_pressed('space'):
break
#End
sleep(1)
os.system("cls")
continue
else:
break
start_game()
from informedSearch import *
from search import *
class EightPuzzleProblem(InformedProblemState):
"""
Inherited from the InformedProblemState class. To solve
the eight puzzle problem.
"""
def __init__(self, myList, list = {}, operator = None):
self.myList = list
self.operator = operator
def __str__(self):
## Method returns a string representation of the state.
result = ""
if self.operator != None:
result += "Operator: " + self.operator + ""
result += " " + ' '.join(self.myList[0:3]) + "\n"
result += " " + ' '.join(self.myList[3:6]) + "\n"
result += " " + ' '.join(self.myList[6:9]) + "\n"
return result
def illegal(self):
## Tests whether the state is illegal.
if self.myList < 0 or self.myList > 9: return 1
return 0
def equals(self, state):
## Method to determine whether the state instance
## and the given state are equal.
return ' '.join(self.myList) == ' '.join(state.myList)
## The five methods below perform the tree traversing
def move(self, value):
nList = self.myList[:] # make copy of the current state
position = nList.index('P') # P acts as the key
val = nList.pop(position + value)
nList.insert(position + value, 'P')
nList.pop(position)
nList.insert(position, val)
return nList
def moveleft(self):
n = self.move(-1)
return EightPuzzleProblem(n, "moveleft")
def moveright(self):
n = self.move(1)
return EightPuzzleProblem(n, "moveright")
def moveup(self):
n = self.move(-3)
return EightPuzzleProblem(n, "moveup")
def movedown(self):
n = self.move(+3)
return EightPuzzleProblem(n, "movedown")
def operatorNames(self):
return ["moveleft", "moveright", "moveup", "movedown"]
def enqueue(self):
q = []
if (self.myList.index('P') != 0) and (self.myList.index('P') != 3) and (self.myList.index('P') != 6):
q.append(self.moveleft())
if (self.myList.index('P') != 2) and (self.myList.index('P') != 5) and (self.myList.index('P') != 8):
q.append(self.moveright())
if self.myList.index('P') >= 3:
q.append(self.moveup())
if self.myList.index('P') >= 5:
q.append(self.movedown())
def applyOperators(self):
return [self.moveleft(), self.moveright(), self.moveup(), self.movedown()]
def heuristic():
counter = 0
for i in range(len(self.myList)):
if ((self.myList[i] != goal.myList[i]) and self.myList[i] != 'P'):
## Position of current:
current = goal.myList.index(self.myList[i])
if current < 3: goalRow = 0
elif current < 6: goalRow = 1
else: goalRow = 2
if i < 3: initRow = 0
elif i < 6: initRow = 1
else: startRow = 2
initColumn = i % 3
goalColumn = current % 3
counter += (abs(goalColumn - initColumn) + abs(goalRow - initRow))
return counter
#Uncomment to test the starting states:
init = ['1','3','P','8','2','4','7','6','5'] #A
#init = ['1','3','4','8','6','2','P','7','5'] #B
#init = ['P','1','3','4','2','5','8','7','6'] #C
#init = ['7','1','2','8','P','3','6','5','4'] #D
#init = ['8','1','2','7','P','4','6','5','3'] #E
#init = ['2','6','3','4','P','5','1','8','7'] #F
#init = ['7','3','4','6','1','5','8','P','2'] #G
#init = ['7','4','5','6','P','3','8','1','2'] #H
goal = ['1','2','3','8','P','4','7','6','5'] #goal state
InformedSearch(EightPuzzleProblem(init), EightPuzzleProblem(goal))
I run it and it shows error
line 34, in __str__ result += " " + ' '.join(self.myList[0:3]) + "\n"
TypeError: unhashable type: 'slice'
Any Ideas?
You're setting the "list" to a dictionary as a default value: list = {} in:
def __init__(self, myList, list = {}, operator = None):
and then assigning it to myList with:
self.myList = list
A dictionary cannot be sliced like a list. So when you try to slice it:
self.myList[0:3]
it fails.
I have programmed a simple console game that allows me to move my player inside a small level with walls and blank spaces. It's all done using only few simple functions.
I'm rather new to Python but I'd like to learn OOP next, how would I continue from here on if I wanted to make this game object-oriented?
I understand classes and objects quite well, but bear with me if I don't understand all the answers.
Here's the current game:
LEVEL = [
'xxxxxx',
'x x',
'x i x',
'x x',
'x x',
'xxxxxx'
]
def get_block(x, y):
"""Gets a block at the given coordinates."""
try:
return LEVEL[y][x]
except IndexError:
return None
def set_block(x, y, block):
"""Sets a block at the given coordinates."""
try:
LEVEL[y] = LEVEL[y][:x] + block + LEVEL[y][x + 1:]
except IndexError:
pass
def get_player_position():
"""Gets player's position."""
for y, row in enumerate(LEVEL):
for x, column in enumerate(row):
if column == 'i':
return x, y
def set_player_position(x, y):
"""Sets player's position."""
block = get_block(x, y)
if block == ' ':
px, py = get_player_position()
set_block(px, py, ' ')
set_block(x, y, 'i')
def main():
"""Entry point for the program."""
cmd = ''
while cmd.lower() not in ('quit', 'q'):
print('\n' * 30)
for row in LEVEL:
print(row)
cmd = input('Command: ').lower()
px, py = get_player_position()
if cmd == 'w':
set_player_position(px, py - 1)
elif cmd == 's':
set_player_position(px, py + 1)
elif cmd == 'a':
set_player_position(px - 1, py)
elif cmd == 'd':
set_player_position(px + 1, py)
print('Bye.')
if __name__ == '__main__':
main()
You're question is pretty open-ended, so it's difficult to give an all-encompassing answer — so what I've done instead is identified one data-structure in your existing code and turned it an class.
Functions which used to operate on global data-datastructure, are now all public methods of instances of the class which is the only ones that's allowed make changes to the data held inside it in a private attribute named _field.
Doing this sort of thing is an essential first step in writing object-oriented software.
Hope you find the example somewhat enlightening.
class PlayingField(object):
# Class constants
PLAYER = 'i'
EMPTY = ' '
EDGE = 'x'
DEFAULT_SIZE = 6
def __init__(self, size=DEFAULT_SIZE):
X, EMPTY = self.EDGE, self.EMPTY
self._size = size
# build playing field
self._field = [size*X] + (size-2)*[X + (size-2)*EMPTY + X] + [size*X]
self._set_block(2, 2, self.PLAYER) # Initialize player's position.
def display(self):
print(30*'\n')
for row in self._field:
print(row)
def get_player_position(self):
"""Gets player's position."""
for y, row in enumerate(self._field):
for x, column in enumerate(row):
if column == self.PLAYER:
return x, y
else:
raise ValueError("Couldn't determine player's location on field")
def set_player_position(self, x, y):
"""Sets player's position."""
block = self._get_block(x, y)
if block == self.EMPTY:
px, py = self.get_player_position()
self._set_block(px, py, self.EMPTY)
self._set_block(x, y, self.PLAYER)
# Private methods
def _get_block(self, x, y):
"""Gets a block at the given coordinates."""
try:
return self._field[y][x]
except IndexError:
return None
def _set_block(self, x, y, block):
"""Sets a block at the given coordinates."""
try:
self._field[y] = self._field[y][:x] + block + self._field[y][x + 1:]
except IndexError:
pass
def main():
"""Entry point for the program."""
field = PlayingField()
cmd = ''
while cmd.lower() not in ('quit', 'q'):
field.display()
cmd = input('Command: ').lower()
px, py = field.get_player_position()
if cmd == 'w':
field.set_player_position(px, py - 1)
elif cmd == 's':
field.set_player_position(px, py + 1)
elif cmd == 'a':
field.set_player_position(px - 1, py)
elif cmd == 'd':
field.set_player_position(px + 1, py)
print('Bye.')
if __name__ == '__main__':
main()