# Mark Brown Tuition

Physics, Mathematics and Computer

Science Tuition & Resources

# Random numbers, order and choices

Posted on 01-10-18 in Computing

Python has many useful standard libraries - Today we will look at the random module.

Whether it's generating data to test our code or creating a machine player for a simple game, random numbers end up being rather useful. In this article we'll look at three uses of randomness. Random numbers, randomising order and random choices.

## Randomness

Firstly we need to point out that computers do not generate random numbers! Consider the following example

```
from random import seed, randint
repeats = 3
for _ in range(repeats):
seed(1) # arbitrary bad seed
for _ in range(20):
print(randint(1, 20), end=", ")
print("")
```

For me this outputs

>>> 5, 19, 3, 9, 4, 16, 15, 16, 13, 7, 4, 16, 1, 13, 14, 20, 1, 15, 9, 8, 5, 19, 3, 9, 4, 16, 15, 16, 13, 7, 4, 16, 1, 13, 14, 20, 1, 15, 9, 8, 5, 19, 3, 9, 4, 16, 15, 16, 13, 7, 4, 16, 1, 13, 14, 20, 1, 15, 9, 8,

What's important to spot here, is the repetition each time the seed is reset. This is worth bearing in mind if you desire greater randomness than what is essentially a weird sequence. In Python if no seed is specified then an OS-dependent value is taken. In short - bear in mind there are no true random numbers here but they are typically good enough for many cases we come across!

## Random Numbers

To begin let us generate a random number using random(). This will create a random number between 0 and 1.

```
from random import random
value = random()
print(f"The value to two decimal places is {value:.2f}"
```

If you're unsure how the format argument works have a look at my post on formatting.
Note how we import the function *random* from the library **random**.
If we wish to see what the library contains we can use

```
import random
help(random)
```

This will output a lot of helpful text! If we only want help on a specific function we can

```
import random
help(random.random)
```

This will give us the output

>>> Help on built-in function random: random(...) method of random.Random instance random() -> x in the interval [0, 1).

Let's use a different function from random. For example, let's create a random bearing

```
from random import uniform
random_bearing = uniform(0, 360)
print(f"{random_bearing:05.1f} Degrees")
```

This may print out

>>> 004.6 Degrees

Let's say we don't want all numbers, just integers.
For that we can use *randrange*.
The arguments are the same as *range*, so

```
from random import randrange
randrange(10) # random integer between 0 and 10
randrange(0, 100, 25) # chooses randomly from 0,25,50,75
```

Another *similar* function is *randint*.
This includes the end value and does not allow us to perform steps.
For example

```
from random import randint
def sum_of_dice_rolls(n=1, lower=1, upper=6):
"""
Sum of n dice rolls
"""
if n > 0:
return sum([randint(lower, upper) for _ in range(n)])
else:
raise ValueError('Invalid number of dice rolls!')
for n in (1,2,3):
print(f"From {n} rolls you rolled a total of {sum_of_dice_rolls(n)}")
```

may give

>>> From 1 rolls you rolled a total of 5 From 2 rolls you rolled a total of 10 From 3 rolls you rolled a total of 17

## Picking Items Randomly

Let's say we want to pick other things randomly, like say names out of a hat.
For this we can use the *choice* function.

```
from random import choice
names = ('Bob', 'Jerry', 'Mary', 'Francis', 'Jill', 'Barbara')
random_name = choice(names)
print(f"It is {random_name}'s turn!")
```

Will potentially give

>>> It's Barbara's turn!

If we wish to choose multiple cases we can use

```
from random import choices
names = ('Bob', 'Jerry', 'Mary', 'Francis', 'Jill', 'Barbara')
for random_name in choices(names, k=6):
print(f"It is {random_name}'s turn!")
```

this (potentially!) outputs

>>> It is Jerry's turn! It is Bob's turn! It is Mary's turn! It is Jill's turn! It is Jerry's turn! It is Mary's turn!

Here we can see that some names are repeated.
This might be okay for our use case but it won't be helpful if we wish to pick everyone just once!
Therefore we can use the *sample* function.
This will pick *k* options as before but won't repeat.

```
from random import sample
names = ('Bob', 'Jerry', 'Mary', 'Francis', 'Jill', 'Barbara')
for random_name in sample(names, k=6):
print(f"It is {random_name}'s turn!")
```

For further reading, go to the Random reference page. For the remaining part of this article, we will out list a few example uses of the random library.

## A Random Password

One common example is to generate random strings. Here we import four groups of symbols and we will randomly select from these. We will randomly choose a group and then randomly choose a symbol from that group. Here is our code.

```
from string import ascii_lowercase, ascii_uppercase, punctuation, digits
from random import choice
options = (ascii_lowercase, ascii_uppercase, punctuation, digits)
length = 12
new_password = ''
while len(new_password) < length:
new_password += choice(choice(options))
print(new_password)
```

