Trouble understanding modulus on a negative in 'Conways Game of Life' - python-3.x

I'm going over the book 'automate the boring stuff with python' and cannot understanding a simple expression with the % operator. The expression is leftCoord = (x - 1) % WIDTH which on the first iteration of the loop evaluates to (0 - 1) % 60. In my mind the % operator should evaluate to the remainder of a division. Why does it evaluate to 9?
This is the part of the program that precedes the expression in question:
import random,time,copy
WIDTH = 60
HEIGHT = 20
# Create a list of list for the cells:
nextCells = []
for x in range(WIDTH):
column = [] # Create a new column.
for y in range(HEIGHT):
if random.randint(0,1) == 0:
column.append('#') # Add a living cell.
else:
column.append(' ') # Add a dead cell.
nextCells.append(column) # nextCells is a list of column lists.
while True: # Main program loop.
print('\n\n\n\n\n') # Separate each step with newlines.
currentCells = copy.deepcopy(nextCells)
# Print currentCells on the screen:
for y in range(HEIGHT):
for x in range(WIDTH):
print(currentCells[x][y], end='') # Print the # or space.
print() # Print a newline at the end of the row.
# Calculate the next step's cells based on current step's cells:
for x in range(WIDTH):
for y in range(HEIGHT):
# Get neighboring coordinates:
# % WIDTH ensures leftCoord is always between 0 and WIDTH -1
leftCoord = (x - 1) % WIDTH
rightCoord = (x + 1) % WIDTH
aboveCoord = (y - 1) % HEIGHT
belowCoord = (y + 1) % HEIGHT

For the sake of example, let's assume that you're using a table of 10x10.
The % operator isn't so intuitive when the first number is smaller than the second. Try going into the interactive python shell and running 4 % 10. Try 8 % 10. Notice how you always get the same number back? That's because the answer to the division is 0... with your whole number being left over as remainder. For most numbers in the table, the modulus doesn't do anything at all.
Now try -1 % 10 (simulating what this would do for the top row). It gives you 9, indicating the bottom row. If you run 10 % 10 (simulating the bottom row), it gives you 0, indicating the top row. Effectively, this makes the table "wrap"... the cells in the top row affect the bottom and vice versa. It also wraps around the sides.
Hope this helps!

Related

IndexError file: list index out of range( arithmetic mean)

