This commit is contained in:
deckensteuerung 2018-10-21 15:19:51 +02:00
parent 27fa6f7c63
commit 6c7d81ed49
6 changed files with 130 additions and 88 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# fork of https://github.com/SmartViking/MaTris by deinkoks # fork of https://github.com/SmartViking/MaTris by deinkoks
# removed everythin but elemental gameplay, added multiplayer controlled via mqtt # removed everythin but elemental gameplay, added multiplayer controlled via mqtt
# most ugly thing: a mqtt client for every player due to callback limitations # most ugly thing: a mqtt client for every player due to callback limitations
@ -9,7 +9,7 @@ import os
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
from tetrominoes import list_of_tetrominoes from tetrominoes import list_of_tetrominoes, shape_str
from tetrominoes import rotate from tetrominoes import rotate
from scores import load_score, write_score from scores import load_score, write_score
@ -21,12 +21,16 @@ def get_sound(filename):
return pygame.mixer.Sound(os.path.join(os.path.dirname(__file__), "resources", filename)) return pygame.mixer.Sound(os.path.join(os.path.dirname(__file__), "resources", filename))
ENABLE_STDIO = True #dump game on stdio, not sure if works as intended. ENABLE_STDIO = True #dump game on stdio, not sure if works as intended.
ENABLE_MQTT = True # for debugging w/o a broker
ENABLE_SOUND = False
ENABLE_KEYBOARD_CONTROLS = False # working for player1. mostly
MQTT_TOPIC = "deckentetris/"
PLAYER = 10 # player count, 1 columns per player PLAYER = 10 # player count, 1 columns per player
BGCOLOR = (15, 15, 20) BGCOLOR = (15, 15, 20)
BORDERCOLOR = (140, 140, 140) BORDERCOLOR = (140, 140, 140)
GAMEOVER_COLOR = (140, 100, 100)
BLOCKSIZE = 16 # set this to 1 if run on the pixeldecke BLOCKSIZE = 1 # set this to 1 if run on the pixeldecke
BORDERWIDTH = 0 BORDERWIDTH = 0
MATRIS_OFFSET = 0 MATRIS_OFFSET = 0
@ -34,14 +38,14 @@ MATRIS_OFFSET = 0
# we have 8pixel columns with a height of 5*8px for every player # we have 8pixel columns with a height of 5*8px for every player
# lets use one for the next block, but 2 lines are hidden # lets use one for the next block, but 2 lines are hidden
MATRIX_WIDTH = 8 MATRIX_WIDTH = 8
MATRIX_HEIGHT = (4+2)*8 MATRIX_HEIGHT = (4)*8
LEFT_MARGIN = 0 LEFT_MARGIN = 0
WIDTH = (MATRIX_WIDTH*BLOCKSIZE + MATRIS_OFFSET*2 + LEFT_MARGIN) * PLAYER WIDTH = (MATRIX_WIDTH*BLOCKSIZE + MATRIS_OFFSET*2 + LEFT_MARGIN) * PLAYER
HEIGHT = (MATRIX_HEIGHT-2)*BLOCKSIZE + MATRIS_OFFSET*2 HEIGHT = (MATRIX_HEIGHT-2)*BLOCKSIZE + MATRIS_OFFSET*2
HEIGHT += BLOCKSIZE*8 # additional tile for preview above game HEIGHT += BLOCKSIZE*8 # additional tile for preview above game
HEIGHT += 2 # height fix for reasons
TRICKY_CENTERX = WIDTH-(WIDTH-(MATRIS_OFFSET+BLOCKSIZE*MATRIX_WIDTH))/2 TRICKY_CENTERX = WIDTH-(WIDTH-(MATRIS_OFFSET+BLOCKSIZE*MATRIX_WIDTH))/2
VISIBLE_MATRIX_HEIGHT = MATRIX_HEIGHT - 2 VISIBLE_MATRIX_HEIGHT = MATRIX_HEIGHT - 2
@ -60,22 +64,18 @@ class Matris(object):
self.request_movement('left') self.request_movement('left')
elif payload == "right": elif payload == "right":
self.request_movement('right') self.request_movement('right')
else: elif payload == "update":
pass #print the matrix self.mqtt.publish(MQTT_TOPIC+str(offset)+"/current", payload=shape_str(self.current_tetromino.shape), qos=0, retain=False)
self.mqtt.publish(MQTT_TOPIC+str(offset)+"/next", payload=shape_str(self.next_tetromino.shape), qos=0, retain=False)
def __init__(self, offset): def __init__(self, offset):
self.surface = screen.subsurface(Rect((MATRIX_WIDTH * BLOCKSIZE*offset, 8*BLOCKSIZE), self.surface = screen.subsurface(Rect((MATRIX_WIDTH * BLOCKSIZE*offset, 8*BLOCKSIZE),
(MATRIX_WIDTH * BLOCKSIZE, (MATRIX_HEIGHT-2) * BLOCKSIZE))) (MATRIX_WIDTH * BLOCKSIZE, (MATRIX_HEIGHT-2) * BLOCKSIZE)))
self.mqtt = mqtt.Client()
self.mqtt.connect("mqtt.chaospott.de", port=1883, keepalive=60, bind_address="")
self.offset = offset self.offset = offset
self.matrix = dict() self.matrix = dict()
for y in range(MATRIX_HEIGHT): for y in range(MATRIX_HEIGHT):
for x in range(MATRIX_WIDTH): for x in range(MATRIX_WIDTH):
self.matrix[(y,x)] = None self.matrix[(y,x)] = None
self.mqtt.subscribe("deckentetris/"+str(offset)+"/action", 2)
self.mqtt.on_message = self.on_msg
self.mqtt.loop_start()
""" """
`self.matrix` is the current state of the tetris board, that is, it records which squares are `self.matrix` is the current state of the tetris board, that is, it records which squares are
currently occupied. It does not include the falling tetromino. The information relating to the currently occupied. It does not include the falling tetromino. The information relating to the
@ -109,6 +109,15 @@ class Matris(object):
self.linescleared_sound = get_sound("linecleared.wav") self.linescleared_sound = get_sound("linecleared.wav")
self.highscorebeaten_sound = get_sound("highscorebeaten.wav") self.highscorebeaten_sound = get_sound("highscorebeaten.wav")
if ENABLE_MQTT:
self.mqtt = mqtt.Client()
self.mqtt.connect("mqtt.chaospott.de", port=1883, keepalive=60, bind_address="")
self.mqtt.subscribe(MQTT_TOPIC+str(offset)+"/action", 2)
self.mqtt.on_message = self.on_msg
self.mqtt.loop_start()
self.mqtt.publish(MQTT_TOPIC+str(offset)+"/current", payload=shape_str(self.current_tetromino.shape), qos=0, retain=False)
self.mqtt.publish(MQTT_TOPIC+str(offset)+"/next", payload=shape_str(self.next_tetromino.shape), qos=0, retain=False)
def set_tetrominoes(self): def set_tetrominoes(self):
self.current_tetromino = self.next_tetromino self.current_tetromino = self.next_tetromino
@ -132,43 +141,45 @@ class Matris(object):
def update(self, timepassed): def update(self, timepassed):
self.needs_redraw = False self.needs_redraw = False
pressed = lambda key: event.type == pygame.KEYDOWN and event.key == key if ENABLE_KEYBOARD_CONTROLS:
unpressed = lambda key: event.type == pygame.KEYUP and event.key == key pressed = lambda key: event.type == pygame.KEYDOWN and event.key == key
#self.mqtt.loop() unpressed = lambda key: event.type == pygame.KEYUP and event.key == key
events = pygame.event.get()
for event in events: events = pygame.event.get()
if pressed(pygame.K_p):
self.surface.fill((0,0,0)) for event in events:
self.needs_redraw = True if pressed(pygame.K_p):
self.paused = not self.paused self.surface.fill((0,0,0))
elif event.type == pygame.QUIT: self.needs_redraw = True
self.gameover(full_exit=True) self.paused = not self.paused
elif pressed(pygame.K_ESCAPE): elif event.type == pygame.QUIT:
self.gameover() self.gameover(full_exit=True)
elif pressed(pygame.K_ESCAPE):
self.gameover()
if self.paused: if self.paused:
return self.needs_redraw return self.needs_redraw
for event in events: if ENABLE_KEYBOARD_CONTROLS:
if pressed(pygame.K_SPACE): for event in events:
self.hard_drop() if pressed(pygame.K_SPACE):
elif pressed(pygame.K_UP) or pressed(pygame.K_w): self.hard_drop()
self.request_rotation() elif pressed(pygame.K_UP) or pressed(pygame.K_w):
self.request_rotation()
elif pressed(pygame.K_LEFT) or pressed(pygame.K_a): elif pressed(pygame.K_LEFT) or pressed(pygame.K_a):
self.request_movement('left') self.request_movement('left')
self.movement_keys['left'] = 1 self.movement_keys['left'] = 1
elif pressed(pygame.K_RIGHT) or pressed(pygame.K_d): elif pressed(pygame.K_RIGHT) or pressed(pygame.K_d):
self.request_movement('right') self.request_movement('right')
self.movement_keys['right'] = 1 self.movement_keys['right'] = 1
elif unpressed(pygame.K_LEFT) or unpressed(pygame.K_a): elif unpressed(pygame.K_LEFT) or unpressed(pygame.K_a):
self.movement_keys['left'] = 0 self.movement_keys['left'] = 0
self.movement_keys_timer = (-self.movement_keys_speed)*2 self.movement_keys_timer = (-self.movement_keys_speed)*2
elif unpressed(pygame.K_RIGHT) or unpressed(pygame.K_d): elif unpressed(pygame.K_RIGHT) or unpressed(pygame.K_d):
self.movement_keys['right'] = 0 self.movement_keys['right'] = 0
self.movement_keys_timer = (-self.movement_keys_speed)*2 self.movement_keys_timer = (-self.movement_keys_speed)*2
@ -176,8 +187,12 @@ class Matris(object):
self.downwards_speed = self.base_downwards_speed ** (1 + self.level/10.) self.downwards_speed = self.base_downwards_speed ** (1 + self.level/10.)
self.downwards_timer += timepassed self.downwards_timer += timepassed
downwards_speed = self.downwards_speed*0.10 if any([pygame.key.get_pressed()[pygame.K_DOWN],
pygame.key.get_pressed()[pygame.K_s]]) else self.downwards_speed if ENABLE_KEYBOARD_CONTROLS:
downwards_speed = self.downwards_speed*0.10 if any([pygame.key.get_pressed()[pygame.K_DOWN],
pygame.key.get_pressed()[pygame.K_s]]) else self.downwards_speed
else:
downwards_speed = self.downwards_speed
if self.downwards_timer > downwards_speed: if self.downwards_timer > downwards_speed:
if not self.request_movement('down'): if not self.request_movement('down'):
self.lock_tetromino() self.lock_tetromino()
@ -185,11 +200,12 @@ class Matris(object):
self.downwards_timer %= downwards_speed self.downwards_timer %= downwards_speed
if any(self.movement_keys.values()): if ENABLE_KEYBOARD_CONTROLS:
self.movement_keys_timer += timepassed if any(self.movement_keys.values()):
if self.movement_keys_timer > self.movement_keys_speed: self.movement_keys_timer += timepassed
self.request_movement('right' if self.movement_keys['right'] else 'left') if self.movement_keys_timer > self.movement_keys_speed:
self.movement_keys_timer %= self.movement_keys_speed self.request_movement('right' if self.movement_keys['right'] else 'left')
self.movement_keys_timer %= self.movement_keys_speed
return self.needs_redraw return self.needs_redraw
@ -216,13 +232,15 @@ class Matris(object):
is responsible for checking if it's game over. is responsible for checking if it's game over.
""" """
write_score(self.score) #write_score(self.score)
if full_exit:
exit()
else:
raise GameOver("Sucker!")
#if full_exit:
# exit()
#else:
# raise GameOver("Sucker!")
self.paused = True
self.surface.fill(GAMEOVER_COLOR)
self.needs_redraw =True
def place_shadow(self): def place_shadow(self):
posY, posX = self.tetromino_position posY, posX = self.tetromino_position
while self.blend(position=(posY, posX)): while self.blend(position=(posY, posX)):
@ -333,17 +351,18 @@ class Matris(object):
self.lines += lines_cleared self.lines += lines_cleared
if lines_cleared: if lines_cleared:
if lines_cleared >= 4: if lines_cleared >= 4 and ENABLE_SOUND:
self.linescleared_sound.play() self.linescleared_sound.play()
self.score += 100 * (lines_cleared**2) * self.combo self.score += 100 * (lines_cleared**2) * self.combo
if not self.played_highscorebeaten_sound and self.score > self.highscore: if not self.played_highscorebeaten_sound and self.score > self.highscore:
if self.highscore != 0: if self.highscore != 0 and ENABLE_SOUND:
self.highscorebeaten_sound.play() self.highscorebeaten_sound.play()
self.played_highscorebeaten_sound = True self.played_highscorebeaten_sound = True
if self.lines >= self.level*10: if self.lines >= self.level*10:
self.levelup_sound.play() if ENABLE_SOUND:
self.levelup_sound.play()
self.level += 1 self.level += 1
self.combo = self.combo + 1 if lines_cleared else 1 self.combo = self.combo + 1 if lines_cleared else 1
@ -351,7 +370,8 @@ class Matris(object):
self.set_tetrominoes() self.set_tetrominoes()
if not self.blend(): if not self.blend():
self.gameover_sound.play() if ENABLE_SOUND:
self.gameover_sound.play()
self.gameover() self.gameover()
self.needs_redraw = True self.needs_redraw = True
@ -450,8 +470,9 @@ class Game(object):
pygame.display.flip() pygame.display.flip()
if ENABLE_STDIO: if ENABLE_STDIO:
#print(screen.get_view('2')) #print("-------------")
os.write(1, screen.get_view('2')) #print(pygame.image.tostring(screen, "RGB", False))
os.write(1, pygame.image.tostring(screen, "RGB", False))
def blit_next_tetromino(self, tetromino_surf, offset): def blit_next_tetromino(self, tetromino_surf, offset):
@ -498,9 +519,14 @@ def construct_nightmare(size):
if __name__ == '__main__': if __name__ == '__main__':
pygame.init() pygame.init()
print("Width:", WIDTH, "Height:", HEIGHT, "Playercount:", PLAYER)
if ENABLE_MQTT or True:
print("MQTT enabled:")
print("TOPIC:", MQTT_TOPIC+"<playerid>/action", " PAYLOAD: left|right|rotate|drop|update")
print("TOPIC:", MQTT_TOPIC+"<playerid>/current", "Show the current tetromino. Updated by update @ /action.")
print("TOPIC:", MQTT_TOPIC+"<playerid>/next", "Show the current tetromino. Updated by update @ /action.")
screen = pygame.display.set_mode((WIDTH, HEIGHT)) screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("MaTris") pygame.display.set_caption("Deckentetris")
# Menu().main(screen) # Menu().main(screen)
Game().main(screen) Game().main(screen)

