Browse Source

fix tetris. image resize aspect ratio. cheap chinese gamepad support.

Thomas Buck 1 year ago
parent
commit
ea9b0d94ff
11 changed files with 407 additions and 202 deletions
  1. 48
    48
      camp_pico.py
  2. 53
    52
      camp_small.py
  3. 61
    29
      gamepad.py
  4. 14
    5
      image.py
  5. 4
    4
      mapper.py
  6. 1
    1
      pi.py
  7. 100
    24
      pico_ota.py
  8. 26
    13
      snake.py
  9. 1
    1
      solid.py
  10. 87
    23
      tetris.py
  11. 12
    2
      util.py

+ 48
- 48
camp_pico.py View File

@@ -14,59 +14,59 @@ camp_green = (63, 255, 33)
14 14
 from qr_tmp import qr_data
15 15
 from img_tmp import img_data
16 16
 
17
-if True:#__name__ == "__main__":
18
-    from solid import Solid
19
-    from life import GameOfLife
20
-    from net import CheckHTTP
21
-    from qr import QRScreen
22
-    from scroll import ScrollText
23
-    from splash import SplashScreen
24
-    from manager import Manager
25
-    from pico import PicoBatt
17
+from solid import Solid
18
+from life import GameOfLife
19
+from net import CheckHTTP
20
+from qr import QRScreen
21
+from scroll import ScrollText
22
+from splash import SplashScreen
23
+from manager import Manager
24
+from pico import PicoBatt
26 25
 
27
-    url = "http://ubabot.frubar.net"
26
+#url = "http://ubabot.frubar.net"
27
+url = "http://www.xythobuz.de"
28 28
 
29
-    import util
30
-    t = util.getTarget()
29
+import util
30
+t = util.getTarget()
31 31
 
32
-    # Loading fonts and graphics takes a while.
33
-    # So show a splash screen while the user waits.
34
-    splash = SplashScreen(t)
35
-    t.loop_start()
36
-    splash.draw()
37
-    t.loop_end()
32
+# Loading fonts and graphics takes a while.
33
+# So show a splash screen while the user waits.
34
+splash = SplashScreen(t)
35
+t.loop_start()
36
+splash.draw()
37
+t.loop_end()
38 38
 
39
-    # UbaBot is online
40
-    success = Manager(t)
41
-    success.add(ScrollText(t, "Visit UbaBot Cocktail machine at FruBar village for drinks!", "bitmap8", 1, 10, camp_green))
42
-    success.add(Solid(t, 1.0))
43
-    success.add(QRScreen(t, qr_data, 30.0, "Drinks", "bitmap6", camp_pink, (0, 0, 0)))
44
-    success.add(Solid(t, 1.0))
39
+# UbaBot is online
40
+success = Manager(t)
41
+success.add(ScrollText(t, "Visit UbaBot Cocktail machine at FruBar village for drinks!", "bitmap8", 1, 10, camp_green))
42
+success.add(Solid(t, 1.0))
43
+success.add(QRScreen(t, qr_data, 30.0, "Drinks", "bitmap6", camp_pink, (0, 0, 0)))
44
+success.add(Solid(t, 1.0))
45 45
 
46
-    # UbaBot is offline
47
-    fail = Manager(t)
48
-    fail.add(ScrollText(t, "#CCCAMP23", "bitmap8", 1, 10, camp_green))
49
-    fail.add(Solid(t, 1.0))
50
-    fail.add(ScrollText(t, "The UbaBot Cocktail machine is closed. Please come back later!", "bitmap8", 1, 10, camp_green))
51
-    fail.add(Solid(t, 1.0))
52
-    fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
53
-    fail.add(Solid(t, 1.0))
54
-    fail.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "bitmap8", 1, 10, camp_green))
55
-    fail.add(Solid(t, 1.0))
46
+# UbaBot is offline
47
+fail = Manager(t)
48
+fail.add(ScrollText(t, "#CCCAMP23", "bitmap8", 1, 10, camp_green))
49
+fail.add(Solid(t, 1.0))
50
+fail.add(ScrollText(t, "The UbaBot Cocktail machine is closed. Please come back later!", "bitmap8", 1, 10, camp_green))
51
+fail.add(Solid(t, 1.0))
52
+fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
53
+fail.add(Solid(t, 1.0))
54
+fail.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "bitmap8", 1, 10, camp_green))
55
+fail.add(Solid(t, 1.0))
56 56
 
57
-    # UbaBot status checker
58
-    d = CheckHTTP(url)
59
-    d.success(success)
60
-    d.fail(fail)
57
+# UbaBot status checker
58
+d = CheckHTTP(url)
59
+d.success(success)
60
+d.fail(fail)
61 61
 
