Skip to content

Commit 44ef692

Browse files
committed
Released version of ballgame.py to the wild, the book is now ready!
1 parent dd28e84 commit 44ef692

File tree

1 file changed

+209
-1
lines changed

1 file changed

+209
-1
lines changed

Adventure8/ballgame.py

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,209 @@
1-
secrets here
1+
# Adventure 8: ballgame.py
2+
#
3+
# From the book: "Adventures in Minecraft", 2nd Edition
4+
# written by David Whale and Martin O'Hanlon, Wiley, 2017
5+
# http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1119439582.html
6+
#
7+
# This program is a complete mini-game involving rolling a ball on a table
8+
9+
# Import necessary modules
10+
import microbit
11+
import mcpi.minecraft as minecraft
12+
import mcpi.block as block
13+
import time
14+
import random
15+
16+
# CONSTANTS
17+
# Change these to vary the size/difficulty of the game
18+
TABLE_WIDTH = 20
19+
TABLE_DEPTH = 20
20+
TREASURE_COUNT = 25
21+
TABLE = block.STONE.id
22+
BALL = block.CACTUS.id
23+
TREASURE = block.GOLD_BLOCK.id
24+
25+
# Connect to the Minecraft game and
26+
# let the player know that the micro:bit is connected
27+
mc = minecraft.Minecraft.create()
28+
mc.postToChat("BBC micro:bit joined the game")
29+
30+
# VARIABLES
31+
32+
table_x = None
33+
table_y = None
34+
table_z = None
35+
ball_x = None
36+
ball_y = None
37+
ball_z = None
38+
speed_x = 0
39+
speed_z = 0
40+
remaining = 1
41+
42+
# Build a complete game table
43+
def build_table(x, y, z):
44+
global table_x, table_y, table_z
45+
# Put a small gap around the edge in case of nearby mountains
46+
GAP = 10
47+
mc.setBlocks(x-GAP, y, z-GAP, x+TABLE_WIDTH+GAP, y+GAP, z+TABLE_DEPTH+GAP,
48+
block.AIR.id)
49+
# Build the table as a big block
50+
mc.setBlocks(x-1, y, z-1, x+TABLE_WIDTH+1, y+1, z+TABLE_DEPTH+1, TABLE)
51+
# Carve out the middle, this creates the wall around the table
52+
mc.setBlocks(x, y+1, z, x+TABLE_WIDTH, y+1, z+TABLE_DEPTH, block.AIR.id)
53+
# Remember where the table was built, for later
54+
table_x = x
55+
table_y = y
56+
table_z = z
57+
58+
# Place random treasure blocks on the table
59+
def place_treasure():
60+
y = table_y
61+
for i in range(TREASURE_COUNT):
62+
# BUGFIX: Prevent block being re-created in same pos
63+
# which would make it impossible to complete the game
64+
while True:
65+
# Choose a random position for the next block
66+
x = random.randint(0, TABLE_WIDTH) + table_x
67+
z = random.randint(0, TABLE_DEPTH) + table_z
68+
# Is it already a treasure block?
69+
b = mc.getBlock(x,y,z)
70+
if b == TREASURE: continue # if so, loop round to try again
71+
# Place treasure at the new random location
72+
mc.setBlock(x, y, z, TREASURE)
73+
break # break out of loop to move to next treasure block
74+
# BUGFIX END
75+
76+
# Move the ball based on the present ball speed
77+
def move_ball():
78+
new_x = ball_x - speed_x
79+
new_z = ball_z - speed_z
80+
if ball_x != new_x or ball_z != new_z: # prevent screen flicker
81+
# Don't draw the ball if it falls off the table for some reason
82+
if is_on_table(new_x, new_z):
83+
move_ball_to(new_x, ball_y, new_z)
84+
85+
# Move the ball to a specific position on the table
86+
def move_ball_to(x, y, z):
87+
global ball_x, ball_y, ball_z
88+
# Clear the existing ball if it has already been built
89+
if ball_x is not None:
90+
mc.setBlock(ball_x, ball_y, ball_z, block.AIR.id)
91+
# Update our record of where the ball is now
92+
ball_x = x
93+
ball_y = y
94+
ball_z = z
95+
# Build a block where the ball needs to be
96+
mc.setBlock(ball_x, ball_y, ball_z, BALL)
97+
98+
# Calculate a new speed value, given the existing speed and amount of tilt.
99+
# You can change this algorithm to make the acceleration and deceleration of
100+
# the ball more life-like
101+
def new_speed(speed, tilt):
102+
# Create a dead spot when the micro:bit is flat to slow/stop the ball
103+
if abs(tilt) < 300: tilt = 0
104+
# Reduce the range of the tilt to roughly -3..+3
105+
tilt = tilt / 300
106+
# Adjust the speed gradually each time, to simulate simple acceleration
107+
if tilt < speed:
108+
speed = speed - 1
109+
elif tilt > speed:
110+
speed = speed + 1
111+
return speed
112+
113+
# Sense the amount of tilt of the micro:bit and update the speed variables
114+
def check_tilt():
115+
global speed_x, speed_z
116+
speed_x = new_speed(speed_x, microbit.accelerometer.get_x())
117+
speed_z = new_speed(speed_z, microbit.accelerometer.get_y())
118+
#print(speed_x, speed_z)
119+
120+
# Work out if a given ball position is on the table or not.
121+
# This can be used to prevent the ball rolling off the table.
122+
# It can also be used to sense if it rolls off the table.
123+
def is_on_table(x, z):
124+
if x < table_x or x > table_x + TABLE_WIDTH:
125+
return False
126+
if z < table_z or z > table_z + TABLE_DEPTH:
127+
return False
128+
return True
129+
130+
# Check what is below the ball, and action it accordingly
131+
def check_below():
132+
global remaining
133+
# What is below the ball?
134+
block_below = mc.getBlock(ball_x, ball_y-1, ball_z)
135+
if block_below == TREASURE:
136+
# It's treasure, so collect it and update the count of remaining items
137+
mc.setBlock(ball_x, ball_y-1, ball_z, block.AIR.id)
138+
remaining = remaining - 1
139+
microbit.display.show(remaining)
140+
elif block_below == block.AIR.id:
141+
# It's a hole, so introduce a time-penalty
142+
move_ball_to(ball_x, ball_y-1, ball_z)
143+
while not microbit.button_b.was_pressed():
144+
time.sleep(0.1)
145+
# Bounce the ball out to a random new position
146+
move_ball_to(table_x + random.randint(0, TABLE_WIDTH), table_y+1,
147+
table_z + random.randint(0, TABLE_DEPTH))
148+
149+
# Wait for the user to start the game
150+
def wait_for_start():
151+
# A helpful message so that the user knows what to do
152+
mc.postToChat("press B to start")
153+
microbit.display.show("?")
154+
# Wait for the B button to be pressed on the micro:bit
155+
while not microbit.button_b.was_pressed():
156+
time.sleep(0.1)
157+
158+
# Countdown 5,4,3,2,1
159+
for t in range(6):
160+
microbit.display.show(str(5-t))
161+
time.sleep(0.5)
162+
163+
# Build the complete game with table and treasure
164+
def build_game():
165+
global speed_x, speed_z, remaining
166+
167+
# Get Steve's position, so the table can be built nearby
168+
pos = mc.player.getTilePos()
169+
# Build the table close by
170+
build_table(pos.x, pos.y-2, pos.z)
171+
# Start the ball at a random position
172+
move_ball_to(table_x + random.randint(0, TABLE_WIDTH), table_y+1,
173+
table_z + random.randint(0, TABLE_DEPTH))
174+
# Place all the treasure at random locations
175+
place_treasure()
176+
# Place Steve at a good viewing point
177+
mc.player.setTilePos(table_x + TABLE_WIDTH/2, table_y+1, table_z)
178+
# Start off the game variables at sensible values
179+
remaining = TREASURE_COUNT
180+
speed_x = 0
181+
speed_z = 0
182+
183+
# Play one complete game from start to end
184+
def play_game():
185+
# Remember what time the game was started
186+
start_time = time.time()
187+
# Loop around a game loop until all treasure is collected
188+
while remaining > 0:
189+
# Slow things down a bit, this loop runs 10 times per second
190+
time.sleep(0.1)
191+
# Find out how much the user is tilting the micro:bit
192+
check_tilt()
193+
# Move the ball based on the tilt
194+
move_ball()
195+
# Process treasure or air blocks below the ball
196+
check_below()
197+
# Remember what time the game finished
198+
end_time = time.time()
199+
# Display the total game time taken to collect all treasure
200+
mc.postToChat("game time=" + str(int(end_time-start_time)))
201+
202+
# The main program.
203+
# Loops forever, until you press CTRL-C
204+
while True:
205+
wait_for_start()
206+
build_game()
207+
play_game()
208+
209+
# END

0 commit comments

Comments
 (0)