A file is given, there are 4 columns in it, (index, age, height (inches), weight (pounds)) the file length is 20000+ the task is the following, i need to add the weight and height, and get the average value (I also tried to convert inches and pounds to cm and kg) I added the column and divided by the number, but I get this error mean + = float (line [2]) IndexError file: list index out of range
def data_scv():
with open('data.csv') as csv_file:
csv_reader = csv.reader(csv_file)
next(csv_reader)
mean = 0
n = 0
for line in csv_reader:
mean += float(line[2])
n += 1
ave_height = str((mean / n) * 2.54)
for line in csv_reader:
mean += float(line[3])
n += 1
ave_weight = str((mean / n) / 2.205)
return f'Average Height(Centimeters) is - {ave_height} <br> Average Height(Kilograms) is - {ave_weight}'
If I understand this correctly (python isn't exactly my expertise) your issue is that you're trying to start your index in the position 1. Python's first position is position 0.

2D List - Python - weird loop behaviour (does not stop when told to)

My having troubles understanding why is my code not working like I think it should.
This function is supposed to fill up a 2 dimensional list with 1 instead of 0 given some parameters:
x = to tell it where to start to fill up on the x axis
y = to tell it where to start to fill up on the y axis
length = length of the rectangle
width = width of the rectangle
rotation = boolean to tell if the rectangle should be drawn
vertically (if True) or horizontally (if False)
So I call drawLoop() that will call drawVertical() that calls drawHorizon() afterward
This function is aimed at receiving multiple rectangle, but my problem lies when the first is added.
Here is the code:
# Create a 2D list of 120 * 60
thisBox = [["0"] * 120] * 60
# Filler function
def drawLoop(x, y, length, width, rotation):
def drawHorizon(x, y, length, width, rotation, row):
drawIndexH = 0
while drawIndexH < len(row):
if rotation == False and drawIndexH >= x and drawIndexH < x + length:
row[drawIndexH] = "1"
drawIndexH += 1
elif rotation == True and drawIndexH >= x and drawIndexH < x + width:
row[drawIndexH] = "1"
drawIndexH += 1
else:
return
def drawVertical(x, y, length, width, rotation):
drawIndexV = 0
while drawIndexV < len(thisBox):
if rotation == False and drawIndexV >= y and drawIndexV < y + width:
drawHorizon(x, y, length, width, rotation, thisBox[drawIndexV])
drawIndexV += 1
elif rotation == True and drawIndexV >= y and drawIndexV < y + length:
drawHorizon(x, y, length, width, rotation, thisBox[drawIndexV])
drawIndexV += 1
else:
drawIndexV += 1
# Launch vertical drawing
drawVertical(x, y, length, width, rotation)
# Launch main function
drawLoop(0, 0, 70, 50, False)
As of now, the 120 * 60 space is empty, so by calling the main function with drawloop(0, 0, 70, 50, False)on the last line, I'm supposed to see a 70 * 50 rectangle drawn at the position (0, 0). So that out of the 7200 0 (120 * 60) I should see only 3700 left (7200 - (70 * 50))
So the function is divided into 2 other functions: drawVertical(x, y, length, width, rotation) that will draw vertically and drawHorizon(x, y, length, width, rotation, row) that will draw horizontally first.
But somehow, at the first iteration of drawVertical(...), all the rows are being filled up in one iteration and do not stop at the exit condition: it should stop at y = 50 (the width) when if rotation == False and drawIndexV >= y and drawIndexV < y + width: of drawVertical(...) because drawIndexV < y + width should stop at 50. But it does not and I have no clue why.
Even if I tell drawVertical(...) to stop the loop at the first iteration with while drawIndexV < 2:, all the rows are being filled up.
So horizontally I have the expected result, but I never have it vertically. Can anybody spot my mistake? Many thanks in advance!
Ben.
The problem is indeed that
thisBox = [["0"] * 120] * 60
creates a list with 60 times the same element, a list with 120 "0", as the following code snippet shows:
for r in range(60):
print(id(thisBox[r]))
for which a sample output is:
140706579889408
140706579889408
140706579889408
140706579889408
140706579889408
140706579889408
...
Updating any element on any row will update the same element on every row, since every row is the same unique list object.
To avoid the issue, one needs to ensure that each of the enclosed lists (the 60 lists, each of which contains 120 "0") is a separate list object, distinct from all the other enclosed lists, i.e., has its own id.
If familiar with numpy (numpy.zeros), and depending on the exact requirements, resorting to numpy arrays could be a solution. If using numpy is an instance of "shooting a bird with a cannonball", one alternative is using list comprehensions to initialise the list:
thisBox = [["0" for c in range(120)] for r in range(60)]
Running the same code as before confirms that each list of 120 "0" now has its own id, i.e., each row is a separate list:
for r in range(60):
print(id(thisBox[r]))
140185522518784
140185522481600
140185522519680
140185522482560
140185503364672
...
(Would have added the solution described above, using list comprehensions, as a comment, but was barred from doing so because of insufficient "reputation" points)
I have found my problem (Merci Philippe!!!)
When I do this:
# Create a 2D list of 120 * 60
thisBox = [["0"] * 120] * 60
I'm actually creating a list of the same element 60 times. They all have the same memory allocation. So if I modify one, I modify them all. That's why one iteration of drawVertical(...) modified the whole 60 rows.

Problem in the function of my program code python

I tried to make a program to do the below things but apparently, the function doesn't work. I want my function to take two or more arguments and give me the average and median and the maximum number of those arguments.
example input:
calc([2, 20])
example output : (11.0, 11.0, 20)
def calc():
total = 0
calc = sorted(calc)
for x in range(len(calc)):
total += int(calc[x])
average = total / len(calc)
sorted(calc)
maximum = calc[len(calc) - 1]
if len(calc) % 2 != 0:
median = calc[(len(calc) // 2) + 1]
else:
median = (float(calc[(len(calc) // 2) - 1]) + float(calc[(len(calc) // 2)])) / 2
return (average, median, maximum)
There are some things I'm going to fix as I go since I can't help myself.
First, you main problem is arguments.
If you hand a function arguments
calc([2, 20])
It needs to accept arguments.
def calc(some_argument):
This will fix your main problem but another thing is you shouldn't have identical names for your variables.
calc is your function name so it should not also be the name of your list within your function.
# changed the arg name to lst
def calc(lst):
lst = sorted(lst)
# I'm going to just set these as variables since
# you're doing the calculations more than once
# it adds a lot of noise to your lines
size = len(lst)
mid = size // 2
total = 0
# in python we can just iterate over a list directly
# without indexing into it
# and python will unpack the variable into x
for x in lst:
total += int(x)
average = total / size
# we can get the last element in a list like so
maximum = lst[-1]
if size % 2 != 0:
# this was a logical error
# the actual element you want is mid
# since indexes start at 0
median = lst[mid]
else:
# here there is no reason to explicity cast to float
# since python division does that automatically
median = (lst[mid - 1] + lst[mid]) / 2
return (average, median, maximum)
print(calc([11.0, 11.0, 20]))
Output:
(14.0, 11.0, 20)
Because you are passing arguments into a function that doesn't accept any, you are getting an error. You could fix this just by making the first line of your program:
def calc(calc):
But it would be better to accept inputs into your function as something like "mylist". To do so you would just have to change your function like so:
def calc(mylist):
calc=sorted(mylist)

How do I end a loop on Turtle using the following code?

import turtle
flower = turtle.Turtle()
flower.color("black", "red")
def draw_flower(d, n, p):
for num in range(n):
flower.forward(d)
flower.lt(180 - (180 * (n-2))/n)
for tilt in range(p):
flower.lt(360/p)
draw_flower(d, n, p)
draw_flower(50, 8, 4)
turtle.done()
d is the length of each side, n is the number of total sides, p is the number of times I want it to repeat. The first loop will draw a polygon. I want to repeat that for p times until it shows something like a flower, and I want to break it out.
I know there are other coding ways to make it such that it does not loop. But based on the above code, how do I break out of it when it draws exactly the number p that I want it to draw?

Infinite loops for turtle listen

I am running into an infinite loop in this code. It should break out if you click in the desired range, however it goes into an infinite loop displaying the current position of the turtle in the row and column format.
def wait_for_click():
turt.penup()
wn.onclick(turt.goto)
wn.listen()
pos = [-1,-1]
row = -1
column = -1
while row > 8 or row < 0 or column > 8 or column < 0:
row = ((turt.ycor()-turt.ycor()%75)+75)/75 + 4
column = ((turt.xcor()-turt.xcor()%75)+75)/75 + 4
pos[0] = row
pos[1] = column
print(pos)
I think your basic approach is wrong: don't loop waiting for the turtle to show up somewhere interesting, instead use the click handler to test where the turtle showed up:
from math import ceil
from turtle import Turtle, Screen
CELLS = 8
CELL_SIZE = 75
STAMP_SIZE = 20
def click_handler(x, y):
screen.onclick(None)
yertle.goto(x, y)
if 0 < x < CELLS and 0 < y < CELLS:
position = [ceil(x), ceil(y)]
print(position)
screen.onclick(click_handler)
screen = Screen()
screen.setup(CELL_SIZE * (CELLS + 2), CELL_SIZE * (CELLS + 2))
screen.setworldcoordinates(-1, -1, CELLS + 1, CELLS + 1)
screen.onclick(click_handler)
marker = Turtle(shape="square")
marker.penup()
marker.turtlesize(CELL_SIZE / STAMP_SIZE)
marker.color("gray", "white")
for x in range(0, CELLS):
for y in range(0, CELLS):
marker.goto(x + 0.5, y + 0.5)
marker.stamp()
marker.color(*marker.color()[::-1])
marker.color(*marker.color()[::-1])
yertle = Turtle(shape="circle")
yertle.speed("fastest")
yertle.penup()
screen.mainloop()
I've thrown in code to show the grid so you can see that where you click matches the printed output. I used setworldcoordinates() to simplify the problem with the side effect that I've given you a larger border.
The lower left cell is [1, 1] and the upper right is [8, 8] -- you may want to do some math to switch these around.

Resources