62
-    # Main "Menu"
63
-    m = Manager(t)
64
-    m.add(QRScreen(t, img_data, 15.0))
65
-    m.add(Solid(t, 1.0))
66
-    m.add(d) # HTTP Check, either "success" or "fail"
67
-    m.add(Solid(t, 1.0))
68
-    m.add(PicoBatt(t, 5.0, 5.0))
69
-    m.add(Solid(t, 1.0))
62
+# Main "Menu"
63
+m = Manager(t)
64
+m.add(QRScreen(t, img_data, 15.0))
65
+m.add(Solid(t, 1.0))
66
+m.add(d) # HTTP Check, either "success" or "fail"
67
+m.add(Solid(t, 1.0))
68
+m.add(PicoBatt(t, 5.0, 5.0))
69
+m.add(Solid(t, 1.0))
70 70
 
71
-    m.restart()
72
-    t.loop(m.draw)
71
+m.restart()
72
+t.loop(m.draw)

+ 53
- 52
camp_small.py View File

@@ -10,63 +10,64 @@
10 10
 camp_pink = (251, 72, 196)
11 11
 camp_green = (63, 255, 33)
12 12
 
13
-if __name__ == "__main__":
14
-    from splash import SplashScreen
15
-    from scroll import ScrollText
16
-    from solid import Solid
17
-    from life import GameOfLife
18
-    from net import CheckHTTP
19
-    from image import ImageScreen
20
-    from qr import QRScreen
21
-    from snake import Snake
22
-    from gamepad import InputWrapper
23
-    from manager import Manager
13
+from splash import SplashScreen
14
+from scroll import ScrollText
15
+from solid import Solid
16
+from life import GameOfLife
17
+from net import CheckHTTP
18
+from image import ImageScreen
19
+from qr import QRScreen
20
+from snake import Snake
21
+from gamepad import InputWrapper
22
+from manager import Manager
23
+import util
24 24
 
25
-    url = "http://ubabot.frubar.net"
25
+url = "http://ubabot.frubar.net"
26 26
 
27
-    import util
28
-    t = util.getTarget()
29
-    i = InputWrapper()
27
+# Need to import InputWrapper before initializing RGB Matrix on Pi
28
+i = InputWrapper()
30 29
 
31
-    # Loading fonts and graphics takes a while.
32
-    # So show a splash screen while the user waits.
33
-    splash = SplashScreen(t)
34
-    t.loop_start()
35
-    splash.draw()
36
-    t.loop_end()
30
+t = util.getTarget()
37 31
 
38
-    # UbaBot is online
39
-    success = Manager(t)
40
-    success.add(ImageScreen(t, "drinka.gif", 0.2, 2, 20.0))
41
-    success.add(Solid(t, 1.0))
42
-    success.add(QRScreen(t, url, 30.0, "Drinks:", "tom-thumb", (255, 255, 255), (0, 0, 0)))
43
-    success.add(Solid(t, 1.0))
32
+# Loading fonts and graphics takes a while.
33
+# So show a splash screen while the user waits.
34
+splash = SplashScreen(t)
35
+t.loop_start()
36
+splash.draw()
37
+t.loop_end()
44 38
 
45
-    # UbaBot is offline
46
-    fail = Manager(t)
47
-    fail.add(ImageScreen(t, "attention.gif", 0.2, 2, 20.0, (0, 0, 0)))
48
-    fail.add(ScrollText(t, "The UbaBot Cocktail machine is currently closed. Please come back later for more drinks!", "lemon", 2, 75, camp_pink))
49
-    fail.add(Solid(t, 1.0))
50
-    fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
51
-    fail.add(Solid(t, 1.0))
39
+# UbaBot is online
40
+success = Manager(t)
41
+success.add(ImageScreen(t, "drinka.gif", 0.2, 2, 20.0))
42
+success.add(Solid(t, 1.0))
43
+success.add(QRScreen(t, url, 30.0, "Drinks:", "tom-thumb", (255, 255, 255), (0, 0, 0)))
44
+success.add(Solid(t, 1.0))
52 45
 
53
-    # UbaBot status checker
54
-    d = CheckHTTP(url)
55
-    d.success(success)
56
-    d.fail(fail)
46
+# UbaBot is offline
47
+fail = Manager(t)
48
+fail.add(ImageScreen(t, "attention.gif", 0.2, 2, 20.0, (0, 0, 0)))
49
+fail.add(ScrollText(t, "The UbaBot Cocktail machine is currently closed. Please come back later for more drinks!", "lemon", 2, 75, camp_pink))
50
+fail.add(Solid(t, 1.0))
51
+fail.add(GameOfLife(t, 20, (0, 255, 0), (0, 0, 0), None, 2.0))
52
+fail.add(Solid(t, 1.0))
57 53
 
58
-    # Main "Menu"
59
-    m = Manager(t)
60
-    m.add(ScrollText(t, "#CCCAMP23", "lemon", 1, 75, camp_green))
61
-    m.add(Solid(t, 1.0))
62
-    m.add(ImageScreen(t, "Favicon.png", 0, 1, 10.0))
63
-    m.add(Solid(t, 1.0))
64
-    m.add(d) # HTTP Check, either "success" or "fail"
65
-    m.add(Solid(t, 1.0))
66
-    m.add(Snake(t, i, camp_pink, camp_green))
67
-    m.add(Solid(t, 1.0))
68
-    m.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "iv18x16u", 2, 70, camp_green))
69
-    m.add(Solid(t, 1.0))
54
+# UbaBot status checker
55
+d = CheckHTTP(url)
56
+d.success(success)
57
+d.fail(fail)
70 58
 