View File

@ -17,9 +17,9 @@ uint32_t hash32(uint32_t d) {
} }
// must be the next prime after NUM_TOTAL_LEDS // must be the next prime after NUM_TOTAL_LEDS
#define IDLE_PRIME 1931 #define IDLE_PRIME 3203
// must be a primitive element of the prime residue field |N/IDLE_PRIM // must be a primitive element of the prime residue field |N/IDLE_PRIM
#define IDLE_PRIM_ELEM 283 #define IDLE_PRIM_ELEM 777
int IDLE_rand(int *seed){ int IDLE_rand(int *seed){
return *seed = (*seed * IDLE_PRIM_ELEM ) % IDLE_PRIME; return *seed = (*seed * IDLE_PRIM_ELEM ) % IDLE_PRIME;

View File

@ -9,8 +9,8 @@ import random
PADDLE_LEN = 6 PADDLE_LEN = 6
Nx = int(sys.argv[1]) Ny = int(sys.argv[1])
Ny = int(sys.argv[2]) Nx = int(sys.argv[2])
param = sys.argv[3] param = sys.argv[3]
speed = 1.0 speed = 1.0
@ -21,8 +21,10 @@ except:
pygame.init() pygame.init()
pygame.joystick.init() pygame.joystick.init()
j = pygame.joystick.Joystick(0) j1 = pygame.joystick.Joystick(0)
j.init() j2 = pygame.joystick.Joystick(1)
j1.init()
j2.init()
numbers = [ numbers = [
int("0b111101101101111", 2), int("0b111101101101111", 2),
@ -69,14 +71,14 @@ while True:
time.sleep(0.016) time.sleep(0.016)
if j.get_axis(0) > 0 and player1+PADDLE_LEN < Nx: if j1.get_axis(0) > 0.2 and player1+PADDLE_LEN < Nx:
player1 += 1 player1 += 1
if j.get_axis(0) < 0 and player1 > 0 : if j1.get_axis(0) < -0.2 and player1 > 0 :
player1 -= 1 player1 -= 1
if j.get_axis(1) > 0 and player2+PADDLE_LEN < Nx: if j2.get_axis(0) > 0.2 and player2+PADDLE_LEN < Nx:
player2 += 1 player2 += 1
if j.get_axis(1) < 0 and player2 > 0 : if j2.get_axis(0) < -0.2 and player2 > 0 :
player2 -= 1 player2 -= 1
x += dx x += dx
@ -114,7 +116,7 @@ while True:
buffer = bytearray(b"\x00"*(3*Nx*Ny)) buffer = bytearray(b"\x00"*(3*Nx*Ny))
def SetPixel(x, y, r, g, b): def SetPixel(x, y, r, g, b):
idx = 3*(x+Nx*y) idx = 3*(y+Ny*x)
buffer[idx+0] = r buffer[idx+0] = r
buffer[idx+1] = g buffer[idx+1] = g
buffer[idx+2] = b buffer[idx+2] = b

View File

@ -1,2 +1,6 @@
#!/bin/sh #!/bin/bash
export PATH="$PATH:/home/deckensteuerung/.config/bin"
youtube-dl --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe - youtube-dl --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
#youtube-dl --no-progress --output - ${3} | ffmpeg -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
exit 0

View File

@ -1,7 +1,7 @@
import logging import logging
#width #width
ScreenX = 48 ScreenX = 80
#height #height
ScreenY = 40 ScreenY = 40
@ -13,19 +13,26 @@ NoDataTimeout = 40
LogLevel = logging.DEBUG LogLevel = logging.DEBUG
UseGui = True UseGui = True
GuiScaleFactor = 20 GuiScaleFactor = 15
WebHost = "localhost" WebHost = "0.0.0.0"
WebPort = 8000 WebPort = 8000
# first app is always running in IDLE # first app is always running in IDLE
Apps = [ Apps = [
{"guiname": "Dual moodlight", "name": "bimood", "cmd": "apps/bimood", "persistent": False},
{"guiname": "IDLE", "name": "pixelflut", "cmd": "apps/idle.py", "persistent": False}, {"guiname": "IDLE", "name": "pixelflut", "cmd": "apps/idle.py", "persistent": False},
{"guiname": "IDLE2", "name": "idlec", "cmd": "apps/idle2"}, {"guiname": "IDLE2", "name": "idlec", "cmd": "apps/idle2"},
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh"}, {"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh", "persistent": False},
{"guiname": "Snake", "name": "snake", "cmd": "apps/snake.py"}, {"guiname": "Stream", "name": "stream", "cmd": "apps/stream.sh", "persistent": False},
{"guiname": "Tetris", "name": "tetris", "cmd": "apps/deckentetris/deckentetris.py"} {"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py"}, {"guiname": "Tetris", "name": "tetris", "cmd": "apps/deckentetris/deckentetris.py", "persistent": False},
{"guiname": "SkyScrapper", "name": "sky", "cmd": "apps/weather/main.py"},
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py", "persistent": False},
{"guiname": "Snake", "name": "snake", "cmd": "apps/snake.py", "persistent": False},
{"name": "pong", "cmd": "apps/pong.py"}, {"name": "pong", "cmd": "apps/pong.py"},
{"name": "framebuffer", "cmd": ["apps/fbcp", "/dev/fb0"]}, {"name": "framebuffer", "cmd": ["apps/fbcp", "/dev/fb0"]},
{"name": "gif", "cmd": "apps/gif.sh"},
{"name": "colormap", "cmd": "apps/colormap.py"},
{"name": "fibonacci-clock", "cmd": "apps/fibonacci-clock.py"}
] ]

5
main.py Normal file → Executable file
View File

@ -1,3 +1,4 @@
import config import config
import subprocess import subprocess
import os import os
@ -94,6 +95,7 @@ class SerialWriter(threading.Thread):
data = self.datasource.getData() data = self.datasource.getData()
ser.write(b"\01") ser.write(b"\01")
ser.write(data) ser.write(data)
ser.flush()
except Exception as e: except Exception as e:
if ser != None: if ser != None:
ser.close() ser.close()
@ -138,8 +140,9 @@ class LogReader(threading.Thread):
logging.info("LogReader started") logging.info("LogReader started")
while running and self.running: while running and self.running:
try: try:
self.log += runner.app.stderr.read(1).decode("utf-8") self.log += self.runner.app.stderr.read(1).decode("utf-8")
except Exception as e: except Exception as e:
print(e)
time.sleep(1) time.sleep(1)
logging.info("LogReader closed") logging.info("LogReader closed")
def stop(self): def stop(self):