Creating a checkers board in python - python-3.x

It's currently between semesters and I wanted to do a second "final project" for nothing other than practice. I decided to make a checkers game in python.
I'm using the graphics library to do this in. The way I want it to work is to build the board using squares and I want to label each of them as their own entity. I don't know if this can work*** but I want to create a dictionary to store each rectangle and have a value to show if there is a piece there or not. So far this is what I have to build the board....
def board(win,coordSys):
xRange = 'ABCDEFGH'
X = 0
for x in range(1,9):
for y in range(1,9):
Rec = Rectangle(Point(x,y),Point(x+1,y+1))
if x%2 == 0 and y%2 == 0:
Rec.setFill('red')
coordSys[xRange[X]+str(y)] = 0
elif x%2 != 0 and y%2 != 0 :
Rec.setFill('red')
coordSys[xRange[X]+str(y)] = 0
else:
Rec.setFill('grey')
Rec.draw(win)
X+=1
I'm relatively new at Python so i'm trying to do it with the tools that I'm already aware of for now. My question is, how can I store individual rectangles using a A1-H8 format in a dictionary so that I can have {A1:0 etc..} and I'm able to check the value of A1 to see if anything has been altered in A1's rectangle?

If what you want is a dictionary whose values are the rectangles in question, it sounds like what you are looking for is something like
for x in range(1, 9):
for y in range(1, 9):
rec = Rectangle(Point(x, y), Point(x+1, y+1))
coordSys[xRange[x-1] + str(y)] = rec
rec.draw(win)

Related

Remove elements while in a for loop

I have a simpel Card Game, which I am currently working on for my thesis.
The Rules are simpel. You have a deck of 52 Cards, from 1 to 10 and jack, queen, knight.
You draw a card from your Deck. If its a Number it gets added to your Account. If you draw a jack, queen or knight, your account gets reset to 0. After every draw you can decide if you want to draw again or stop.
For this game, i programmed a code with the help of this site.
It should give the probability, that you draw exactly "target".
So for example, the probability to draw, so that you have 1 Point in your account,
is 4/52, since you have four 1´s. The Programm does give me exactly this value.
But. The probabiltity, that you have exactly 2 points in your account is
4/52 + 4/52*3/51. You can either draw a 2 with prob of 4/52 or a 1 and another 1 with prob 4/52*3/51.
Here the code messes up. It calculates the probability to have exactly 2 points in your account as
4/52 + 4/52*4/51 and i dont get why?
Can anyone help me?
import collections
import numpy as np
def probability(n, s, target):
prev = {0: 1} # previous roll is 0 for first time
for q in range(n):
cur = collections.defaultdict(int) # current probability
for r, times in prev.items():
cards = [card for card in range(1, 11)] * 4
for i in cards[:]:
cards.remove(i)
# if r occurred `times` times in the last iteration then
# r+i have `times` more possibilities for the current iteration.
cur[r + i] += times
prev = cur # use this for the next iteration
return (cur[t]*np.math.factorial(s-n)) / (np.math.factorial(s))
if __name__ == '__main__':
s = 52
for target in range(1, 151):
prob = 0
for n in range(1, 52):
prob += probability(n, s, target)
print(prob)
EDIT: I am fairly sure, that the line
for i in [i for i in cards]:
is the problem. Since cards.remove(i) removes the drawn card, but i doesnt care and can draw it anyway.
EDIT 2: Still searching. I tried the suggestions in this two qestions
How to remove list elements in a for loop in Python?
and
How to remove items from a list while iterating?
Nothing worked so far as it should.
I'm assuming with probability(n, s, target) you want to calculate the probability if you draw exactly n out of s cards that the sum of values is exactly target.
Then you will have a problem with n>=2. If I understand this right, for every iteration in the loop
for q in range(n):
you save in cur[sum] the number of ways to reach sum after drawing one card (p=0), two cards (p=1) and so on. But when you set p=1 you don't "remember" which card you have already drawn as you set
cards = [i for i in range(1, 11)] * 4
afterwards. So if you have drawn a "1" first (four possibilities) you have again still four "1"s you can draw out of your deck, which will give you your 4/52*4/51.
As a side note:
Shouldn't there be some kind of check if i==11 since that should reset your account?
I have solved it. After like a 4 Days.
This is the Code:
import numpy as np
def probability(cards, target, with_replacement = False):
x = 0 if with_replacement else 1
def _a(idx, l, r, t):
if t == sum(l):
r.append(l)
elif t < sum(l):
return
for u in range(idx, len(cards)):
_a(u + x, l + [cards[u]], r, t)
return r
return _a(0, [], [], target)
if __name__ == '__main__':
s = 52 # amount of cards in your deck
cards = [c for c in range(1, 11)] * 4
prob = 0
for target in range(1, 151): # run till 150 points
prob = probability(cards, target, with_replacement = False)
percentage = 0
for i in range(len(prob)):
percentage += np.math.factorial(len(prob[i])) * np.math.factorial(s-len(prob[i]))/(np.math.factorial(s))
print(percentage)
This Code is the Solution to my Question. Therefore this Thread can be closed.
For those who want to know, what it does as a tl;dr version.
You have a List (in this case Cards). The Code gives you every possible Combination of Elements in the List such as the Sum over the elements equals the target Value. Furthermore it also gives the Probability in the above mentioned Cardgame to draw a specific Value. The above mentioned game is basically the pig dice game but with cards.