71
-    m.restart()
72
-    t.loop(m.draw)
59
+# Main "Menu"
60
+m = Manager(t)
61
+m.add(ScrollText(t, "#CCCAMP23", "lemon", 1, 75, camp_green))
62
+m.add(Solid(t, 1.0))
63
+m.add(ImageScreen(t, "Favicon.png", 0, 1, 10.0))
64
+m.add(Solid(t, 1.0))
65
+m.add(d) # HTTP Check, either "success" or "fail"
66
+m.add(Solid(t, 1.0))
67
+m.add(Snake(t, i, camp_pink, camp_green))
68
+m.add(Solid(t, 1.0))
69
+m.add(ScrollText(t, "Your advertisement could appear here. Open a Pull Request on git.xythobuz.de/thomas/rgb-matrix-visualizer or send an e-mail to thomas@xythobuz.de", "iv18x16u", 2, 70, camp_green))
70
+m.add(Solid(t, 1.0))
71
+
72
+m.restart()
73
+t.loop(m.draw)

+ 61
- 29
gamepad.py View File

@@ -26,22 +26,28 @@ class InputWrapper:
26 26
             "b": False,
27 27
             "x": False,
28 28
             "y": False,
29
+            "l": False,
30
+            "r": False,
31
+            "start": False,
32
+            "select": False,
29 33
         }
30 34
 
31 35
         devices = [InputDevice(path) for path in list_devices()]
32 36
         for device in devices:
33
-            c = device.capabilities()
37
+            c = device.capabilities(absinfo=False)
34 38
             keep = False
35 39
 
36
-            # check for key events
37
-            if ecodes.EV_KEY in c:
40
+            # check for axis events
41
+            if ecodes.EV_ABS in c:
38 42
                 # check for gamepad
39
-                if ecodes.BTN_A in c[ecodes.EV_KEY]:
43
+                if ecodes.ABS_X in c[ecodes.EV_ABS]:
40 44
                     print("Gamepad detected: ", device.name)
41 45
                     keep = True
42 46
 
47
+            # check for key events
48
+            if ecodes.EV_KEY in c:
43 49
                 # check for arrow keys
44
-                elif ecodes.KEY_LEFT in c[ecodes.EV_KEY]:
50
+                if ecodes.KEY_LEFT in c[ecodes.EV_KEY]:
45 51
                     print("Keyboard detected:", device.name)
46 52
                     keep = True
47 53
 
@@ -56,31 +62,57 @@ class InputWrapper:
56 62
         for key, mask in self.selector.select(0):
57 63
             device = key.fileobj
58 64
             for event in device.read():
59
-                if event.type != ecodes.EV_KEY:
60
-                    continue
61
-
62
-                v = False
63
-                if event.value != 0:
64
-                    v = True
65
+                if event.type == ecodes.EV_KEY:
66
+                    v = False
67
+                    if event.value != 0:
68
+                        v = True
65 69
 
