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
# removed everythin but elemental gameplay, added multiplayer controlled via mqtt
# 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
from tetrominoes import list_of_tetrominoes
from tetrominoes import list_of_tetrominoes, shape_str
from tetrominoes import rotate
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))
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
BGCOLOR = (15, 15, 20)
BORDERCOLOR = (140, 140, 140)
BLOCKSIZE = 16 # set this to 1 if run on the pixeldecke
GAMEOVER_COLOR = (140, 100, 100)
BLOCKSIZE = 1 # set this to 1 if run on the pixeldecke
BORDERWIDTH = 0
MATRIS_OFFSET = 0
@ -34,14 +38,14 @@ MATRIS_OFFSET = 0
# 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
MATRIX_WIDTH = 8
MATRIX_HEIGHT = (4+2)*8
MATRIX_HEIGHT = (4)*8
LEFT_MARGIN = 0
WIDTH = (MATRIX_WIDTH*BLOCKSIZE + MATRIS_OFFSET*2 + LEFT_MARGIN) * PLAYER
HEIGHT = (MATRIX_HEIGHT-2)*BLOCKSIZE + MATRIS_OFFSET*2
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
VISIBLE_MATRIX_HEIGHT = MATRIX_HEIGHT - 2
@ -60,22 +64,18 @@ class Matris(object):
self.request_movement('left')
elif payload == "right":
self.request_movement('right')
else:
pass #print the matrix
elif payload == "update":
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):
self.surface = screen.subsurface(Rect((MATRIX_WIDTH * BLOCKSIZE*offset, 8*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.matrix = dict()
for y in range(MATRIX_HEIGHT):
for x in range(MATRIX_WIDTH):
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
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.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):
self.current_tetromino = self.next_tetromino
@ -132,9 +141,10 @@ class Matris(object):
def update(self, timepassed):
self.needs_redraw = False
if ENABLE_KEYBOARD_CONTROLS:
pressed = lambda key: event.type == pygame.KEYDOWN and event.key == key
unpressed = lambda key: event.type == pygame.KEYUP and event.key == key
#self.mqtt.loop()
events = pygame.event.get()
for event in events:
@ -150,6 +160,7 @@ class Matris(object):
if self.paused:
return self.needs_redraw
if ENABLE_KEYBOARD_CONTROLS:
for event in events:
if pressed(pygame.K_SPACE):
self.hard_drop()
@ -176,8 +187,12 @@ class Matris(object):
self.downwards_speed = self.base_downwards_speed ** (1 + self.level/10.)
self.downwards_timer += timepassed
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 not self.request_movement('down'):
self.lock_tetromino()
@ -185,6 +200,7 @@ class Matris(object):
self.downwards_timer %= downwards_speed
if ENABLE_KEYBOARD_CONTROLS:
if any(self.movement_keys.values()):
self.movement_keys_timer += timepassed
if self.movement_keys_timer > self.movement_keys_speed:
@ -216,13 +232,15 @@ class Matris(object):
is responsible for checking if it's game over.
"""
write_score(self.score)
if full_exit:
exit()
else:
raise GameOver("Sucker!")
#write_score(self.score)
#if full_exit:
# exit()
#else:
# raise GameOver("Sucker!")
self.paused = True
self.surface.fill(GAMEOVER_COLOR)
self.needs_redraw =True
def place_shadow(self):
posY, posX = self.tetromino_position
while self.blend(position=(posY, posX)):
@ -333,16 +351,17 @@ class Matris(object):
self.lines += lines_cleared
if lines_cleared:
if lines_cleared >= 4:
if lines_cleared >= 4 and ENABLE_SOUND:
self.linescleared_sound.play()
self.score += 100 * (lines_cleared**2) * self.combo
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.played_highscorebeaten_sound = True
if self.lines >= self.level*10:
if ENABLE_SOUND:
self.levelup_sound.play()
self.level += 1
@ -351,6 +370,7 @@ class Matris(object):
self.set_tetrominoes()
if not self.blend():
if ENABLE_SOUND:
self.gameover_sound.play()
self.gameover()
@ -450,8 +470,9 @@ class Game(object):
pygame.display.flip()
if ENABLE_STDIO:
#print(screen.get_view('2'))
os.write(1, screen.get_view('2'))
#print("-------------")
#print(pygame.image.tostring(screen, "RGB", False))
os.write(1, pygame.image.tostring(screen, "RGB", False))
def blit_next_tetromino(self, tetromino_surf, offset):
@ -498,9 +519,14 @@ def construct_nightmare(size):
if __name__ == '__main__':
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))
pygame.display.set_caption("MaTris")
pygame.display.set_caption("Deckentetris")
# Menu().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
#define IDLE_PRIME 1931
#define IDLE_PRIME 3203
// 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){
return *seed = (*seed * IDLE_PRIM_ELEM ) % IDLE_PRIME;

View File

@ -9,8 +9,8 @@ import random
PADDLE_LEN = 6
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
Ny = int(sys.argv[1])
Nx = int(sys.argv[2])
param = sys.argv[3]
speed = 1.0
@ -21,8 +21,10 @@ except:
pygame.init()
pygame.joystick.init()
j = pygame.joystick.Joystick(0)
j.init()
j1 = pygame.joystick.Joystick(0)
j2 = pygame.joystick.Joystick(1)
j1.init()
j2.init()
numbers = [
int("0b111101101101111", 2),
@ -69,14 +71,14 @@ while True:
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
if j.get_axis(0) < 0 and player1 > 0 :
if j1.get_axis(0) < -0.2 and player1 > 0 :
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
if j.get_axis(1) < 0 and player2 > 0 :
if j2.get_axis(0) < -0.2 and player2 > 0 :
player2 -= 1
x += dx
@ -114,7 +116,7 @@ while True:
buffer = bytearray(b"\x00"*(3*Nx*Ny))
def SetPixel(x, y, r, g, b):
idx = 3*(x+Nx*y)
idx = 3*(y+Ny*x)
buffer[idx+0] = r
buffer[idx+1] = g
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 -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
#width
ScreenX = 48
ScreenX = 80
#height
ScreenY = 40
@ -13,19 +13,26 @@ NoDataTimeout = 40
LogLevel = logging.DEBUG
UseGui = True
GuiScaleFactor = 20
GuiScaleFactor = 15
WebHost = "localhost"
WebHost = "0.0.0.0"
WebPort = 8000
# first app is always running in IDLE
Apps = [
{"guiname": "Dual moodlight", "name": "bimood", "cmd": "apps/bimood", "persistent": False},
{"guiname": "IDLE", "name": "pixelflut", "cmd": "apps/idle.py", "persistent": False},
{"guiname": "IDLE2", "name": "idlec", "cmd": "apps/idle2"},
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh"},
{"guiname": "Snake", "name": "snake", "cmd": "apps/snake.py"},
{"guiname": "Tetris", "name": "tetris", "cmd": "apps/deckentetris/deckentetris.py"}
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py"},
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh", "persistent": False},
{"guiname": "Stream", "name": "stream", "cmd": "apps/stream.sh", "persistent": False},
{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
{"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": "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 subprocess
import os
@ -94,6 +95,7 @@ class SerialWriter(threading.Thread):
data = self.datasource.getData()
ser.write(b"\01")
ser.write(data)
ser.flush()
except Exception as e:
if ser != None:
ser.close()
@ -138,8 +140,9 @@ class LogReader(threading.Thread):
logging.info("LogReader started")
while running and self.running:
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:
print(e)
time.sleep(1)
logging.info("LogReader closed")
def stop(self):