Individual keypress bug in python - python

The following code is a python sprinting game. It was posted as an answer to my previous post, by #mango You have to tap 'a' and 'd' as fast as you can to run 100 meters. However, there are a few bugs...
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
Previous code:
import msvcrt
import time
high_score = 50
name = "no-one"
while True:
distance = int(0)
print("\n--------------------------------------------------------------")
print('\n\nWelcome to the 100m sprint, tap a and d rapidly to move!')
print('* = 10m')
print("\n**Current record: " + str(high_score) + "s, by: " + name)
print('\nPress enter to start')
input()
print('Ready...')
time.sleep(1)
print('GO!')
start_time = time.time()
while distance < 100:
k1 = msvcrt.getch().decode('ASCII')
if k1 == 'a':
k2 = msvcrt.getch().decode('ASCII')
if k2 == 'd':
distance += 1
if distance == 50:
print("* You're halfway there!")
elif distance % 10 == 0:
print('*')
fin_time = time.time() - start_time
fin_time = round(fin_time,2)
print('Well done you did it in...'+str(fin_time))
if fin_time < high_score:
print("Well done you've got a new high score ")
name = input("Please enter your name : ")
This is the code that I recieved as feedback, I have made a few changes, but I am struggling witht the bugs that I listed before.
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
# these are the modules we'll need
import sys
import tkinter as tk
class SprintGame(object):
# this represents the distance we'll allow our player to run
# NOTE: the distance is in meters
distance = 100
# this represents the stride length of the players legs
# NOTE: basically how far the player can travel in one footstep
stride = 1.5
# this represents the last key the user has pressed
lastKey = ""
# this represents wether or not the race has been completed
completed = False
# this function initiates as soon as the "sprint" variable is defined
def __init__(self):
# create the tk window
self.root = tk.Tk()
# set the tk window size
self.root.geometry('600x400')
# set the tk window title
self.root.title("Sprinting Game")
# bind the keypress event to the self.keypress handler
self.root.bind('<KeyPress>', self.keypress)
# center the window
self.centerWindow(self.root)
# insert the components
self.insertComponents()
# initial distance notice
self.output("{0}m left to go!".format(self.distance))
# start out wonderful game
self.start()
# this function centers the window
def centerWindow(self, window):
window.update_idletasks()
# get the screen width
width = window.winfo_screenwidth()
# get the screen height
height = window.winfo_screenheight()
# get the screen size
size = tuple(int(_) for _ in window.geometry().split('+') [0].split('x'))
# get the screen's dimensions
x = (width / 2) - (size[0] / 2)
y = (height / 2) - (size[1] / 2)
# set the geometry
window.geometry("%dx%d+%d+%d" % (size + (x, y)))
# this function replaces the old text in the textbox with new text
def output(self, text = ""):
self.text.delete('1.0', tk.END)
self.text.insert("end", text)
# this function handles key presses inside the tkinter window
def keypress(self, event):
# get the key and pass it over to self.logic
self.logic(event.char)
# this function handles game logic
def logic(self, key):
# convert key to a lower case string
key = str(key).lower()
# let us know how far we've got left
if key == "l":
self.output("{0}m left to go!".format(self.distance))
# restart the race
if key == "r":
# clear the output box
self.text.delete('1.0', tk.END)
# reset the distance
self.distance = 100
# reset the stride
self.stride = 1.5
# reset the last key
self.lastKey = ""
# set race completed to false
self.completed = False
# output restart notice
self.output("The Race Has Been Restarted.")
# don't bother with logic if race is completed
if self.completed == True:
return False
# check if distance is less than or equal to zero (meaning the race is over)
if self.distance <= 0:
# set the "self.completed" variable to True
self.completed = True
# let us know we've completed the race
self.output("Well done, you've completed the race!")
# return true to stop the rest of the logic
return True
# convert the key to lower case
key = key.lower()
# this is the quit function
if key == "q":
# lights out...
sys.exit(0)
# check if the key is a
if key == "a":
# set the last key to a so that we can decrement the "distance"
# variable if it is pressed next
self.lastKey = "a"
# only bother with "d" keypresses if the last key was "a"
if self.lastKey == "a":
# capture the "d" keypress
if key == "d":
# decrement the "distance" variable
self.distance -= self.stride
# let us know how far into the game we are
self.output("{0}m left to go!".format(self.distance))
# this function inserts the components into the window
def insertComponents(self):
# this component contains all of our output
self.text = tk.Text(self.root, background='#d6d167', foreground='#222222', font=('Comic Sans MS', 12))
# lets insert out text component
self.text.pack()
# this function opens the window and starts the game
def start(self):
self.root.mainloop()
# create a new instance of the game
Game = SprintGame()