66
-                if (event.code == ecodes.KEY_LEFT) or (event.code == ecodes.BTN_WEST):
67
-                    self.keys["left"] = v
68
-                elif (event.code == ecodes.KEY_RIGHT) or (event.code == ecodes.BTN_EAST):
69
-                    self.keys["right"] = v
70
-                elif (event.code == ecodes.KEY_UP) or (event.code == ecodes.BTN_NORTH):
71
-                    self.keys["up"] = v
72
-                elif (event.code == ecodes.KEY_DOWN) or (event.code == ecodes.BTN_SOUTH):
73
-                    self.keys["down"] = v
74
-                elif (event.code == ecodes.KEY_SPACE) or (event.code == ecodes.BTN_A):
75
-                    self.keys["a"] = v
76
-                elif (event.code == ecodes.KEY_LEFTCTRL) or (event.code == ecodes.BTN_B):
77
-                    self.keys["b"] = v
78
-                elif (event.code == ecodes.KEY_LEFTALT) or (event.code == ecodes.BTN_X):
79
-                    self.keys["x"] = v
80
-                elif (event.code == ecodes.KEY_ENTER) or (event.code == ecodes.BTN_Y):
81
-                    self.keys["y"] = v
82
-                else:
83
-                    return self.keys
70
+                    if (event.code == ecodes.KEY_LEFT) or (event.code == ecodes.BTN_WEST):
71
+                        self.keys["left"] = v
72
+                    elif (event.code == ecodes.KEY_RIGHT) or (event.code == ecodes.BTN_EAST):
73
+                        self.keys["right"] = v
74
+                    elif (event.code == ecodes.KEY_UP) or (event.code == ecodes.BTN_NORTH):
75
+                        self.keys["up"] = v
76
+                    elif (event.code == ecodes.KEY_DOWN) or (event.code == ecodes.BTN_SOUTH):
77
+                        self.keys["down"] = v
78
+                    elif (event.code == ecodes.KEY_SPACE) or (event.code == ecodes.BTN_A) or (event.code == ecodes.BTN_THUMB):
79
+                        self.keys["a"] = v
80
+                    elif (event.code == ecodes.KEY_LEFTCTRL) or (event.code == ecodes.BTN_B) or (event.code == ecodes.BTN_THUMB2):
81
+                        self.keys["b"] = v
82
+                    elif (event.code == ecodes.KEY_LEFTALT) or (event.code == ecodes.BTN_X) or (event.code == ecodes.BTN_JOYSTICK) or (event.code == ecodes.BTN_TRIGGER):
83
+                        self.keys["x"] = v
84
+                    elif (event.code == ecodes.KEY_ENTER) or (event.code == ecodes.BTN_Y) or (event.code == ecodes.BTN_TOP):
85
+                        self.keys["y"] = v
86
+                    elif event.code == ecodes.BTN_PINKIE:
87
+                        self.keys["r"] = v
88
+                    elif event.code == ecodes.BTN_TOP2:
89
+                        self.keys["l"] = v
90
+                    elif event.code == ecodes.BTN_BASE4:
91
+                        self.keys["start"] = v
92
+                    elif event.code == ecodes.BTN_BASE3:
93
+                        self.keys["select"] = v
94
+                elif event.type == ecodes.EV_ABS:
95
+                    # TODO 8bit hardcoded
96
+                    if event.code == ecodes.ABS_X:
97
+                        if event.value > 200:
98
+                            self.keys["right"] = True
99
+                            self.keys["left"] = False
100
+                        elif event.value < 100:
101
+                            self.keys["right"] = False
102
+                            self.keys["left"] = True
103
+                        else:
104
+                            self.keys["right"] = False
105
+                            self.keys["left"] = False
106
+                    elif event.code == ecodes.ABS_Y:
107
+                        if event.value > 200:
108
+                            self.keys["down"] = True
109
+                            self.keys["up"] = False
110
+                        elif event.value < 100:
111
+                            self.keys["down"] = False
112
+                            self.keys["up"] = True
113
+                        else:
114
+                            self.keys["down"] = False
115
+                            self.keys["up"] = False
84 116
 
85 117
                 if verbose:
86 118
                     print(categorize(event), event)

+ 14
- 5
image.py View File

@@ -34,14 +34,23 @@ class ImageScreen:
34 34
 
35 35
         # automatically crop and scale large images
36 36
         if not self.image.is_animated and ((self.image.width > self.gui.width) or (self.image.height > self.gui.height)):
37
+            # crop to visible area
37 38
             self.image = self.image.crop(self.image.getbbox())
38 39
 
39
-            if util.isPi():
40
-                # TODO PIL version is too old on Pi
41
-                self.image = self.image.resize((self.gui.width, self.gui.height))
40
+            # keep the aspect ratio and fit within visible area
41
+            ratio = self.image.width / self.image.height
42
+            width = self.gui.width
43
+            height = self.gui.height
44
+            if width < height:
45
+                width = self.gui.width
46
+                height = int(width / ratio)
42 47
             else:
43
-                self.image = self.image.resize((self.gui.width, self.gui.height),
44
-                                            Image.Resampling.NEAREST)
48
+                height = self.gui.height
49
+                width = int(ratio * height)
50
+
51
+            # resize
52
+            self.image = self.image.resize((width, height),
53
+                                           Image.Resampling.NEAREST)
45 54
 
46 55
             # new image object is also missing these
47 56
             self.image.is_animated = False

+ 4
- 4
mapper.py View File

@@ -52,11 +52,11 @@ class MapperNull:
52 52
 # on hard-corded coordinate ranges.
53 53
 class MapperColorAdjust(MapperNull):
54 54
     def set_pixel(self, x, y, color):
55
-        # third panel from the left, with 64 <= x < 96,
55
+        # second panel from the left, with 32 <= x,
56 56
         # is "old" type with brighter LEDs.
57
-        # rest of panels to the left and right are less bright.
58
-        # so adjust brightness of inner panel rg channels down.
59
-        if (x >= (self.gui.panelW * 2)) and (x < (self.gui.panelW * 3)):
57
+        # rest of panels to the left are less bright.
58
+        # so adjust brightness of other panel rg channels down.
59
+        if x >= self.gui.panelW:
60 60
             color = (int(color[0] * 0.75), int(color[1] * 0.75), color[2])
61 61
 
62 62
         self.gui.set_pixel(x, y, color)

+ 1
- 1
pi.py View File

@@ -32,7 +32,7 @@ from rgbmatrix import RGBMatrix, RGBMatrixOptions
32 32
 from PIL import Image
33 33
 
34 34
 class PiMatrix:
35
-    def __init__(self, w = 32 * 4, h = 32, panelW = 32, panelH = 32):
35
+    def __init__(self, w = 32 * 2, h = 32, panelW = 32, panelH = 32):
36 36
         self.width = w # x-axis
37 37
         self.height = h # y-axis
38 38
 

+ 100
- 24
pico_ota.py View File

@@ -19,7 +19,7 @@ import os
19 19
 # to check if we're actually running on MicroPython
20 20
 on_pico = False
21 21
 try:
22
-    import uos
22
+    import machine
23 23
     on_pico = True
24 24
 except Exception as e:
25 25
     print()
@@ -37,11 +37,16 @@ class PicoOTA:
37 37
 
38 38
         self.get = None
39 39
         self.update_path = "."
40
+        self.exe_path = ""
41
+        self.version_file = "ota_version"
40 42
         self.blacklist = []
41 43
 
42 44
     def path(self, p):
43 45
         self.update_path = p
44 46
 
47
+    def exe(self, e):
48
+        self.exe_path = e
49
+
45 50
     def ignore(self, path):
46 51
         if not path in self.blacklist:
47 52
             self.blacklist.append(path)
@@ -67,6 +72,32 @@ class PicoOTA:
67 72
             print()
68 73
             return None
69 74
 
75
+    def get_stored_commit(self):
76
+        current = "unknown"
77
+        try:
78
+            f = open(os.path.join(self.update_path, self.version_file), "r")
79
+            current = f.readline().strip()
80
+            f.close()
81
+        except Exception as e:
82
+            print()
83
+            if hasattr(sys, "print_exception"):
84
+                sys.print_exception(e)
85
+            else:
86
+                print(e)
87
+            print()
88
+        return current
89
+
90
+    def get_previous_commit(self, commit):
91
+        r = self.fetch(self.host + "/" + self.repo + "/commit/" + commit).text
92
+        for line in r.splitlines():
93
+            if not (self.repo + "/commit/") in line:
94
+                continue
95
+
96
+            line = line[line.find("/commit/") : ][8 : ][ : 40]
97
+            if line != commit:
98
+                return line
99
+        return "unknown"
100
+
70 101
     def check(self, verbose = False):
71 102
         if self.branch == None:
72 103
             # get default branch
@@ -83,18 +114,7 @@ class PicoOTA:
83 114
         if verbose:
84 115
             print("Latest commit is " + commit)
85 116
 
86
-        current = ""
87
-        try:
88
-            f = open(os.path.join(self.update_path, "ota_version"), "r")
89
-            current = f.readline().strip()
90
-            f.close()
91
-        except Exception as e:
92
-            print()
93
-            if hasattr(sys, "print_exception"):
94
-                sys.print_exception(e)
95
-            else:
96
-                print(e)
97
-            print()
117
+        current = self.get_stored_commit()
98 118
 
99 119
         if verbose:
100 120
             if current != commit:
@@ -132,15 +152,73 @@ class PicoOTA:
132 152
                 print("Writing " + f["path"] + " to " + self.update_path)
133 153
 
134 154
             # overwrite existing file
135
-            f = open(os.path.join(self.update_path, f["path"]), "w")
136
-            f.write(r)
137
-            f.close()
155
+            fo = open(os.path.join(self.update_path, f["path"]), "w")
156
+            fo.write(r)
157
+            fo.close()
158
+
159
+            if f["path"] == self.exe_path:
160
+                if verbose:
161
+                    print("Writing " + f["path"] + " to main.py")
162
+
163
+                fo = open(os.path.join(self.update_path, "main.py"), "w")
164
+                fo.write(r)
165
+                fo.close()
138 166
 
139 167
         # Write new commit id to local file
140
-        f = open(os.path.join(self.update_path, "ota_version"), "w")
168
+        f = open(os.path.join(self.update_path, self.version_file), "w")
141 169
         f.write(commit + os.linesep)
142 170
         f.close()
143 171
 
172
+def non_pico_ota_test(ota):
173
+    if not os.path.exists("tmp"):
174
+        os.makedirs("tmp")
175
+    ota.path("tmp")
176
+
177
+    print("Checking for updates")
178
+    newer, commit = ota.check(True)
179
+    print()
180
+
181
+    # Just for testing
182
+    previous = ota.get_previous_commit(commit)
183
+    print("Previous commit (-1):", previous)
184
+    previous = ota.get_previous_commit(previous)
185
+    print("Previous commit (-2):", previous)
186
+    print()
187
+
188
+    if newer:
189
+        print("Updating")
190
+        ota.update_to_commit(commit, True)
191
+    else:
192
+        print("No update required")
193
+
194
+def pico_ota_run(ota):
195
+    newer, commit = ota.check(True)
196
+
197
+    if newer:
198
+        ota.update_to_commit(commit, True)
199
+        machine.soft_reset()
200
+
201
+    fallback = False
202
+
203
+    try:
204
+        import camp_pico
205
+    except Exception as e:
206
+        fallback = True
207
+        print()
208
+        if hasattr(sys, "print_exception"):
209
+            sys.print_exception(e)
210
+        else:
211
+            print(e)
212
+        print()
213
+
214
+    # TODO this would immediately cause another update on reboot
215
+    # TODO set a flag to prevent updates after fallbacks?
216
+    # TODO or better, blacklist failed commit_id!
217
+    #if fallback:
218
+    #    previous = ota.get_previous_commit(commit, True)
219
+    #    ota.update_to_commit(previous, True)
220
+    #    machine.soft_reset()
221
+
144 222
 if __name__ == "__main__":