Making a randomly generated 2d map in python is taking too long to process all of the map generation

import random
l = "lava"
d = "dessert"
f = "forest"
v = "village"
s = "sect"
w = "water"
c = "city"
m = "mountains"
p = "plains"
t = "swamp"
map_list = [l,d,f,v,s,w,c,m,p,t]
map = []
for i in range(50):
map.append([])
def rdm_map(x):
for i in range(50):
map[x].append(random.choice(map_list))
def map_create():
x = 0
while x <= len(map):
rdm_map(x)
x + 1
map_create()
print(map[2][1])
I'm not getting anything for output not even an error code.I'm trying to create a randomly generated game map of descent size but when i went to run it nothing happened i'm thinking since my computer isn't that great its just taking way to long to process but i just wanted to post it on here to double check. If that is the issue is there a way to lessen the load without lessening the map size?
You have the following bugs:
Inside the map_create you must change x + 1 to x += 1. For this reason your script runs for ever.
After that you should change the while x <= len(map): to while x < len(map):. If you keep the previous, you will get a Index Error.
In any case, your code can be further improved. Please try to read some pages of the tutorial first.

Some basic python coding bug I don't know how to solve

I've encountered some problems when I was trying simulate school's math question,I've test the inner loop independently and the result is what I expect.I have no idea where is the problem and where can I get the resolution.It should be a simple bug.
This is the question:
There is a bag which includes three red balls ,four white balls and five black balls.Take one ball each time.Then what is the probability when red balls were the first color being collected.
And This is my code:(All annotations were not added in my code)
import random as rd
y = 1000 *//total try*
succ = 0 *//success times*
orgbg = ['r','r','r','w','w','w','w','b','b','b','b','b'] *//original bag for each loop initialization*
while (y >= 0):
redball = 0
blackball = 0
whiteball = 0
newbg = orgbg *//every bag for a single try*
while (redball < 3 and whiteball < 4 and blackball < 5):
tknum = rd.randrange(0,len(newbg),1)
tkball = newbg[tknum]
if (tkball == 'r'):
redball = redball + 1
elif (tkball =='w'):
whiteball = whiteball + 1
else:
blackball = blackball + 1
del newbg[tknum]
if (redball == 3):
succ = succ + 1
y = y - 1
print (succ)
This is what the error report says:
ValueError: empty range for randrange() (0,0, 0)
When I turn the code
tknum = rd.randrange(0,len(newbg),1)
into
tknum = rd.randrange(5,len(newbg),1)
The error reoprt says:
ValueError: empty range for randrange() (5,5, 0)
I guess it is the initialization in the outer loop newbg = orgbg doesn't work out,but how can that happen?
Sorry for giving such a length question ,I'm a beginner and this is the first time I ask question on StackOverFlow,you can also give me some suggestion on my code style or method and the way of asking question,next time I will be better,hope you don't mind.
I think that your problem is indeed linked with the initialization in the outer loop newbg = orgbg. To correct your code, you should modify this line with
newbg = deepcopy(orgbg)
and import the corresponding module at the start of your code:
from copy import deepcopy
The explanation of the bug is a bit complicated and is linked with the way that Python handles the memory when copying a list. In fact, there is two possibility for this: a shallow or a deep copy. Here, you made a shallow copy when a deep copy would have been necessary. It is better explained here: https://www.python-course.eu/deep_copy.php or What exactly is the difference between shallow copy, deepcopy and normal assignment operation?

