Make your own game of Brick-O-Blox (Tetris clone)

https://www.adafruit.com/product/420

Python file: •https://gist.github.com/ScienceElectronicsFun/aa84ac095d66a1f6e114c0cbc9aa4a58

8051 assembly file: •https://gist.github.com/ScienceElectronicsFun/fc68753311d5008b7ae7401c67d91a68

Eagle files:

Schematic

Project File

#Version 0.2
#Different color bricks now supported.
import random
import serial
import time
u = serial.Serial("COM13", baudrate=9600, timeout=10)
class block():
def __init__(self):
self.generate_block()
self.location = [8, 1]
def generate_block(self):
'''
Generate block of random type and color.
Returns block matrix and color.
'''
self.block_type = random.randint(0,5)
c = random.randint(1,3)
self.color = c
if self.block_type == 0:
self.block = [[c,c,c],
[c,c,c],
[c,c,c]]
self.rotated = self.block
elif self.block_type == 1:
self.block = [[c,0,0],
[c,c,0],
[c,c,c]]
self.rotated = [[c,c,c],
[0,c,c],
[0,0,c]]
elif self.block_type == 2:
self.block = [[c],
[c],
[c]]
self.rotated = [[c,c,c]]
elif self.block_type == 3:
self.block = [[c],
[c],
[c],
[c]]
self.rotated = [[c,c,c,c]]
elif self.block_type == 4:
self.block = [[c,c],
[c,c]]
self.rotated = self.block
elif self.block_type == 5:
self.block = [[c,c,0],
[0,c,c]]
self.rotated = [[0,c],
[c,c],
[c,0]]
def rotate(self):
temp = self.block
self.block = self.rotated
self.rotated = temp
def check_collision(self, m):
'''
Checks to see if the forward face of the block touches an element
of given matrix object 'm'.
'''
collision = False
x = self.location[0]
y = self.location[1]
for i, row in enumerate(self.block):
#print(row)
furthest = 0
#In the block, find the furthest to the right it extends to.
for j, col in enumerate(row):
if self.block[i][j] > 0:
furthest = j
#Furthest is j
#Now, does the matrix have a block in the next position?
if m.data[i+x][furthest+y+1] > 0:
collision = True
break
return collision
def generate_merge(self, m):
'''
Adds the block to the given matrix object 'm' for display purposes.
'''
x = self.location[0]
y = self.location[1]
c = self.color
for i, row in enumerate(self.block):
for j, col in enumerate(row):
if self.block[i][j] > 0:
m.data[x+i][y+j] = c
def subtract(self, m):
'''
Removes the block from the given matrix object 'm'.
This is done prior to moving the block to a new location.
'''
x = self.location[0]
y = self.location[1]
for i, row in enumerate(self.block):
for j, col in enumerate(row):
if self.block[i][j] > 0:
m.data[x+i][y+j] = 0
class matrix():
'''
The matrix class is a 16 (row) x 32 (column) matrix where 0 = off and 1 = on.
The translate function converts to a stream of 256 bytes that can be sent to the '8051 driver
to display the pattern on the RGB LED matrix.
'''
def __init__(self):
r = [0 for i in range(0, 32)]
#self.data initializes to an empty matrix.
self.data = [list(r) for i in range(0, 16)]
self.stream = None
self.bytes = None
self.max = 25
self.color_map = {1:(2, 1),
2:(8, 4),
3:(64, 32)}
def check_for_full_cols(self):
full_col_list = []
for i in range(0, self.max):
if all([x[i] for x in self.data]):
full_col_list.append(i)
for col in full_col_list:
for k in range(0, 16):
for j in range(1,i+1):
self.data[k][j] = self.data[k][j-1]
def translate(self):
#Translates pattern to byte stream to drive the RGB LED matrix.
self.stream = [0 for i in range(0,256)]
#Top half
for k in range(0, 8):
for i, member in enumerate(self.data[k]):
if member > 0:
self.stream[223-i-k*32] += self.color_map[member][0]
#Bottom half
for k in range(8, 16):
for i, member in enumerate(self.data[k]):
if member > 0:
self.stream[223-i-((k-8)*32)] += self.color_map[member][1]
self.bytes = ''
#Convert to hex
for byte in self.stream:
self.bytes += chr(byte)
def dump_matrix(self):
'''
Prints matrix object for debugging.
'''
print()
for row in self.data:
print(''.join([str(x) for x in row]))
print()
def readfile(filename):
'''
Reads a csv file matrix object.
Returns grid which can be loaded into m.data.
'''
file_r = open(filename, 'r')
data = file_r.readlines()
grid = []
for line in data:
newline = [int(x.strip()) for x in line.split(',')]
grid.append(newline)
return grid
def start_game():
m = matrix()
# This sets bottom brick layer
t = 25
for i in range(0,16):
m.data[i][t] = 1
m.translate()
game_running = True
points = 0
while game_running:
m.check_for_full_cols()
#Start new round
round_running = True
b = block()
c = b.check_collision(m)
if c:
game_running = False
round_running = False
while round_running:
#Display the new block
b.generate_merge(m)
m.translate()
u.write('\x10'.encode())
u.write(m.bytes.encode())
time.sleep(0.01)
c = b.check_collision(m)
if c:
round_running = False
if round_running:
b.subtract(m)
#Move the block to the right
b.location[1] = b.location[1] + 1
#If command pending, move
u.write('\x11'.encode())
current = u.read()
if current == bytes(chr(2).encode()):
b.location[0] += 1
if current == bytes(chr(4).encode()):
b.location[0] -= 1
if current == bytes(chr(8).encode()):
b.rotate()
if current == bytes(chr(16).encode()):
b.location[0] -= 2
start_game()
view raw BrickOBlox.py hosted with ❤ by GitHub
; Used Tom C. Hayes codes as templates (bit flip.a51, serial message receive.a51, int_inc_dec.a51)
; from Learning the Art of Electronics
$NOSYMBOLS ; keeps listing short..
$INCLUDE (C:\MICRO\8051\RAISON\INC\c8051f410.inc)
$INCLUDE (C:\MICRO\8051\RAISON\INC\VECTORS320.INC)
CLOCK EQU P0.0
LATCH EQU P0.1
TIMER96K EQU 96h
ORG 0 ; tells assembler the address at which to place this code
SJMP STARTUP ; here code begins--with just a jump to start of
; real program. ALL our programs will start thus
ORG 80h ; ...and here the program starts
STARTUP:
CLR 0
CLR 1
CLR 2
CLR 3
ACALL USUAL_SETUP
ACALL TIMER_SETUP
ACALL SERIAL_SETUP
ACALL INTERRUPT_SETUP
SETB LATCH ;LATCH IS ACTIVE LOW
CLR CLOCK
RESET:
MOV R3, #00h ; This holds the current byte
MOV P2, #00h ; ADDRESS
MOV R2, #00h ; ADDRESS COUNTER
MOV P1, #00h ; port 1 = GGXXBBRR
MOV R1, #00h; increment for serial
MOV DPTR, #0000h ; initialize the data pointer
;SCRATCH REGISTERS USED
;R0 CLOCK PULSES
;R1 INCREMENT FOR SERIAL DATA INPUT
;R2 ADDRESS COUNTER (LINE ON DISPLAY)
;R3 BYTE INDEX FOR DISPALY
MAIN_LOOP:
JNB RI0, REFRESH_DISPLAY ; await receipt of a byte from UART
CLR RI0 ; ...clear flag when it's asserted
MOV A, SBUF0
CJNE A, #10h, CHECK_NEXT
SJMP RESET
CHECK_NEXT:
CJNE A, #11h, STORE_IT ; PROGRAM REQUEST KEY STATUS
MOV R7, #00h
JNB 0, CHECK_UP_DIR
MOV R7, #02h
CLR 0 ; CLEAR THE FLAG
SJMP SEND_COMMAND
;MOV R7, #41h
CHECK_UP_DIR:
JNB 1, CHECK_ROTATE
MOV R7, #04h
CLR 1
SJMP SEND_COMMAND
CHECK_ROTATE:
JNB 2, CHECK_DROP
MOV R7, #08h
CLR 2
SJMP SEND_COMMAND
CHECK_DROP:
JNB 3, SEND_COMMAND
MOV R7, #10h
CLR 3
SEND_COMMAND:
ACALL SENDIT
SJMP RESET
;SJMP REFRESH_DISPLAY
STORE_IT:
MOVX @R1, A ; get received data
INC R1
REFRESH_DISPLAY:
CLR LATCH
MOV R0, #20h ; R0 holds clock pulses, EACH ADDRESS 4 bytes of clocks (32)
PULSES:
;CLOCK IN PULSES TO R1,2/G1,2/B1,2
ACALL TICKTOCKBYTE
;FOUR BYTES OF PULSES CLOCKED IN TO CURRENT ADDRESS
DJNZ R0, PULSES
;LATCH DATA FROM ADDRESS
SETB LATCH
CLR LATCH
;INCREMENT ADDRESS
INC P2
INC R2
;ALL ADDRESSES COMPLETE?
CJNE R2, #08, REFRESH_DISPLAY ; NO KEEP GOING
;YES, SET ADDRESS BACK TO 0
MOV P2, #00h
MOV R2, #00h
;ANOTHER ROUND
SJMP MAIN_LOOP
USUAL_SETUP:
ANL PCA0MD, #NOT(040h) ; Clear Watchdog Enable bit
MOV OSCICN, #087h
MOV XBR1, #40h ; Enable Crossbar
MOV P0MDOUT, #0DFh
MOV P1MDOUT, #0FFh
MOV P2MDOUT, #0FFh
MOV XBR0, #01h
RET
TIMER_SETUP:
;mov TCON, #040h
MOV TMOD, #20h ; Timer 1: Mode 2 (8-bit reload) (Dallas sample pgm, p. 299)
MOV TH1, #96h ; Reload for 9600, if SMOD = 0 (table, p. 296, again)
;MOV TL1, #TIMER96K ; This is for first-pass only
MOV TL1, #00h
ANL (PCON), #07Fh ; clear SMOD bit (this is default, but let's be thorough):
; prevents baud-rate doubling at Serial Port 0 (p. 21)
RET
SERIAL_SETUP:
mov SCON0, #10h ; set receive enable, 8-bit rather than "9-bit" or what the Dallas pgm calls 10-bit
SETB TR1 ; Start Timer
ret
SENDIT:
MOV SBUF0, R7 ; Put character in buffer (send it)--UART 1
LINGER: JNB TI0, LINGER ; wait here till told it's been sent
CLR TI0
RET
TICKTOCK:
SETB CLOCK
CLR CLOCK
RET
TICKTOCKBYTE:
ACALL GETCODE
MOV P1, A
SETB CLOCK
CLR CLOCK
INC R3
DONE:
RET
; SUBROUTINE GETCODE
; LOAD R3 WITH INDEX OF CODE YOU WANT (0 INDEXED)
; BYTE RETURNS IN ACCUMULATOR
GETCODE:
MOV DPL, R3
MOVX A, @DPTR
RET
INTERRUPT_SETUP:
SETB IT0
;SETB IT1
MOV IT01CF, #02h ; SETS INT0* on P0.2
SETB EX0
;SETB EX1
SETB EA
RET
;BUTTON PUSHED MOVE BLOCK LEFT OR RIGHT
ORG INT0VECTOR
SJMP ISR0
ISR0:
JNB P0.3, BUTTON_A_PRESS
JNB P0.6, BUTTON_B_PRESS
JNB P0.7, BUTTON_C_PRESS
;MUST BE BUTTON_D
SETB 3
RETI
BUTTON_A_PRESS:
SETB 0
RETI
BUTTON_B_PRESS:
SETB 1
RETI
BUTTON_C_PRESS:
SETB 2
RETI
END