145 223
     ota = PicoOTA("https://git.xythobuz.de", "thomas/rgb-matrix-visualizer")
146 224
 
@@ -157,11 +235,9 @@ if __name__ == "__main__":
157 235
     ota.ignore("pi.py")
158 236
     ota.ignore("test.py")
159 237
 
160
-    if not on_pico:
161
-        if not os.path.exists("tmp"):
162
-            os.makedirs("tmp")
163
-        ota.path("tmp")
238
+    ota.exe("camp_pico.py")
164 239
 
165
-    newer, commit = ota.check(True)
166
-    if newer:
167
-        ota.update_to_commit(commit, True)
240
+    if not on_pico:
241
+        non_pico_ota_test(ota)
242
+    else:
243
+        pico_ota_run(ota)

+ 26
- 13
snake.py View File

@@ -41,6 +41,13 @@ class Snake:
41 41
         self.player = [ (int(self.gui.width / 2), int(self.gui.height / 2)) ]
42 42
         self.data[self.player[0][0]][self.player[0][1]] = 1
43 43
 
44
+        self.old_keys = {
45
+            "left": False,
46
+            "right": False,
47
+            "up": False,
48
+            "down": False,
49
+        }
50
+
44 51
         self.placeDot()
45 52
 
46 53
     def finished(self):
@@ -63,14 +70,19 @@ class Snake:
63 70
 
64 71
     def buttons(self):
65 72
         keys = self.input.get()
66
-        if keys["left"]:
73
+
74
+        if keys["left"] and (not self.old_keys["left"]):
67 75
             self.directionTmp = "l"
68
-        elif keys["right"]:
76
+        elif keys["right"] and (not self.old_keys["right"]):
69 77
             self.directionTmp = "r"
70
-        elif keys["up"]:
78
+        elif keys["up"] and (not self.old_keys["up"]):
71 79
             self.directionTmp = "u"
72
-        elif keys["down"]:
80
+        elif keys["down"] and (not self.old_keys["down"]):
73 81
             self.directionTmp = "d"
82
+        elif (keys["select"] and keys["start"] and (not self.old_keys["start"])) or (keys["start"] and keys["select"] and (not self.old_keys["select"])):
83
+            self.restart()
84
+
85
+        self.old_keys = keys.copy()
74 86
 
75 87
     def step(self):
76 88
         player = self.player[len(self.player) - 1]
@@ -122,6 +134,12 @@ class Snake:
122 134
         self.scoreText.draw()
123 135
 
124 136
     def draw(self):
137
+        if self.input != None:
138
+            self.buttons()
139
+        else:
140
+            # TODO "AI"
141
+            pass
142
+
125 143
         if self.direction == "":
126 144
             if self.finishedEndScreen():
127 145
                 self.drawScoreScreen()
@@ -130,12 +148,6 @@ class Snake:
130 148
                 self.scoreText.restart()
131 149
             return
132 150
 
133
-        if self.input != None:
134
-            self.buttons()
135
-        else:
136
-            # TODO "AI"
137
-            pass
138
-
139 151
         now = time.time()
140 152
         if (now - self.last) >= self.timestep:
141 153
             self.last = now
@@ -158,11 +170,12 @@ class Snake:
158 170
                 self.gui.set_pixel(x, y, self.colors[self.data[x][y]])
159 171
 
160 172
 if __name__ == "__main__":
161
-    import util
162
-    t = util.getTarget()
163
-
173
+    # Need to import InputWrapper before initializing RGB Matrix on Pi
164 174
     from gamepad import InputWrapper
165 175
     i = InputWrapper()
166 176
 
177
+    import util
178
+    t = util.getTarget()
179
+
167 180
     d = Snake(t, i)
168 181
     t.loop(d.draw)

+ 1
- 1
solid.py View File

@@ -57,7 +57,7 @@ if __name__ == "__main__":
57 57
         global s, colors, ci, n, c
58 58
 
59 59
         now = time.time()
60
-        if ((now - s) >= 0.1) or (now < s):
60
+        if ((now - s) >= 0.5) or (now < s):
61 61
             s = now
62 62
             n += 1
63 63
             if n >= 15:

+ 87
- 23
tetris.py View File

@@ -12,11 +12,16 @@ import time
12 12
 import random
13 13
 
14 14
 class Tetris:
15
-    def __init__(self, g, i, ts = 0.5, to = 60.0):
15
+    def __init__(self, g, i, ts = 0.5, to = 60.0, w = 10, h = 22):
16 16
         self.gui = g