will produce something like

>>> 3}z(vvmeO~L3

**extension**

- Alter the above code to select a specific number of each kind of characters. Eg 3 uppercase, 3 lowercase, 2 numbers and 2 punctuation.
- Alter the code to produce passwords based on a pattern. Eg nu3p5* would mean number, uppercase, 3 punctation and 5 anything.

## Turtle - Random Motion

Turtle is a great little tool for using Python to move a virtual turtle about. Here's a couple of simple programs!

```
import turtle
from random import choices
dim = 100
turtle.setworldcoordinates(-dim, -dim, dim, dim)
bob = turtle.Turtle()
while True:
x,y = choices(range(-dim, dim), k=2)
bob.goto(x,y)
turtle.exitonclick()
```

will produce something like

Here we have generated random coordinates for the turtle to move to each step. Another example!

```
import turtle
from random import random
turtle.speed(10)
prob = 0.5
step = 10
angle = 60
while True:
turtle.forward(step)
if random() > prob:
turtle.left(angle)
else:
turtle.right(angle)
```

produces

Here we have used random() to ensure that we turn left the same number of times as turning right. In this way we produce a randomised honeycomb pattern.

## Rock Paper Scissors

I'll discuss implementations of this game in further detail in another post, for now let's look at getting input. We need methods of getting player and computer choices for the game. Here's one way to this

```
from random import choice
moves = ('rock', 'paper', 'scissors')
def computer_choice():
global moves
return choice(moves)
def player_choice():
global moves
while True:
print("Valid moves are", *moves, sep="\n", end="")
player_move = input("Please enter your move >>").lower()
if player_move in moves:
return player_move
else:
print(f"{player_move} is an invalid option, try again!")
```

Here there a few things to note.
Firstly see how we have used the *choice* function to select a random move for the computer.
Secondly we have kept *moves* outside both functions.
This is so if we wish to expand the game later, we only have one variable to change.
Finally note that we have written the *player_choice()* function to prevent an invalid option being presented!

## Battleships

As a final demonstration, I include a crude example of a computer playing batteships. In battleships we want the computer to find all the ships we have hidden.

Ideally we want the computer to visit every tile just once. Furthermore we want the computer to guess adjacent tiles if the computer does in fact get a hit

Here's a gif showing my implementation

Here red squares indicate misses and green indicate hits.

**Extensions**

- Get the computer to focus on a particular axis (horizontal or vertical) once its clear what direction the ship is facing.
- Prevent the code from generating ships that overlap.
- Add a human player!

```
import turtle
from random import shuffle, choice, random
dim = 250 # dimension of our screen
L = 25 # size of a box
turtle.setworldcoordinates(-dim, -dim, dim, dim)
def draw_square(tl, x, y, L, fill=False, col='red'):
"""
Draws filled square at x,y location with side of L
if fill is True the box is filled with colour 'col'
"""
tl.penup()
tl.goto(x, y)
tl.pendown()
if fill:
tl.color("black", col)
tl.begin_fill()
for side in range(4):
tl.forward(L)
tl.left(90)
if fill:
tl.end_fill()
tl.penup()
tl.goto(x,y)
bob = turtle.Turtle()
bob.speed(10)
## Generate all possible box locations
locations = []
for x in range(-dim, dim, L):
for y in range(-dim, dim, L):
locations.append((x,y))
shuffle(locations) # Computer will visit all locations randomly
def new_ship(length=3):
"""
Generate location of ships
Code checks if both front and end of randomly placed ship is on screen
if so it returns this
TODO : does not currently check if intersection with existing ships
"""
ship = []
global locations
x,y = choice(locations) # Front of ship!
while len(ship) < length:
step = choice((-1, 1))
if random() > 0.5: # randomly choice horizontal or vertical ship
end_x = x + length * step * L
if (end_x, y) in locations: # Only allow valid end position
for loc in range(x, end_x + 1, step*L):
ship.append((loc, y))
else:
end_y = y + length * step * L
if (x, end_y) in locations:
for loc in range(y, end_y + 1, step*L):
ship.append((x, loc))
return ship
ships = []
for j in range(2, 8):
ships += new_ship(j)
while locations:
x,y = locations.pop(0) # get next guess from front of queue
if (x,y) in ships:
col = 'green'
to_shift = []
for index, (new_x, new_y) in enumerate(locations):
if new_x in range(x-L, x+2*L, L) and new_y in range(y-L, y+2*L, L):
to_shift.append(index)
# Places all adjacent tiles at the front of the queue
for index in to_shift:
locations.insert(0, locations.pop(index))
else:
col = 'red'
draw_square(bob, x, y, L, True, col)
turtle.exitonclick()
```

Enjoy!