I want to make a function that I give it a number and the function returns a spiral from 1 to that number(in 2 dimensional array). For example if I give the number 25 to the function it will return something like this:
I tried different ways but nothing worked out. I just cant figure it out.
Hope I explained myself properly.
turtle. bye() , aka turtle. Screen(). bye() , closes a turtle graphics window.
Mostly the issue here is one of enumerating coordinates - match numbers to coordinates, then print it out however you want.
Start by noticing the two fundamental patterns:
So with those rules, write a generator that yields number, coordinates
tuples.
It's clearest if you set up some helper functions first; I'll be extra verbose:
def move_right(x,y):
return x+1, y
def move_down(x,y):
return x,y-1
def move_left(x,y):
return x-1,y
def move_up(x,y):
return x,y+1
moves = [move_right, move_down, move_left, move_up]
Easy enough, now the generator:
def gen_points(end):
from itertools import cycle
_moves = cycle(moves)
n = 1
pos = 0,0
times_to_move = 1
yield n,pos
while True:
for _ in range(2):
move = next(_moves)
for _ in range(times_to_move):
if n >= end:
return
pos = move(*pos)
n+=1
yield n,pos
times_to_move+=1
demo:
list(gen_points(25))
Out[59]:
[(1, (0, 0)),
(2, (1, 0)),
(3, (1, -1)),
(4, (0, -1)),
(5, (-1, -1)),
(6, (-1, 0)),
(7, (-1, 1)),
(8, (0, 1)),
(9, (1, 1)),
(10, (2, 1)),
(11, (2, 0)),
(12, (2, -1)),
(13, (2, -2)),
(14, (1, -2)),
(15, (0, -2)),
(16, (-1, -2)),
(17, (-2, -2)),
(18, (-2, -1)),
(19, (-2, 0)),
(20, (-2, 1)),
(21, (-2, 2)),
(22, (-1, 2)),
(23, (0, 2)),
(24, (1, 2)),
(25, (2, 2))]
Here's a diagram to help you think about the problem:
You can think of this as repeatedly adding to an NxN square to make an (N+1)x(N+1) square:
if N is odd:
move right one step
move down N steps
move left N steps
else:
move left one step
move up N steps
move right N steps
and at each step you write a number to the current location.
As @Milan points out, you may not always want to complete the current shell (ie if you only want to count to 23). The easiest way to do this is to make a generator function which produces an endless stream of steps, then consume only as many steps as you need:
from itertools import count
def steps_from_center():
for n in count(start=1):
if n % 2:
yield RIGHT
for i in range(n):
yield DOWN
for i in range(n):
yield LEFT
else:
yield LEFT
for i in range(n):
yield UP
for i in range(n):
yield RIGHT
Before this can be used we have to decide how to store the values and, based on that, how to represent UP
, DOWN
, LEFT
, and RIGHT
.
The simplest storage is a 2d array, or in Python terms a list of lists. The outer list will hold rows of output and the inner lists will each hold cells within a row, and each cell can be addressed as my_array[y][x]
with x increasing from left to right and y increasing from the top downward (this matches the order in which we expect to print output).
This allows us to define our directions:
from collections import namedtuple
Step = namedtuple("Step", ["dx", "dy"])
RIGHT = Step( 1, 0)
DOWN = Step( 0, 1)
LEFT = Step(-1, 0)
UP = Step( 0, -1)
Before we can allocate storage, we need to know how big an array we need:
from math import ceil, floor, log10, sqrt
max_i = int(input("What number do you want to display up to? "))
# how big does the square have to be?
max_n = int(ceil(sqrt(max_i)))
# here is our initialized data structure
square = [[EMPTY] * max_n for _ in range(max_n)]
# and we start by placing a 1 in the center:
x = y = max_n // 2
square[y][x] = output(1)
I have added two extra pieces here: in order for the output to be tidy, every item should print the same width. output()
is a function that takes a value and returns a string of the correct width, and EMPTY
is a string of spaces of that width:
# how many digits in the largest number?
max_i_width = int(floor(log10(max_i))) + 1
# custom output formatter - make every item the same width
def output(item, format_string="{{:>{}}}".format(max_i_width)):
return format_string.format(item)
EMPTY = output("")
Now the pieces are in place, and we can generate the spiral:
for i, step in enumerate(steps_from_center(), start=2):
if i > max_i:
break
else:
x += step.dx
y += step.dy
square[y][x] = output(i)
and print it out:
print("\n".join(" ".join(row) for row in square))
and it runs like:
What number do you want to display up to? 79
73 74 75 76 77 78 79
72 43 44 45 46 47 48 49 50
71 42 21 22 23 24 25 26 51
70 41 20 7 8 9 10 27 52
69 40 19 6 1 2 11 28 53
68 39 18 5 4 3 12 29 54
67 38 17 16 15 14 13 30 55
66 37 36 35 34 33 32 31 56
65 64 63 62 61 60 59 58 57
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With