17 17
         self.input = i
18 18
         self.timestep = ts
19 19
         self.timeout = to
20
+        self.width = min(w, self.gui.width)
21
+        self.height = min(h, self.gui.height)
22
+
23
+        self.x_off = int((self.gui.panelW - self.width - 2) / 2)
24
+        self.y_off = int((self.gui.panelH - self.height - 2) / 2)
20 25
 
21 26
         self.endText = ScrollText(self.gui, "Game Over!", "uushi",
22 27
                                    2, 75, (251, 72, 196))
@@ -94,8 +99,14 @@ class Tetris:
94 99
         self.button = None
95 100
         self.score = 0
96 101
         self.done = False
97
-        self.data = [[self.bg for y in range(self.gui.height)] for x in range(self.gui.width)]
102
+        self.data = [[self.bg for y in range(self.height)] for x in range(self.width)]
98 103
         self.piece = None
104
+        self.old_keys = {
105
+            "left": False,
106
+            "right": False,
107
+            "up": False,
108
+            "down": False,
109
+        }
99 110
 
100 111
     def finished(self):
101 112
         if self.input == None:
@@ -109,9 +120,6 @@ class Tetris:
109 120
 
110 121
         return False
111 122
 
112
-    # TODO collision detection is broken
113
-    # TODO not working for pixels outside of vertical center line???
114
-    # TODO something like that...
115 123
     def collision(self):
116 124
         # check for collision of piece with data
117 125
         pos = (self.piece[2], self.piece[3])
@@ -122,7 +130,11 @@ class Tetris:
122 130
                     continue
123 131
 
124 132
                 # check for collision with bottom wall
125
-                if (y + pos[1]) >= self.gui.height:
133
+                if (y + pos[1]) >= self.height:
134
+                    return True
135
+
136
+                # check for collision with right wall
137
+                if (x + pos[0]) >= self.width:
126 138
                     return True
127 139
 
128 140
                 # check for collision with previous pieces
@@ -142,15 +154,49 @@ class Tetris:
142 154
 
143 155
         for y in range(0, len(piece)):
144 156
             for x in range(0, len(piece[y])):
145
-                remove = False
157
+                # only set or clear where piece actually is
146 158
                 if piece[y][x] == 0:
147
-                    remove = True
159
+                    continue
148 160
 
149
-                if clear or remove:
161
+                if clear:
150 162
                     self.data[x + position[0]][y + position[1]] = self.bg
151 163
                 else:
152 164
                     self.data[x + position[0]][y + position[1]] = self.piece[1]
153 165
 
166
+    def checkWin(self):
167
+        had_data = False
168
+        for y in range(0, self.height):
169
+            line_full = True
170
+            for x in range(0, self.width):
171
+                if self.data[x][y] == self.bg:
172
+                    line_full = False
173
+                else:
174
+                    had_data = True
175
+
176
+            if had_data and line_full:
177
+                self.score += 1
178
+
179
+                # move stuff above into this line
180
+                for y2 in reversed(range(1, y + 1)):
181
+                    for x in range(0, self.width):
182
+                        self.data[x][y2] = self.data[x][y2 - 1]
183
+
184
+                # clear out top line
185
+                for x in range(0, self.width):
186
+                    self.data[x][0] = self.bg
187
+
188
+        # check for complete win
189
+        board_clear = (self.piece == None)
190
+        for y in range(0, self.height):
191
+            for x in range(0, self.width):
192
+                if self.data[x][y] != self.bg:
193
+                    board_clear = False
194
+                if board_clear == False:
195
+                    break
196
+            if board_clear == False:
197
+                break
198
+        return board_clear
199
+
154 200
     def step(self):
155 201
         if self.piece == None:
156 202
             justPlaced = True
@@ -164,7 +210,7 @@ class Tetris:
164 210
             ]
165 211
 
166 212
             # center the piece on top of the playing board
167
-            self.piece[2] = int((self.gui.width - len(self.piece[0][0])) / 2)
213
+            self.piece[2] = int((self.width - len(self.piece[0][0])) / 2)
168 214
 
169 215
             if self.collision():
170 216
                 # new piece immediately collided. game over!
@@ -188,7 +234,7 @@ class Tetris:
188 234
             if self.piece[2] > 0:
189 235
                 self.piece[2] -= 1
190 236
         elif self.button == "r":
191
-            if self.piece[2] < (self.gui.width - 1):
237
+            if self.piece[2] < (self.width - 1):
192 238
                 self.piece[2] += 1
193 239
         else:
194 240
             # one pixel down
@@ -205,9 +251,13 @@ class Tetris:
205 251
             self.piece[2] = oldPosition[0]
206 252
             self.piece[3] = oldPosition[1]
207 253
 
208
-            if (self.button != "l") and (self.button != "r"):
254
+            if (self.button != "l") and (self.button != "r") and (self.button != "u"):
209 255
                 # but only stop playing it if it was moving down
210 256
                 self.piece = None