Two trigonometry-based turtle codes not giving similar output

This is my first time posting a question.
I'm having trouble creating a code involving cosine, and I am not recieving the desired outcome. What is even more confusing is the fact that the two codes should be creating similar images (Explained later). Any ideas?
In the code below, these variables represent:
Y is a counter, making sure that the code only runs until the specified amount of radi is produced.
W is the colour randomly generated.
Z is the angle turn from 0 degrees. (The turtle's angle resets due to turtle.home).
Adjacent is the smallest length from centre to a line.
Radi is the amount of lines protruding from the centre.
def Triangle(Radi, Adjacent):
y = 0
if (Radi) % 1 == 0:
while (Radi) > y:
y = y + 1
w = randhex()
z = 360/(Radi)*y
turtle.left(z+30)
turtle.color(w)
if z > 300:
turtle.forward(Adjacent/math.cos(math.pi*(60 - (z - 300))/180))
elif z > 240:
turtle.forward(Adjacent/math.cos(math.pi*(z - 240)/180))
elif z > 180:
turtle.forward(Adjacent/math.cos(math.pi*(60 - (z - 180))/180))
elif z > 120:
turtle.forward(Adjacent/math.cos(math.pi*(z - 120)/180))
elif z > 60:
turtle.forward(Adjacent/math.cos(math.pi*(60 - (z - 60))/180))
else:
turtle.forward(Adjacent/math.cos(math.pi*z/180))
turtle.home()
Above is my first code which appears to work, giving these results when Triangle(100,180) is entered (Please note that randhex() is a custom function that generates random colours).
Triangle(100,180) results.
My apologies if my variable naming creativity is annoying.
In this code, counter represents 'y' and angle represents 'z' from the previous code
Here is my second code:
def Polygon(Radi, Adjacent, Sides):
counter = 0
if Sides % 1 != 0 or Sides == 2 or Sides <= 0:
print ("INVALID")
elif Sides == 1:
while Radi > counter:
counter = counter + 1
colour = randhex()
turn = 360/Radi*counter
turtle.left(turn)
turtle.color(colour)
turtle.forward(Adjacent)
turtle.home()
else:
while Radi > counter:
counter = counter + 1
colour = randhex()
turn = 360/Radi*counter
turtle.left(turn)
turtle.color(colour)
segment = str(counter/Radi*Sides*2)
position = segment.index('.')
test = int(segment[:position])
if test % 2 == 1:
length = Adjacent/math.cos(math.pi*(turn - (360 - 360/Sides*((test+1)/2)))/180)
turtle.forward(length)
else:
length = Adjacent/math.cos(math.pi*(180/Sides - (turn - (360 - 180/Sides*(test+1))))/180)
turtle.forward(length)
turtle.home()
Above is my second code, being the one I'm struggling with. Once again, apologies for my variable names being annoying and some of the maths not simplified. I find it easier to see how my ideas make sense when I leave them as they are. Below are my results for my second code after entering Polygon(180,100,3).
Polygon(180,100,3) results.
As you can see, it didn't go quite how I was planning.
I should also note that I tried substituting the numbers into the codes where one of the codes were giving a different line length. Sometimes they even went in an opposite direction (because the number came out negative). I did this on the Google calculator, but it seemed that both codes would give the same answer, but they corresponded to what the second code was outputing, not the first.
If you want me to explain anything leave a comment.
But if it turns out that my code is wrong (Which I believe), could you please point me to what I need to do instead.
I'd appreciate the help.
Your code is too complicated to debug. The unhelpful variable names, the lack of comments and excessively long equations make it hard to read.
If we consider the equation suggested in this answer to Is there an equation to describe regular polygons? then your original triangle code simplifies to:
import math
import turtle
def randhex():
""" substitute random color generator here """
return 'red'
def triangle(radii, adjacent):
if radii % 1 != 0: # only whole numbers (int or float) allowed
return
counter = 1
while counter <= radii:
angle = counter * (2 * math.pi / radii)
turtle.setheading(angle)
colour = randhex()
turtle.color(colour)
radius = adjacent / math.cos(angle % (math.pi / 1.5) - math.pi / 3)
turtle.forward(radius)
turtle.backward(radius)
counter += 1
turtle.radians() # avoid individual conversions, switch to radians
triangle(100, 180)
turtle.exitonclick()
And the general polygon solution can be achieved with just a few changes:
import math
import turtle
def randhex():
""" substitute random color generator here """
return 'red'
def polygon(radii, adjacent, sides):
if radii % 1 != 0: # only whole numbers (int or float) allowed
return
if sides % 1 != 0 or sides == 2 or sides <= 0:
return
counter = 1
while counter <= radii:
angle = counter * (2 * math.pi / radii)
turtle.setheading(angle)
colour = randhex()
turtle.color(colour)
if sides == 1: # special case, a circle
radius = adjacent
else:
radius = adjacent / math.cos(angle % (math.pi / (sides / 2)) - math.pi / sides)
turtle.forward(radius)
turtle.backward(radius)
counter += 1
turtle.radians() # avoid individual conversions, switch to radians
polygon(100, 180, 3)
turtle.exitonclick()
With polygon(100, 90, 5) looking like:

How to improve the performance of Cellular Automata

I've made a simple terrain generator, but it takes an excessive amount of time to generate anything bigger than 50x50. Is there anything I can do to optimise the code so that I can generate larger things? I know that things such as pygame or numpy might be better for doing this, but at my school they wont install those, so this is what I have to work with.
Here's the relevant code:
def InitMap(self):
aliveCells = []
for x in range(self.width):
for y in range(self.height):
if random.random() < self.aliveChance:
aliveCells.append(self.FindInGrid(x,y))
return aliveCells
def GenerateMap(self):
aliveCells = self.InitMap()
shallowCells=[]
self.count = 1
for i in range(self.steps):
aliveCells = self.DoGenStep(aliveCells)
for i in aliveCells:
self.canvas.itemconfig(i,fill="green")
for i in aliveCells:
for j in self.FindNeighbours(i):
if j not in aliveCells: self.canvas.itemconfig(i,fill="#0000FF")
def DoGenStep(self,oldAliveCells):
newAliveCells = []
for allCells in self.pos:
for cell in allCells:
self.root.title(str(round((self.count/(self.height*self.width)*100)/self.steps))+"%")
self.count += 1
aliveNeighbours = 0
for i in self.FindNeighbours(cell):
if i in oldAliveCells: aliveNeighbours += 1
if cell in oldAliveCells:
if aliveNeighbours < self.deathLimit:
pass
else:
newAliveCells.append(cell)
else:
if aliveNeighbours > self.birthLimit:
newAliveCells.append(cell)
return newAliveCells
def FindNeighbours(self,cell):
cellCoords = self.GetCoords(cell)
neighbours = []
for xMod in [-1,0,1]:
x = xMod+cellCoords[0]
for yMod in [-1,0,1]:
y = yMod+cellCoords[1]
if x < 0 or x >= self.width: pass
elif y < 0 or y >= self.height: pass
elif xMod == 0 and yMod == 0: pass
else: neighbours.append(self.FindInGrid(x,y))
return neighbours
NB: You didn't add the method "FindInGrid", so I'm making some assumptions. Please correct me if I'm wrong.
One thing which would help immensely for larger maps, and also when at high densities, is not to store just the alive cells, but the entire grid. By storing the alive cells, you make the behavior of your program in the order O( (x*y)^2), since you have to iterate over all alive cells for each alive cell. If you would store the entire grid, this would not be necessary, and the calculation can be performed with a time complexity linear to the surface of your grid, rather than a quadratic one.
Additional point:
self.root.title(str(round((self.count/(self.height*self.width)*100)/self.steps))+"%")
That's a string operation, which makes it relatively expensive. Are you sure you need to do this after each and every update of a single cell?

Resources