Related

Window freeze for single thread in while loop of Pygame

I am writing a short program to display cards in a round. I suspect that it is the length of the code which prevents the final 'OK' submit on P3 (the last player's submission) from executing properly: at which point the program sometimes will evaluate the winner and clear the round, but most of the time instead will freeze.
I have tried clock.tick(low fps), pygame.event.pump(), and pygame.event.clear(). Any leads would be much appreciated.
# Round loop begins. Finish until all hands are empty.
while not self.game.get_is_last_round():
player = self.game.get_player(self.game.get_player_turn())
hand = player.order_hand(player.get_hand(),
self.game.get_round_level(),
self.game.get_round_trump_suit())
ok_clicked_2 = False
pygame.event.pump()
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.deal_running = False
self.is_running = False
pygame.display.quit()
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
play = player.get_play()
click = pygame.mouse.get_pressed(num_buttons=3)
pos = pygame.mouse.get_pos()
# Used DeMorgan's law to resolve error
ok_clicked_2 = (OK1_X < pos[0] < OK1_X + B_W) and (OK1_Y < pos[1] < OK1_Y + B_H) and click[0]
b1, card = self.check_hand(pos, player)
b2, play_card = self.check_play(pos, player)
if b1:
hand.remove(card)
play.append(card)
player.set_play(
player.order_hand(play, self.game.get_round_level(),
self.game.get_round_trump_suit()))
player.set_hand(
player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
if b2:
play.remove(play_card)
hand.append(play_card)
player.set_play(
player.order_hand(play, self.game.get_round_level(),
self.game.get_round_trump_suit()))
player.set_hand(player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
clock.tick(100)
surface.blit(background, (0, 0))
if len(self.game.get_player(0).get_hand()) == 25:
self.game.set_is_first_round(True)
else:
self.game.set_is_first_round(False)
if len(self.game.get_player(0).get_hand()) == 0:
self.game.set_is_last_round(True)
else:
self.game.set_is_last_round(False)
if self.game.get_play_in_turn() != NUM_PLAYERS:
pygame.event.pump()
clock.tick(100)
if len(hand) <= 1:
width = 0
x = (BG_WIDTH - CARD_WIDTH) // 2
elif len(hand) >= 8:
width = (BG_WIDTH - SIDE_W - CARD_WIDTH) // (len(hand) - 1)
x = BG_WIDTH // 2 - (CARD_WIDTH + (width * (len(hand) - 1))) // 2
else:
width = CARD_WIDTH
x = (BG_WIDTH - (CARD_WIDTH * len(hand))) // 2
surface.blit(background, (0, 0))
self.blit_backs()
self.blit_round()
self.show_ok()
self.show_hand(x, ROW3h, width, hand)
self.show_hand(CARD_POSITIONS[0][0], CARD_POSITIONS[0][1], SLIM_WIDTH, play)
if ok_clicked_2:
for card in play:
hand.append(card)
player.set_hand(player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
# If player is first to start a round, he/she has a different validity check.
# (Sets the pattern for the cycle)
if player.get_begins_cycle():
valid = self.game.check_validity(True) # is_first
else:
valid = self.game.check_validity(False) # Is not first to play in the round
if not valid: # Clear holding if invalid
if (play == []) or (player.get_play() == []):
print("\nYou must make a play.\n")
else:
print("Invalid play. Try again.")
if not player.get_begins_cycle():
valid_plays = player.get_valid_plays(self.game.get_pattern(),
self.game.get_round_trump_suit())
print("Valid plays: \n")
for temp_play_idx in range(len(valid_plays)):
temp_play = valid_plays[temp_play_idx]
print("[", end='')
for temp_card_idx in range(len(temp_play)):
valid_plays[temp_play_idx][temp_card_idx].show_card("", '')
if temp_card_idx != len(temp_play) - 1:
print(", ", end='')
print("]")
# Clear the current player's selection and restore hand to its original content
cycle_order = self.game.get_cycle_order()
cycle = self.game.get_cycle()
for player_order in range(len(cycle_order)):
if player == cycle_order[player_order]:
cycle_order.remove(player)
cycle.pop()
self.game.set_cycle_order(cycle_order)
self.game.set_cycle(cycle)
else: # Valid play on submit
# Special case for HIGH_SUIT play, play lowest card if another player has greater
play = self.game.check_high_suit(play)
# If friend card played, establish and print teammates
# TODO: auto-designate friends if the last round
# has been reached (friends buried in treasure case)
# TODO: determine whether friend is "dead"
self.game.check_for_friends()
cycle = self.game.get_cycle()
cycle.append(play)
self.game.set_cycle(cycle)
cycle_order = self.game.get_cycle_order()
cycle_order.append(player)
self.game.set_cycle_order(cycle_order)
# self.clear_positions()
for card in play:
hand.remove(card)
player.set_hand(
player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
self.game.next_player_turn()
self.game.set_play_in_turn(self.game.get_play_in_turn() + 1)
print(self.game.get_play_in_turn())
play = []
else:
self.game.set_play_in_turn(0)
# Distribute any points in the round to round winner
self.update_cycle_points()
for p in self.game.get_players():
for card in p.get_play():
discard = self.game.get_discard()
discard.append(card)
p.set_play([])
pygame.event.clear()
clock.tick(100)
pygame.display.update()
I think it's time for a code-cleanup, then your issue will go away (or you'll find it).
Currently the main loop is a big mix-up of event handling, screen-painting and game engine. Try to separate these parts out.
Move some of the in-loop processing out to functions - like the block after if ok_clicked_2:. It may help to make a data structure in which you store the game-state, then have the events change that game state. When it comes time to draw the game to the screen, the painting code can query the state, acting accordingly.
In terms of your actual lockup, if self.game.get_play_in_turn() == NUM_PLAYERS nothing is painted to the screen. Is this intentional? Add some print()s to your code so you can know the execution flow (or learn to use the python debugger).
I think the biggest step forward would be to move all the screen painting to one section of the main loop, something like:
# Render the screen
print( "Rendering Screen" )
surface.blit(background, (0, 0))
self.blit_backs()
self.blit_round()
# etc. for all other things, score, buttons, ...
clock.tick(60)
pygame.display.update()
You seem to be handling the events OK, so it would probably be better to remove the calls to pygame.event.pump() and pygame.event.clear(). You don't need these.
Following Kingsley's advice, I organized the code by function: rendering screen, game engine, and event handling. I would provide MRE as Random Davis suggests, but that would include 5 integrated files which would take too long to pare down.
It turns out that the problem lay in a piece of code which is called separately: "update_cycle_points()". Within, there is a while loop which does not contain an event handler. The solution was to change it to a for loop, which Pygame seems to process without error (does not freeze because it does not expect event handling there).
I also removed pygame.event.clear() and pump() functions without problems.

How to know who is winner in Tic Tac Toe in python using guizero

I have created a game called Tic Tac Toe. There are 2 players one of them are Xs one of them are Os all you have to do is get your symbol 3 in a row without the other person blocking you.
The Gui for the game looks like this:
Code:
from guizero import App, TextBox, PushButton, Text, info
empty = ' '
player = "X"
def clicked(z):
button = buttonlist[int(z)] # Finds out which button was pressed
global empty, player
if button.text != empty:
pass # If button already pushed do nothing
else:
# Marks button with user's go
button.text = player
# Switches players
if player == "X":
player = "O"
else:
player = "X"
return
def instructions():
info("Instructions","There are 2 players one of them are Xs one of them are Os. All you have to do is you have to try getting your symbol 3 times in a row without the other player blocking you")
def quit_game():
app.destroy()
app = App(title="Tic Tac Toe", layout="grid", width=200, height=250)
buttonlist = [] # Empty list to contain a list of Buttons
Player1_label = Text(app, text="Player 1", align="top", grid=[0, 0, 2 , 1])
Player1 = TextBox(app, text=empty, align="top", grid=[2, 0, 3, 1])# Player 1 enters the username
Player2_label = Text(app, text="Player 2", align="top", grid=[0, 1, 2 , 1])
Player2 = TextBox(app, text=empty, align="top", grid=[2, 1, 3, 1])# Player 2 enters the username
Instructions = PushButton(app, command=instructions, text="Instructions", grid=[0, 5, 3, 1])# Display the instructions
Quit = PushButton(app, command=quit_game ,text="Quit",grid=[10, 0, 3, 2])
# Create Buttons for game, in 3 rows of 3
z=0
for x in range(3):
for y in range(2, 5):
buttonlist.append(PushButton(app, text=empty, args=str(z), grid=[x, y], command=clicked))
z+=1
app.display()
The problem I have is in displaying who is the winner. I know how to make a window appear to show the winner and the line that does this is:
app.info("Tic Tac Toe Winner","The winner is" + Player1.value)
But the problem that i am having is knowing who is the winner at the end of the game so and displaying that they are the winner also I have a feeling that to find out the winner its got something to do with buttonlist list that i made but i just cannot figure out how to do it so I would appriciate if some one could help me.
Thanks
Nice game. To work with the game board, I would suggest to create an internal data representation rather than working with the gui elements all the time according to common practice.
The reason is, that if you work with the gui elements everywhere, it might be slower, as it is not the ideal data representation. You could not save the data with pickle etc and you also add a strong dependency to the gui framework you use.
E.g. if you later plan to convert it to a web application etc. you will have a very hard time.
So I just created a simple data representation in which I store the field in a two dimensional array.
# check_win checks if there are three markings
# in one row/column or diagonally
def check_win():
# the checks array is just a trick to
# reduce the amount of code,
# you could avoid it by just adding
# one nested loop where the outer iterates
# over y and the inner over x
# one with the outer on x and the inner on y
# and then two others with just one loop
# for the diagonals
# the body of the inner loops would
# esentially look the same as for the while
# loop below
checks= [(0, 0, 1, 1), (0, 2, 1, -1)]
for i in range(3):
checks.append((i, 0, 0, 1))
checks.append((0, i, 1, 0))
won= None
for y, x, incr_y, incr_x in checks:
player= field[y][x]
found= player is not None
while found and x < 3 and y < 3:
found= (player == field[y][x])
y+= incr_y
x+= incr_x
if found:
won= player
break
return won
# I also changed your clicked method a bit, so
# it calles the check_win method
# and also changed the signature, so
# it gets the y and x coordinate of the
# button which makes maintaining the
# field array inside the clicked method
# a bit simpler and also seems more natural
# to me
def clicked(y, x):
button = buttonlist[y][x] # Finds out which button was pressed
global empty, player
if button.text != empty:
pass # If button already pushed do nothing
else:
# Marks button with user's go
button.text = player
field[y][x] = player
# Switches players
if player == "X":
player = "O"
else:
player = "X"
won= check_win()
if won is not None:
print(f'Player {won} has won the game')
return
# now I initialize the field array
# it will contain None for an untouched
# cell and X/O if taken by the corresponding
# user
field= [[None] * 3 for i in range(3)]
buttonlist= list()
# to get the buttons to call the
# clicked function with the new
# signature and also maintain the
# buttons in a two dimensional array
# I changed the order of your loops
# and the args argument
for y in range(0, 3):
rowlist= list()
buttonlist.append(rowlist)
for x in range(3):
rowlist.append(PushButton(app, text=empty, args=(y, x), grid=[x, y+2], command=clicked))
z+=1
app.display()
# a plain vanilla version of check_win would look something like:
def check_win():
for start in range(3):
x= start
mark= field[0][x]
for y in range(1, 3):
if field[y][x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
y= start
mark= field[y][0]
for x in range(1, 3):
if field[y][x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
mark= field[0][0]
for x in range(1, 3):
if field[y][x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
mark= field[0][3]
for x in range(1, 3):
if field[y][2-x] != mark:
# the sequence is not complete
mark= None
if mark is not None:
# player who set the mark won
return mark
return None

Python Curses addstr() error when keys are not pressed

I'm recreating this game for terminal in python using the curses library. Whenever a key is not pressed, stdscr.addstr() returns an error.
This seems to be because the cursor is off screen (in the bottom-right corner) during those frames, but I can't figure out why it moves at all. The canvas that I'm printing is always the same size (It consists entirely of spaces which are replaced when Apple or Basket objects are rendered). I tried decreasing the canvas size, but the cursor still goes to that corner. I also tried setting curses.leaveok(True), and the cursor seemed to be following the basket, but my logging proves that it is still going to that corner.
The file that uses curses:
import random
import time
import curses
from apple_season.basket import Basket
from apple_season.apple import Apple
from apple_season.coords import Canvas
def main(stdscr):
curses.curs_set(1) # so i can see where the cursor is
dims = [curses.COLS - 1, curses.LINES - 1] # pylint: disable=no-member
stdscr.nodelay(True)
stdscr.leaveok(True)
key=""
stdscr.clear()
canvas = Canvas(*dims)
basket = Basket(canvas)
apples = []
i = 0
def finished_apples():
if len(apples) <= 100:
return False
else:
for apple in apples:
if not apple.has_fallen:
return False
return True
while not finished_apples():
if len(apples) <= 100: # don't make more if there are already 100
# decide whether or not to create new apple (1/100 chance per frame)
num = random.randint(0, 100)
if num == 25:
apples.append(Apple(canvas))
try:
key = stdscr.getkey()
stdscr.clear()
# pick up keyboard inputs
# quit option
if str(key) == "q":
break
# right arrow
elif str(key) == "KEY_RIGHT":
basket.move('right')
# left arrow
elif str(key) == "KEY_LEFT":
basket.move('left')
except Exception:
pass
# render objects - alters canvas to display them
for apple in apples:
if apple.has_fallen:
apple.render()
else:
if '.0' not in str(i / 2): # check if i is even (drop every other frame)
apple.fall()
apple.render()
basket.render()
try:
stdscr.addstr(canvas.display)
except Exception:
pass
stdscr.refresh()
i += 1
time.sleep(0.01)
if __name__ == "__main__":
curses.wrapper(main)
(The code above runs fine, but it doesn't do anything when stdscr.addstr(canvas.display) doesn't work, which is whenever there are no keys pressed)
What's interesting is that this doesn't happen when it's just the basket or just the apples.
To see all of the code: https://github.com/lol-cubes/Terminal-Apple-Season/tree/soErrorCode.
I put the stdscr.clear() in the try and except block, which made it so that that code was only executed when a key was pressed, therefore overflowing the terminal because I was attempting to display multiple frames at once.

Getting the computer to press a button

I'm making a tic tac toe game using tkinter for the GUI but my issue is, since i've got a function (that makes the moves) to run each time a button is clicked, it won't automatically make a move until I click a button (it doesn't matter which one) which hence stuffs up my moves indexing, and also means you have to click before the computer makes a move.
Is there anyway I can get the 'computer' itself to pretend to click a button, hence initiating the function?
Here's the make button bit:
def make_button(n, row, col):
button_list[n] = Button(tk,bg='gray', text = " ", fg='white',height=4,width=8,command=lambda:ttt(n, button_list))
button_list[n].grid(row=row,column=col,sticky = S+N+E+W)
and here's the make move bit (its wip so don't mind the fact that currently computer move will only play the middle or the corners):
def computer_move(buttons, index):
global bclick
buttons["text"] = "O"
COM.add(index)
bclick = True
buttons=StringVar()
bclick = True
corner_moves = {0,2,6,8}
def ttt(n, buttons):
global bclick
if n not in P and n not in COM and bclick == True:
button_list[n]["text"] = "X"
P.add(n)
bclick = False
elif bclick == False and 4 not in COM and 4 not in P:
computer_move(button_list[4], 4)
elif bclick == False:
corners_not_COM = (corner_moves.difference(COM))
corners_not_P_or_COM = (corners_not_COM.difference(P))
list_corners_not_P_or_COM = list(corners_not_P_or_COM)
random_corner_index = random.randint(0,len(list_corners_not_P_or_COM))
random_corner = list_corners_not_P_or_COM[random_corner_index]
computer_move(button_list[random_corner], random_corner)
Try using:
computer_move.invoke()
it has the same effect as clicking on the button it should work in your case. It calls the function. You can call it after you have moved and it will call computer_move for you. Happy coding!

Pygame Random Dungeon Generation

I have looked online everywhere and I am yet to find how to create a basic random dungeon generation technique.
I have been experimenting myself by randomising a list of numbers and allocating them to width height and position etc. but I just can't seem to get it working.
It would be great if someone could direct me to a website or maybe point me in the right direction, I am a little inexperienced. Thanks in advance.
Here's an example of a random dungeon generator, taken from RogueBasin, which has a lot of articles on that topic and on rogue-likes in general:
# Class to produce random map layouts
from random import *
from math import *
class dMap:
def __init__(self):
self.roomList=[]
self.cList=[]
def makeMap(self,xsize,ysize,fail,b1,mrooms):
"""Generate random layout of rooms, corridors and other features"""
# makeMap can be modified to accept arguments for values of failed, and percentile of features.
# Create first room
self.size_x = xsize
self.size_y = ysize
# initialize map to all walls
self.mapArr=[]
for y in range(ysize):
tmp = []
for x in range(xsize):
tmp.append(1)
self.mapArr.append( tmp )
w,l,t=self.makeRoom()
while len(self.roomList)==0:
y=randrange(ysize-1-l)+1
x=randrange(xsize-1-w)+1
p=self.placeRoom(l,w,x,y,xsize,ysize,6,0)
failed=0
while failed<fail: #The lower the value that failed< , the smaller the dungeon
chooseRoom=randrange(len(self.roomList))
ex,ey,ex2,ey2,et=self.makeExit(chooseRoom)
feature=randrange(100)
if feature<b1: #Begin feature choosing (more features to be added here)
w,l,t=self.makeCorridor()
else:
w,l,t=self.makeRoom()
roomDone=self.placeRoom(l,w,ex2,ey2,xsize,ysize,t,et)
if roomDone==0: #If placement failed increase possibility map is full
failed+=1
elif roomDone==2: #Possiblilty of linking rooms
if self.mapArr[ey2][ex2]==0:
if randrange(100)<7:
self.makePortal(ex,ey)
failed+=1
else: #Otherwise, link up the 2 rooms
self.makePortal(ex,ey)
failed=0
if t<5:
tc=[len(self.roomList)-1,ex2,ey2,t]
self.cList.append(tc)
self.joinCorridor(len(self.roomList)-1,ex2,ey2,t,50)
if len(self.roomList)==mrooms:
failed=fail
self.finalJoins()
def makeRoom(self):
"""Randomly produce room size"""
rtype=5
rwide=randrange(8)+3
rlong=randrange(8)+3
return rwide,rlong,rtype
def makeCorridor(self):
"""Randomly produce corridor length and heading"""
clength=randrange(18)+3
heading=randrange(4)
if heading==0: #North
wd=1
lg=-clength
elif heading==1: #East
wd=clength
lg=1
elif heading==2: #South
wd=1
lg=clength
elif heading==3: #West
wd=-clength
lg=1
return wd,lg,heading
def placeRoom(self,ll,ww,xposs,yposs,xsize,ysize,rty,ext):
"""Place feature if enough space and return canPlace as true or false"""
#Arrange for heading
xpos=xposs
ypos=yposs
if ll<0:
ypos+=ll+1
ll=abs(ll)
if ww<0:
xpos+=ww+1
ww=abs(ww)
#Make offset if type is room
if rty==5:
if ext==0 or ext==2:
offset=randrange(ww)
xpos-=offset
else:
offset=randrange(ll)
ypos-=offset
#Then check if there is space
canPlace=1
if ww+xpos+1>xsize-1 or ll+ypos+1>ysize:
canPlace=0
return canPlace
elif xpos<1 or ypos<1:
canPlace=0
return canPlace
else:
for j in range(ll):
for k in range(ww):
if self.mapArr[(ypos)+j][(xpos)+k]!=1:
canPlace=2
#If there is space, add to list of rooms
if canPlace==1:
temp=[ll,ww,xpos,ypos]
self.roomList.append(temp)
for j in range(ll+2): #Then build walls
for k in range(ww+2):
self.mapArr[(ypos-1)+j][(xpos-1)+k]=2
for j in range(ll): #Then build floor
for k in range(ww):
self.mapArr[ypos+j][xpos+k]=0
return canPlace #Return whether placed is true/false
def makeExit(self,rn):
"""Pick random wall and random point along that wall"""
room=self.roomList[rn]
while True:
rw=randrange(4)
if rw==0: #North wall
rx=randrange(room[1])+room[2]
ry=room[3]-1
rx2=rx
ry2=ry-1
elif rw==1: #East wall
ry=randrange(room[0])+room[3]
rx=room[2]+room[1]
rx2=rx+1
ry2=ry
elif rw==2: #South wall
rx=randrange(room[1])+room[2]
ry=room[3]+room[0]
rx2=rx
ry2=ry+1
elif rw==3: #West wall
ry=randrange(room[0])+room[3]
rx=room[2]-1
rx2=rx-1
ry2=ry
if self.mapArr[ry][rx]==2: #If space is a wall, exit
break
return rx,ry,rx2,ry2,rw
def makePortal(self,px,py):
"""Create doors in walls"""
ptype=randrange(100)
if ptype>90: #Secret door
self.mapArr[py][px]=5
return
elif ptype>75: #Closed door
self.mapArr[py][px]=4
return
elif ptype>40: #Open door
self.mapArr[py][px]=3
return
else: #Hole in the wall
self.mapArr[py][px]=0
def joinCorridor(self,cno,xp,yp,ed,psb):
"""Check corridor endpoint and make an exit if it links to another room"""
cArea=self.roomList[cno]
if xp!=cArea[2] or yp!=cArea[3]: #Find the corridor endpoint
endx=xp-(cArea[1]-1)
endy=yp-(cArea[0]-1)
else:
endx=xp+(cArea[1]-1)
endy=yp+(cArea[0]-1)
checkExit=[]
if ed==0: #North corridor
if endx>1:
coords=[endx-2,endy,endx-1,endy]
checkExit.append(coords)
if endy>1:
coords=[endx,endy-2,endx,endy-1]
checkExit.append(coords)
if endx<self.size_x-2:
coords=[endx+2,endy,endx+1,endy]
checkExit.append(coords)
elif ed==1: #East corridor
if endy>1:
coords=[endx,endy-2,endx,endy-1]
checkExit.append(coords)
if endx<self.size_x-2:
coords=[endx+2,endy,endx+1,endy]
checkExit.append(coords)
if endy<self.size_y-2:
coords=[endx,endy+2,endx,endy+1]
checkExit.append(coords)
elif ed==2: #South corridor
if endx<self.size_x-2:
coords=[endx+2,endy,endx+1,endy]
checkExit.append(coords)
if endy<self.size_y-2:
coords=[endx,endy+2,endx,endy+1]
checkExit.append(coords)
if endx>1:
coords=[endx-2,endy,endx-1,endy]
checkExit.append(coords)
elif ed==3: #West corridor
if endx>1:
coords=[endx-2,endy,endx-1,endy]
checkExit.append(coords)
if endy>1:
coords=[endx,endy-2,endx,endy-1]
checkExit.append(coords)
if endy<self.size_y-2:
coords=[endx,endy+2,endx,endy+1]
checkExit.append(coords)
for xxx,yyy,xxx1,yyy1 in checkExit: #Loop through possible exits
if self.mapArr[yyy][xxx]==0: #If joins to a room
if randrange(100)<psb: #Possibility of linking rooms
self.makePortal(xxx1,yyy1)
def finalJoins(self):
"""Final stage, loops through all the corridors to see if any can be joined to other rooms"""
for x in self.cList:
self.joinCorridor(x[0],x[1],x[2],x[3],10)
# ----------------------------------------------------------------------------
# Main
# ----------------------------------------------------------------------------
startx=20 # map width
starty=10 # map height
themap= dMap()
themap.makeMap(startx,starty,110,50,60)
for y in range(starty):
line = ""
for x in range(startx):
if themap.mapArr[y][x]==0:
line += "."
if themap.mapArr[y][x]==1:
line += " "
if themap.mapArr[y][x]==2:
line += "#"
if themap.mapArr[y][x]==3 or themap.mapArr[y][x]==4 or themap.mapArr[y][x]==5:
line += "="
print line
There are a lot of different methods to create a random dungeon, a common one is using Binary space partitioning (BSP).
Also make sure to take a look at libtcod, which offers also an implementation of that algorithm.

Categories