257
+
258
+                # check for cleared line
259
+                if self.checkWin():
260
+                    return False
211 261
         else:
212 262
             # copy piece at new location into buffer
213 263
             self.put(False)
@@ -219,14 +269,19 @@ class Tetris:
219 269
 
220 270
     def buttons(self):
221 271
         keys = self.input.get()
222
-        if keys["left"]:
272
+
273
+        if keys["left"] and (not self.old_keys["left"]):
223 274
             self.button = "l"
224
-        elif keys["right"]:
275
+        elif keys["right"] and (not self.old_keys["right"]):
225 276
             self.button = "r"
226
-        elif keys["up"]:
277
+        elif keys["up"] and (not self.old_keys["up"]):
227 278
             self.button = "u"
228 279
         elif keys["down"]:
229 280
             self.button = "d"
281
+        elif (keys["select"] and keys["start"] and (not self.old_keys["start"])) or (keys["start"] and keys["select"] and (not self.old_keys["select"])):
282
+            self.restart()
283
+
284
+        self.old_keys = keys.copy()
230 285
 
231 286
     def draw(self):
232 287
         if self.done:
@@ -245,23 +300,32 @@ class Tetris:
245 300
 
246 301
         now = time.time()
247 302
         if (self.button != None) or ((now - self.last) >= self.timestep) or (now < self.last):
248
-            self.last = now
303
+            # don't let user stop falling pieces by moving/rotating endlessly
304
+            if (self.button != "l") and (self.button != "r") and (self.button != "u"):
305
+                self.last = now
306
+
249 307
             cont = self.step()
250 308
             if cont == False:
251 309
                 self.done = True
252 310
                 self.scoreText.setText("Score: " + str(self.score), "uushi")
253 311
                 self.endText.restart()
254 312
 
255
-        for x in range(0, self.gui.width):
256
-            for y in range(0, self.gui.height):
257
-                self.gui.set_pixel(x, y, self.data[x][y])
313
+        # TODO placement of play area
314
+        for x in range(-1, self.width + 1):
315
+            for y in range(-1, self.height + 1):
316
+                c = (255, 255, 255)
317
+                if (x >= 0) and (y >= 0) and (x < self.width) and (y < self.height):
318
+                    c = self.data[x][y]
258 319
 
259
-if __name__ == "__main__":
260
-    import util
261
-    t = util.getTarget()
320
+                self.gui.set_pixel(x + 1 + self.x_off, y + 1 + self.y_off, c)
262 321
 
322
+if __name__ == "__main__":
323
+    # Need to import InputWrapper before initializing RGB Matrix on Pi
263 324
     from gamepad import InputWrapper
264 325
     i = InputWrapper()
265 326
 
266
-    d = Tetris(t, i, 0.1)
327
+    import util
328
+    t = util.getTarget()
329
+
330
+    d = Tetris(t, i)
267 331
     t.loop(d.draw)

+ 12
- 2
util.py View File

@@ -9,6 +9,7 @@
9 9
 
10 10
 import sys
11 11
 
12
+cachedTarget = None
12 13
 targetPlatform = None
13 14
 wifiConnected = False
14 15
 
@@ -29,7 +30,10 @@ def isPico():
29 30
     return targetPlatform == "pico"
30 31
 
31 32
 def getTarget():
32
-    global targetPlatform
33
+    global targetPlatform, cachedTarget
34
+
35
+    if cachedTarget != None:
36
+        return cachedTarget
33 37
 
34 38
     target = None
35 39
     try:
@@ -40,13 +44,16 @@ def getTarget():
40 44
         # TODO hard-coded adjustments
41 45
         from mapper import MapperColorAdjust, MapperStripToRect
42 46
         col = MapperColorAdjust(pi)
43
-        target = MapperStripToRect(col)
47
+        #target = MapperStripToRect(col)
48
+        target = col
44 49
 
45 50
         if targetPlatform == None:
46 51
             # only print once
47 52
             print("Raspberry Pi Adafruit RGB LED Matrix detected")
48 53
         targetPlatform = "pi"
49 54
     except Exception as e:
55
+        target = None
56
+
50 57
         print()
51 58
         if hasattr(sys, "print_exception"):
52 59
             sys.print_exception(e)
@@ -68,6 +75,8 @@ def getTarget():
68 75
                 print("Raspberry Pi Pico Interstate75 RGB LED Matrix detected")
69 76
             targetPlatform = "pico"
70 77
         except Exception as e:
78
+            target = None
79
+
71 80
             print()
72 81
             if hasattr(sys, "print_exception"):
73 82
                 sys.print_exception(e)
@@ -84,6 +93,7 @@ def getTarget():
84 93
                 print("Falling back to GUI debug interface")
85 94
             targetPlatform = "tk"
86 95
 
96
+    cachedTarget = target
87 97
     return target
88 98
 
89 99
 # https://github.com/raspberrypi/pico-examples/blob/master/pico_w/wifi/python_test_tcp/micropython_test_tcp_client.py

Loading…
Cancel
Save