Selaa lähdekoodia

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

Thomas Buck 11 kuukautta sitten
vanhempi
commit
ea9b0d94ff
11 muutettua tiedostoa jossa 407 lisäystä ja 202 poistoa
  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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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…
Peruuta
Tallenna