bitsy.nv
· 14 KiB · Text
Brut
Playground
|| :\: OH, btw, the text stuff assumes you have this font installed
:\: managore.itch.io/m3x6
|:: get color for .|
:color: 51 68 85
|:: get color for #|
:color: 255 187 51
|:: get sprite 1 for .|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
)
|:: get sprite 1 for p|
:sprite data: (
. # . # # # # .
. # # # # # . .
. . . . . . . .
. . # # # # . .
. # . # # . # .
. . # . . # . .
. . # . . # . .
. . # . . # . .
)
|:: is p actor| :actor: p yes
|:: get sprite 2 for p|
:sprite data: (
# . # # # # . .
# # # # # . . .
. . . . . . . .
. . # # # # . .
. # . # # . # .
. . # . . # . .
. . # . . # . .
. . # . . # . .
)
|:: is c actor| :actor: c yes
|:: touched c :angy:?|
|:: touched c :already pet:| :angy:
|:: touched c :touched once:| :already pet:
|:: touched c| :touched once:
|:: get dialogue for c :angy:? :current room: test2?|
:@text to draw: "still agny!!"
|:: get dialogue for c :angy:?|
:@text to draw: "no talk, me angy."
|:: get dialogue for c :already pet:?|
:@text to draw: "okay, enough."
|:: get dialogue for c| :@text to draw: "meow! meow!"
|:: get sprite 1 for c :angy:? :current room: test2?|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . # . #
. . . . . # # #
. . . . . # # #
. . . . # # # .
# . . # # # # .
. # # # # . # .
)
|:: get sprite 2 for c :angy:? :current room: test2?|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . # . #
. . . . . # # #
. . . . . # # #
. . . . # # # .
. # . # # # # .
. # # # # . # .
)
|:: get sprite 1 for c :angy:?|
:sprite data: (
. . . . . . . .
. . . . . . . .
# . # . . . . .
# # # . . . . .
# # # . . . . .
. # # # . . . .
. # # # # . . #
. # . # # # # .
)
|:: get sprite 2 for c :angy:?|
:sprite data: (
. . . . . . . .
. . . . . . . .
# . # . . . . .
# # # . . . . .
# # # . . . . .
. # # # . . . .
. # # # # . # .
. # . # # # # .
)
|:: get sprite 1 for c|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . # . #
. . . . . # # #
. # . . . # # #
. # . # # # # .
. . # # # # # .
. . . # # . # .
)
|:: get sprite 2 for c|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . # . #
. . . . . # # #
. . . . . # # #
# . . # # # # .
. # # # # # # .
. . . # # . # .
)
|:: is w solid| :solid: w yes
|:: get sprite 1 for w|
:sprite data: (
# # # # # # # #
# # . . . . # #
# . # # # # . #
# . # . . # . #
# . # . . # . #
# . # # # # . #
# # . . . . # #
# # # # # # # #
)
|:: is [ solid| :solid: [ yes
|:: get sprite 1 for [|
:sprite data: (
# # # # # # # #
# # . . . . . .
# . # # # # # #
# . # . . . . .
# . # . . . . .
# . # # # # # #
# # . . . . . .
# # # # # # # #
)
|:: is = solid| :solid: = yes
|:: get sprite 1 for =|
:sprite data: (
# # # # # # # #
. . . . . . . .
# # # # # # # #
. . . . . . . .
. . . . . . . .
# # # # # # # #
. . . . . . . .
# # # # # # # #
)
|:: is ] solid| :solid: ] yes
|:: get sprite 1 for ]|
:sprite data: (
# # # # # # # #
. . . . . . # #
# # # # # # . #
. . . . . # . #
. . . . . # . #
# # # # # # . #
. . . . . . # #
# # # # # # # #
)
|:: get sprite 1 for g|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . # . . . . .
. . # . . # . .
. . # . . # . .
)
|:: get sprite 2 for g|
:sprite data: (
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. # . . . . . .
. # . . # . . .
. . # . . # . .
)
|:: is e exit| :exit: e yes
|:: is x exit| :exit: x yes
|:: is i exit| :exit: i yes
|:: is t exit| :exit: t yes
|:: e goes to :current room: test2?| :go to: test
|:: e goes to| :go to: test2
|:: x goes to| :: e goes to
|:: i goes to| :: e goes to
|:: t goes to| :: e goes to
|:: get sprite 1 for e :current room: test2?|
:sprite data: (
. . . . . . . .
. . . # . . . .
. . # # . . . .
. # # # # # # .
. # # # # # # .
. . # # . . . .
. . . # . . . .
. . . . . . . .
)
|:: get sprite 1 for e |
:sprite data: (
. . . . . . . .
. . . . # . . .
. . . . # # . .
. # # # # # # .
. # # # # # # .
. . . . # # . .
. . . . # . . .
. . . . . . . .
)
|:: get sprite 1 for x| :: get sprite 1 for e
|:: get sprite 1 for i| :: get sprite 1 for e
|:: get sprite 1 for t| :: get sprite 1 for e
|| :starting room: test
|:: player for test room| :player: p
|:: get tiles for test|
:room data: (
w w . . . . w w
w . . . . g . w
. . [ = = ] . e
. c . g . . p x
. . . . . . . i
. g [ = = ] . t
w . . . . g . w
w w . . . . w w
)
|:: player for test2 room| :player: p
|:: get tiles for test2|
:room data: (
w w w w w w w w
w . . . g . . w
e . w . . w . w
x p . . . c . w
i . . g . . g w
t . w . . w . w
w . . . g . . w
w w w w w w w w
)
|| :screen resolution: 64 64
:frame for sprites: 1
|:: start? :starting room: $room|
:: change rooms :go to: $room
|:: change rooms? :actors: $1 $2 $3|
|:: change rooms? :solids: $1 $2 $3|
|:: change rooms? :exits: $1 $2 $3|
|:: change rooms? :player actor: $1 $2 $3|
|:: change rooms? :current room: $_|
|:: change rooms :go to: $room|
:current room: $room
:: player for $room room
:: get actor locations
:: set player actor
|:: get actor locations :current room: $room?|
:: get tiles for $room :: find each actor :tile location: 0 0
:: get tiles for $room :: find each solid :tile location: 0 0
:: get tiles for $room :: find each exit :tile location: 0 0
|:: find each $type :room data: ($1 $2 $3 $4 $5 $6 $7 $8)|
:: is $1 $type :: add to $type list :: next tile column
:: is $2 $type :: add to $type list :: next tile column
:: is $3 $type :: add to $type list :: next tile column
:: is $4 $type :: add to $type list :: next tile column
:: is $5 $type :: add to $type list :: next tile column
:: is $6 $type :: add to $type list :: next tile column
:: is $7 $type :: add to $type list :: next tile column
:: is $8 $type :: add to $type list :: next tile row
:: find each $type
|:: find each $type :tile location: $x $y|
|:: add to actor list :actor: $who yes :tile location: $x $y?|
:actors: $who $x $y
|:: add to actor list :actor: $who no|
|:: add to solid list :solid: $what yes :tile location: $x $y?|
:solids: $what $x $y
|:: add to solid list :solid: $what no|
|:: add to exit list :exit: $what yes :tile location: $x $y?|
:exits: $what $x $y
|:: add to exit list :exit: $what no|
|:: set player actor :player: $actor :actors: $actor $x $y|
:: rollback actors :player actor: $actor $x $y
|:: set player actor :player: $actor :no more actors:|
|:: set player actor?|
:: next actor
|::start :screen resolution: $x $y?|
:@graphics: set-resolution fullscreen $x $y
::main loop
|:: draw? :go to: $room?|
:: change rooms
|:: draw|
:: move player actor
:: clear the screen
:: draw current room
:: draw npcs
:: draw player
:: get dialogue :: draw dialogue
:: tick frame
|:: get dialogue :player touching: $something?|
:: get dialogue for $something
|:: get dialogue|
|:: draw dialogue :@text to draw: $something?|
:@graphics: draw-text 0 48
|:: draw dialogue :no dialogue:|
|:: draw dialogue|
|:: is $x actor| :actor: $x no
|:: is $x solid| :solid: $x no
|:: is $x exit| :exit: $x no
|:: get dialogue for $what| :no dialogue:
|:: get sprite $frame for $id|
:: get sprite 1 for $id
|:: next actor :actors: $actor $x $y :actors: $more $_x $_y?|
:discarded actors: $actor $x $y
|:: next actor :actors: $actor $x $y|
:discarded actors: $actor $x $y
:no more actors:
|:: next actor|
:no more actors:
|:: rollback actors :discarded actors: $actor $x $y|
:actors: $actor $x $y
|:: rollback actors|
|:: next solid :solids: $actor $x $y :solids: $more $_x $_y?|
:discarded solids: $actor $x $y
|:: next solid :solids: $actor $x $y|
:discarded solids: $actor $x $y
:no more solids:
|:: next solid|
:no more solids:
|:: rollback solids? :discarded solids: $solid $x $y|
:solids: $solid $x $y
|:: rollback solids|
|:: next exit :exits: $exit $x $y :exits: $more $_x $_y?|
:discarded exits: $exit $x $y
|:: next exit :exits: $exit $x $y|
:discarded exits: $exit $x $y
:no more exits:
|:: next exit|
:no more exits:
|:: rollback exits? :discarded exits: $exit $x $y|
:exits: $exit $x $y
|:: rollback exits|
|:: draw current room :current room: $room?|
:tile location: 0 0
:: get tiles for $room
:: draw room
|:: draw room :room data: ($1 $2 $3 $4 $5 $6 $7 $8)|
:: render tile $1 to screen
:: render tile $2 to screen
:: render tile $3 to screen
:: render tile $4 to screen
:: render tile $5 to screen
:: render tile $6 to screen
:: render tile $7 to screen
:: render tile $8 to screen :: last column
:: draw room
|:: draw room :tile location: $x $y|
|:: render tile $id to screen :: last column|
:: is $id actor :: draw sprite $id :: next tile row
|:: render tile $id to screen|
:: is $id actor :: draw sprite $id :: next tile column
|:: draw npcs? :tile location: $x $y|
|:: draw npcs :no more actors:|
:: rollback actors
|:: draw npcs? :actors: $actor $x $y?|
:: draw sprite $actor :tile location: $x $y
:: next actor
|:: draw player :player actor: $actor $x $y?|
:: draw sprite $actor :tile location: $x $y
|:: draw sprite $actor :actor: $actor yes|
:: draw sprite .
|:: draw sprite $actor? :actor: $actor no|
|:: draw sprite $sprite :frame for sprites: $frame? :tile location: $x $y?|
:pixel location: $x $y
:: get sprite $frame for $sprite
:: draw sprite
|:: draw sprite :sprite data: ($1 $2 $3 $4 $5 $6 $7 $8)|
:: plot $1 :: next pixel column
:: plot $2 :: next pixel column
:: plot $3 :: next pixel column
:: plot $4 :: next pixel column
:: plot $5 :: next pixel column
:: plot $6 :: next pixel column
:: plot $7 :: next pixel column
:: plot $8 :: next pixel row
:: draw sprite
|:: draw sprite :pixel location: $x $y|
|:: plot . :pixel location: $x $y? :color: $r $g $b|
:@graphics: set-pixel $x $y $r $g $b
|:: plot # :pixel location: $x $y? :color: $r $g $b|
:@graphics: set-pixel $x $y $r $g $b
|:: plot $char?|
:: get color for $char
|:: next pixel column :@math: $x :pixel location: $_ $y|
:pixel location: $x $y
|:: next pixel column? :pixel location: $x $y?|
:@math: add $x 1
|:: next pixel row :@math: $y :pixel location: $_x $_y :tile location: $x $_?|
:pixel location: $x $y
|:: next pixel row? :pixel location: $x $y?|
:@math: add $y 1
|:: next tile column :@math: $x :tile location: $_ $y|
:tile location: $x $y
|:: next tile column? :tile location: $x $y?|
:@math: add $x 8
|:: next tile row :@math: $y :tile location: $x $_|
:tile location: 0 $y
|:: next tile row? :tile location: $x $y?|
:@math: add $y 8
|:: tick frame :frame for sprites: 1|
:frame for sprites: 2
|:: tick frame :frame for sprites: 2|
:frame for sprites: 1
|:: move player actor|
:: check if up is pressed :: move player
:: check if right is pressed :: move player
:: check if down is pressed :: move player
:: check if left is pressed :: move player
|:: move player :key pressed: up :player actor: $actor $x 0?|
|:: move player? :key pressed: up :player actor: $actor $x $y?|
:: move y :@math: subtract $y 8
|:: move player :key pressed: right :player actor: $actor 56 $y?|
|:: move player? :key pressed: right :player actor: $actor $x $y?|
:: move x :@math: add $x 8
|:: move player :key pressed: down :player actor: $actor $x 56?|
|:: move player? :key pressed: down :player actor: $actor $x $y?|
:: move y :@math: add $y 8
|:: move player :key pressed: left :player actor: $actor 0 $y?|
|:: move player? :key pressed: left :player actor: $actor $x $y?|
:: move x :@math: subtract $x 8
|:: move player|
|:: move x? :@math: $x :player actor: $actor $_ $y?|
:make move: $x $y
:: clear touching
:: check for solid $x $y
:: check for exit $x $y
:: check for actor $x $y
|:: move y? :@math: $y :player actor: $actor $x $_?|
:make move: $x $y
:: clear touching
:: check for solid $x $y
:: check for exit $x $y
:: check for actor $x $y
|:: move $axis :make move: $x $y :player actor: $actor $_x $_y|
:player actor: $actor $x $y
|:: move $axis|
|:: clear touching :player touching: $not-this|
|:: clear touching|
|:: check for solid $x $y :solids: $what $x $y? :make move: $x $y|
:: rollback solids
|:: check for solid $x $y :no more solids:|
:: rollback solids
|:: check for solid $x $y?|
:: next solid
|:: check for exit $x $y :exits: $what $x $y? :make move: $_x $_y|
:: rollback exits :: $what goes to
|:: check for exit $x $y :no more exits:|
:: rollback exits
|:: check for exit $x $y?|
:: next exit
|:: check for actor $x $y :actors: $what $x $y?|
:: rollback actors
:: touched $what
:player touching: $what
|:: check for actor $x $y :no more actors:|
:: rollback actors
|:: check for actor $x $y?|
:: next actor
| 1 | || :\: OH, btw, the text stuff assumes you have this font installed |
| 2 | :\: managore.itch.io/m3x6 |
| 3 | |
| 4 | |:: get color for .| |
| 5 | :color: 51 68 85 |
| 6 | |:: get color for #| |
| 7 | :color: 255 187 51 |
| 8 | |
| 9 | |:: get sprite 1 for .| |
| 10 | :sprite data: ( |
| 11 | . . . . . . . . |
| 12 | . . . . . . . . |
| 13 | . . . . . . . . |
| 14 | . . . . . . . . |
| 15 | . . . . . . . . |
| 16 | . . . . . . . . |
| 17 | . . . . . . . . |
| 18 | . . . . . . . . |
| 19 | ) |
| 20 | |
| 21 | |:: get sprite 1 for p| |
| 22 | :sprite data: ( |
| 23 | . # . # # # # . |
| 24 | . # # # # # . . |
| 25 | . . . . . . . . |
| 26 | . . # # # # . . |
| 27 | . # . # # . # . |
| 28 | . . # . . # . . |
| 29 | . . # . . # . . |
| 30 | . . # . . # . . |
| 31 | ) |
| 32 | |
| 33 | |:: is p actor| :actor: p yes |
| 34 | |:: get sprite 2 for p| |
| 35 | :sprite data: ( |
| 36 | # . # # # # . . |
| 37 | # # # # # . . . |
| 38 | . . . . . . . . |
| 39 | . . # # # # . . |
| 40 | . # . # # . # . |
| 41 | . . # . . # . . |
| 42 | . . # . . # . . |
| 43 | . . # . . # . . |
| 44 | ) |
| 45 | |
| 46 | |:: is c actor| :actor: c yes |
| 47 | |
| 48 | |:: touched c :angy:?| |
| 49 | |:: touched c :already pet:| :angy: |
| 50 | |:: touched c :touched once:| :already pet: |
| 51 | |:: touched c| :touched once: |
| 52 | |
| 53 | |:: get dialogue for c :angy:? :current room: test2?| |
| 54 | :@text to draw: "still agny!!" |
| 55 | |:: get dialogue for c :angy:?| |
| 56 | :@text to draw: "no talk, me angy." |
| 57 | |:: get dialogue for c :already pet:?| |
| 58 | :@text to draw: "okay, enough." |
| 59 | |:: get dialogue for c| :@text to draw: "meow! meow!" |
| 60 | |
| 61 | |:: get sprite 1 for c :angy:? :current room: test2?| |
| 62 | :sprite data: ( |
| 63 | . . . . . . . . |
| 64 | . . . . . . . . |
| 65 | . . . . . # . # |
| 66 | . . . . . # # # |
| 67 | . . . . . # # # |
| 68 | . . . . # # # . |
| 69 | # . . # # # # . |
| 70 | . # # # # . # . |
| 71 | ) |
| 72 | |:: get sprite 2 for c :angy:? :current room: test2?| |
| 73 | :sprite data: ( |
| 74 | . . . . . . . . |
| 75 | . . . . . . . . |
| 76 | . . . . . # . # |
| 77 | . . . . . # # # |
| 78 | . . . . . # # # |
| 79 | . . . . # # # . |
| 80 | . # . # # # # . |
| 81 | . # # # # . # . |
| 82 | ) |
| 83 | |
| 84 | |:: get sprite 1 for c :angy:?| |
| 85 | :sprite data: ( |
| 86 | . . . . . . . . |
| 87 | . . . . . . . . |
| 88 | # . # . . . . . |
| 89 | # # # . . . . . |
| 90 | # # # . . . . . |
| 91 | . # # # . . . . |
| 92 | . # # # # . . # |
| 93 | . # . # # # # . |
| 94 | ) |
| 95 | |:: get sprite 2 for c :angy:?| |
| 96 | :sprite data: ( |
| 97 | . . . . . . . . |
| 98 | . . . . . . . . |
| 99 | # . # . . . . . |
| 100 | # # # . . . . . |
| 101 | # # # . . . . . |
| 102 | . # # # . . . . |
| 103 | . # # # # . # . |
| 104 | . # . # # # # . |
| 105 | ) |
| 106 | |
| 107 | |:: get sprite 1 for c| |
| 108 | :sprite data: ( |
| 109 | . . . . . . . . |
| 110 | . . . . . . . . |
| 111 | . . . . . # . # |
| 112 | . . . . . # # # |
| 113 | . # . . . # # # |
| 114 | . # . # # # # . |
| 115 | . . # # # # # . |
| 116 | . . . # # . # . |
| 117 | ) |
| 118 | |:: get sprite 2 for c| |
| 119 | :sprite data: ( |
| 120 | . . . . . . . . |
| 121 | . . . . . . . . |
| 122 | . . . . . # . # |
| 123 | . . . . . # # # |
| 124 | . . . . . # # # |
| 125 | # . . # # # # . |
| 126 | . # # # # # # . |
| 127 | . . . # # . # . |
| 128 | ) |
| 129 | |
| 130 | |:: is w solid| :solid: w yes |
| 131 | |:: get sprite 1 for w| |
| 132 | :sprite data: ( |
| 133 | # # # # # # # # |
| 134 | # # . . . . # # |
| 135 | # . # # # # . # |
| 136 | # . # . . # . # |
| 137 | # . # . . # . # |
| 138 | # . # # # # . # |
| 139 | # # . . . . # # |
| 140 | # # # # # # # # |
| 141 | ) |
| 142 | |
| 143 | |:: is [ solid| :solid: [ yes |
| 144 | |:: get sprite 1 for [| |
| 145 | :sprite data: ( |
| 146 | # # # # # # # # |
| 147 | # # . . . . . . |
| 148 | # . # # # # # # |
| 149 | # . # . . . . . |
| 150 | # . # . . . . . |
| 151 | # . # # # # # # |
| 152 | # # . . . . . . |
| 153 | # # # # # # # # |
| 154 | ) |
| 155 | |
| 156 | |:: is = solid| :solid: = yes |
| 157 | |:: get sprite 1 for =| |
| 158 | :sprite data: ( |
| 159 | # # # # # # # # |
| 160 | . . . . . . . . |
| 161 | # # # # # # # # |
| 162 | . . . . . . . . |
| 163 | . . . . . . . . |
| 164 | # # # # # # # # |
| 165 | . . . . . . . . |
| 166 | # # # # # # # # |
| 167 | ) |
| 168 | |
| 169 | |:: is ] solid| :solid: ] yes |
| 170 | |:: get sprite 1 for ]| |
| 171 | :sprite data: ( |
| 172 | # # # # # # # # |
| 173 | . . . . . . # # |
| 174 | # # # # # # . # |
| 175 | . . . . . # . # |
| 176 | . . . . . # . # |
| 177 | # # # # # # . # |
| 178 | . . . . . . # # |
| 179 | # # # # # # # # |
| 180 | ) |
| 181 | |
| 182 | |
| 183 | |:: get sprite 1 for g| |
| 184 | :sprite data: ( |
| 185 | . . . . . . . . |
| 186 | . . . . . . . . |
| 187 | . . . . . . . . |
| 188 | . . . . . . . . |
| 189 | . . . . . . . . |
| 190 | . . # . . . . . |
| 191 | . . # . . # . . |
| 192 | . . # . . # . . |
| 193 | ) |
| 194 | |
| 195 | |:: get sprite 2 for g| |
| 196 | :sprite data: ( |
| 197 | . . . . . . . . |
| 198 | . . . . . . . . |
| 199 | . . . . . . . . |
| 200 | . . . . . . . . |
| 201 | . . . . . . . . |
| 202 | . # . . . . . . |
| 203 | . # . . # . . . |
| 204 | . . # . . # . . |
| 205 | ) |
| 206 | |
| 207 | |:: is e exit| :exit: e yes |
| 208 | |:: is x exit| :exit: x yes |
| 209 | |:: is i exit| :exit: i yes |
| 210 | |:: is t exit| :exit: t yes |
| 211 | |:: e goes to :current room: test2?| :go to: test |
| 212 | |:: e goes to| :go to: test2 |
| 213 | |:: x goes to| :: e goes to |
| 214 | |:: i goes to| :: e goes to |
| 215 | |:: t goes to| :: e goes to |
| 216 | |:: get sprite 1 for e :current room: test2?| |
| 217 | :sprite data: ( |
| 218 | . . . . . . . . |
| 219 | . . . # . . . . |
| 220 | . . # # . . . . |
| 221 | . # # # # # # . |
| 222 | . # # # # # # . |
| 223 | . . # # . . . . |
| 224 | . . . # . . . . |
| 225 | . . . . . . . . |
| 226 | ) |
| 227 | |:: get sprite 1 for e | |
| 228 | :sprite data: ( |
| 229 | . . . . . . . . |
| 230 | . . . . # . . . |
| 231 | . . . . # # . . |
| 232 | . # # # # # # . |
| 233 | . # # # # # # . |
| 234 | . . . . # # . . |
| 235 | . . . . # . . . |
| 236 | . . . . . . . . |
| 237 | ) |
| 238 | |:: get sprite 1 for x| :: get sprite 1 for e |
| 239 | |:: get sprite 1 for i| :: get sprite 1 for e |
| 240 | |:: get sprite 1 for t| :: get sprite 1 for e |
| 241 | |
| 242 | || :starting room: test |
| 243 | |:: player for test room| :player: p |
| 244 | |:: get tiles for test| |
| 245 | :room data: ( |
| 246 | w w . . . . w w |
| 247 | w . . . . g . w |
| 248 | . . [ = = ] . e |
| 249 | . c . g . . p x |
| 250 | . . . . . . . i |
| 251 | . g [ = = ] . t |
| 252 | w . . . . g . w |
| 253 | w w . . . . w w |
| 254 | ) |
| 255 | |
| 256 | |:: player for test2 room| :player: p |
| 257 | |:: get tiles for test2| |
| 258 | :room data: ( |
| 259 | w w w w w w w w |
| 260 | w . . . g . . w |
| 261 | e . w . . w . w |
| 262 | x p . . . c . w |
| 263 | i . . g . . g w |
| 264 | t . w . . w . w |
| 265 | w . . . g . . w |
| 266 | w w w w w w w w |
| 267 | ) |
| 268 | |
| 269 | |
| 270 | |
| 271 | || :screen resolution: 64 64 |
| 272 | :frame for sprites: 1 |
| 273 | |
| 274 | |:: start? :starting room: $room| |
| 275 | :: change rooms :go to: $room |
| 276 | |
| 277 | |:: change rooms? :actors: $1 $2 $3| |
| 278 | |:: change rooms? :solids: $1 $2 $3| |
| 279 | |:: change rooms? :exits: $1 $2 $3| |
| 280 | |:: change rooms? :player actor: $1 $2 $3| |
| 281 | |:: change rooms? :current room: $_| |
| 282 | |:: change rooms :go to: $room| |
| 283 | :current room: $room |
| 284 | :: player for $room room |
| 285 | :: get actor locations |
| 286 | :: set player actor |
| 287 | |
| 288 | |:: get actor locations :current room: $room?| |
| 289 | :: get tiles for $room :: find each actor :tile location: 0 0 |
| 290 | :: get tiles for $room :: find each solid :tile location: 0 0 |
| 291 | :: get tiles for $room :: find each exit :tile location: 0 0 |
| 292 | |
| 293 | |:: find each $type :room data: ($1 $2 $3 $4 $5 $6 $7 $8)| |
| 294 | :: is $1 $type :: add to $type list :: next tile column |
| 295 | :: is $2 $type :: add to $type list :: next tile column |
| 296 | :: is $3 $type :: add to $type list :: next tile column |
| 297 | :: is $4 $type :: add to $type list :: next tile column |
| 298 | :: is $5 $type :: add to $type list :: next tile column |
| 299 | :: is $6 $type :: add to $type list :: next tile column |
| 300 | :: is $7 $type :: add to $type list :: next tile column |
| 301 | :: is $8 $type :: add to $type list :: next tile row |
| 302 | :: find each $type |
| 303 | |:: find each $type :tile location: $x $y| |
| 304 | |
| 305 | |:: add to actor list :actor: $who yes :tile location: $x $y?| |
| 306 | :actors: $who $x $y |
| 307 | |:: add to actor list :actor: $who no| |
| 308 | |
| 309 | |:: add to solid list :solid: $what yes :tile location: $x $y?| |
| 310 | :solids: $what $x $y |
| 311 | |:: add to solid list :solid: $what no| |
| 312 | |
| 313 | |:: add to exit list :exit: $what yes :tile location: $x $y?| |
| 314 | :exits: $what $x $y |
| 315 | |:: add to exit list :exit: $what no| |
| 316 | |
| 317 | |
| 318 | |:: set player actor :player: $actor :actors: $actor $x $y| |
| 319 | :: rollback actors :player actor: $actor $x $y |
| 320 | |:: set player actor :player: $actor :no more actors:| |
| 321 | |:: set player actor?| |
| 322 | :: next actor |
| 323 | |
| 324 | |::start :screen resolution: $x $y?| |
| 325 | :@graphics: set-resolution fullscreen $x $y |
| 326 | ::main loop |
| 327 | |
| 328 | |
| 329 | |:: draw? :go to: $room?| |
| 330 | :: change rooms |
| 331 | |
| 332 | |:: draw| |
| 333 | :: move player actor |
| 334 | :: clear the screen |
| 335 | :: draw current room |
| 336 | :: draw npcs |
| 337 | :: draw player |
| 338 | :: get dialogue :: draw dialogue |
| 339 | :: tick frame |
| 340 | |
| 341 | |
| 342 | |:: get dialogue :player touching: $something?| |
| 343 | :: get dialogue for $something |
| 344 | |:: get dialogue| |
| 345 | |
| 346 | |:: draw dialogue :@text to draw: $something?| |
| 347 | :@graphics: draw-text 0 48 |
| 348 | |:: draw dialogue :no dialogue:| |
| 349 | |:: draw dialogue| |
| 350 | |
| 351 | |
| 352 | |:: is $x actor| :actor: $x no |
| 353 | |:: is $x solid| :solid: $x no |
| 354 | |:: is $x exit| :exit: $x no |
| 355 | |:: get dialogue for $what| :no dialogue: |
| 356 | |:: get sprite $frame for $id| |
| 357 | :: get sprite 1 for $id |
| 358 | |
| 359 | |
| 360 | |:: next actor :actors: $actor $x $y :actors: $more $_x $_y?| |
| 361 | :discarded actors: $actor $x $y |
| 362 | |:: next actor :actors: $actor $x $y| |
| 363 | :discarded actors: $actor $x $y |
| 364 | :no more actors: |
| 365 | |:: next actor| |
| 366 | :no more actors: |
| 367 | |:: rollback actors :discarded actors: $actor $x $y| |
| 368 | :actors: $actor $x $y |
| 369 | |:: rollback actors| |
| 370 | |
| 371 | |:: next solid :solids: $actor $x $y :solids: $more $_x $_y?| |
| 372 | :discarded solids: $actor $x $y |
| 373 | |:: next solid :solids: $actor $x $y| |
| 374 | :discarded solids: $actor $x $y |
| 375 | :no more solids: |
| 376 | |:: next solid| |
| 377 | :no more solids: |
| 378 | |:: rollback solids? :discarded solids: $solid $x $y| |
| 379 | :solids: $solid $x $y |
| 380 | |:: rollback solids| |
| 381 | |
| 382 | |
| 383 | |:: next exit :exits: $exit $x $y :exits: $more $_x $_y?| |
| 384 | :discarded exits: $exit $x $y |
| 385 | |:: next exit :exits: $exit $x $y| |
| 386 | :discarded exits: $exit $x $y |
| 387 | :no more exits: |
| 388 | |:: next exit| |
| 389 | :no more exits: |
| 390 | |:: rollback exits? :discarded exits: $exit $x $y| |
| 391 | :exits: $exit $x $y |
| 392 | |:: rollback exits| |
| 393 | |
| 394 | |
| 395 | |:: draw current room :current room: $room?| |
| 396 | :tile location: 0 0 |
| 397 | :: get tiles for $room |
| 398 | :: draw room |
| 399 | |:: draw room :room data: ($1 $2 $3 $4 $5 $6 $7 $8)| |
| 400 | :: render tile $1 to screen |
| 401 | :: render tile $2 to screen |
| 402 | :: render tile $3 to screen |
| 403 | :: render tile $4 to screen |
| 404 | :: render tile $5 to screen |
| 405 | :: render tile $6 to screen |
| 406 | :: render tile $7 to screen |
| 407 | :: render tile $8 to screen :: last column |
| 408 | :: draw room |
| 409 | |:: draw room :tile location: $x $y| |
| 410 | |
| 411 | |:: render tile $id to screen :: last column| |
| 412 | :: is $id actor :: draw sprite $id :: next tile row |
| 413 | |:: render tile $id to screen| |
| 414 | :: is $id actor :: draw sprite $id :: next tile column |
| 415 | |
| 416 | |
| 417 | |
| 418 | |:: draw npcs? :tile location: $x $y| |
| 419 | |:: draw npcs :no more actors:| |
| 420 | :: rollback actors |
| 421 | |:: draw npcs? :actors: $actor $x $y?| |
| 422 | :: draw sprite $actor :tile location: $x $y |
| 423 | :: next actor |
| 424 | |
| 425 | |
| 426 | |
| 427 | |:: draw player :player actor: $actor $x $y?| |
| 428 | :: draw sprite $actor :tile location: $x $y |
| 429 | |
| 430 | |
| 431 | |
| 432 | |:: draw sprite $actor :actor: $actor yes| |
| 433 | :: draw sprite . |
| 434 | |:: draw sprite $actor? :actor: $actor no| |
| 435 | |:: draw sprite $sprite :frame for sprites: $frame? :tile location: $x $y?| |
| 436 | :pixel location: $x $y |
| 437 | :: get sprite $frame for $sprite |
| 438 | :: draw sprite |
| 439 | |
| 440 | |:: draw sprite :sprite data: ($1 $2 $3 $4 $5 $6 $7 $8)| |
| 441 | :: plot $1 :: next pixel column |
| 442 | :: plot $2 :: next pixel column |
| 443 | :: plot $3 :: next pixel column |
| 444 | :: plot $4 :: next pixel column |
| 445 | :: plot $5 :: next pixel column |
| 446 | :: plot $6 :: next pixel column |
| 447 | :: plot $7 :: next pixel column |
| 448 | :: plot $8 :: next pixel row |
| 449 | :: draw sprite |
| 450 | |:: draw sprite :pixel location: $x $y| |
| 451 | |
| 452 | |
| 453 | |:: plot . :pixel location: $x $y? :color: $r $g $b| |
| 454 | :@graphics: set-pixel $x $y $r $g $b |
| 455 | |:: plot # :pixel location: $x $y? :color: $r $g $b| |
| 456 | :@graphics: set-pixel $x $y $r $g $b |
| 457 | |:: plot $char?| |
| 458 | :: get color for $char |
| 459 | |
| 460 | |:: next pixel column :@math: $x :pixel location: $_ $y| |
| 461 | :pixel location: $x $y |
| 462 | |:: next pixel column? :pixel location: $x $y?| |
| 463 | :@math: add $x 1 |
| 464 | |:: next pixel row :@math: $y :pixel location: $_x $_y :tile location: $x $_?| |
| 465 | :pixel location: $x $y |
| 466 | |:: next pixel row? :pixel location: $x $y?| |
| 467 | :@math: add $y 1 |
| 468 | |
| 469 | |:: next tile column :@math: $x :tile location: $_ $y| |
| 470 | :tile location: $x $y |
| 471 | |:: next tile column? :tile location: $x $y?| |
| 472 | :@math: add $x 8 |
| 473 | |:: next tile row :@math: $y :tile location: $x $_| |
| 474 | :tile location: 0 $y |
| 475 | |:: next tile row? :tile location: $x $y?| |
| 476 | :@math: add $y 8 |
| 477 | |
| 478 | |:: tick frame :frame for sprites: 1| |
| 479 | :frame for sprites: 2 |
| 480 | |:: tick frame :frame for sprites: 2| |
| 481 | :frame for sprites: 1 |
| 482 | |
| 483 | |
| 484 | |
| 485 | |:: move player actor| |
| 486 | :: check if up is pressed :: move player |
| 487 | :: check if right is pressed :: move player |
| 488 | :: check if down is pressed :: move player |
| 489 | :: check if left is pressed :: move player |
| 490 | |
| 491 | |:: move player :key pressed: up :player actor: $actor $x 0?| |
| 492 | |:: move player? :key pressed: up :player actor: $actor $x $y?| |
| 493 | :: move y :@math: subtract $y 8 |
| 494 | |:: move player :key pressed: right :player actor: $actor 56 $y?| |
| 495 | |:: move player? :key pressed: right :player actor: $actor $x $y?| |
| 496 | :: move x :@math: add $x 8 |
| 497 | |:: move player :key pressed: down :player actor: $actor $x 56?| |
| 498 | |:: move player? :key pressed: down :player actor: $actor $x $y?| |
| 499 | :: move y :@math: add $y 8 |
| 500 | |:: move player :key pressed: left :player actor: $actor 0 $y?| |
| 501 | |:: move player? :key pressed: left :player actor: $actor $x $y?| |
| 502 | :: move x :@math: subtract $x 8 |
| 503 | |:: move player| |
| 504 | |
| 505 | |
| 506 | |:: move x? :@math: $x :player actor: $actor $_ $y?| |
| 507 | :make move: $x $y |
| 508 | :: clear touching |
| 509 | :: check for solid $x $y |
| 510 | :: check for exit $x $y |
| 511 | :: check for actor $x $y |
| 512 | |
| 513 | |:: move y? :@math: $y :player actor: $actor $x $_?| |
| 514 | :make move: $x $y |
| 515 | :: clear touching |
| 516 | :: check for solid $x $y |
| 517 | :: check for exit $x $y |
| 518 | :: check for actor $x $y |
| 519 | |
| 520 | |:: move $axis :make move: $x $y :player actor: $actor $_x $_y| |
| 521 | :player actor: $actor $x $y |
| 522 | |:: move $axis| |
| 523 | |
| 524 | |:: clear touching :player touching: $not-this| |
| 525 | |:: clear touching| |
| 526 | |
| 527 | |
| 528 | |:: check for solid $x $y :solids: $what $x $y? :make move: $x $y| |
| 529 | :: rollback solids |
| 530 | |:: check for solid $x $y :no more solids:| |
| 531 | :: rollback solids |
| 532 | |:: check for solid $x $y?| |
| 533 | :: next solid |
| 534 | |
| 535 | |:: check for exit $x $y :exits: $what $x $y? :make move: $_x $_y| |
| 536 | :: rollback exits :: $what goes to |
| 537 | |:: check for exit $x $y :no more exits:| |
| 538 | :: rollback exits |
| 539 | |:: check for exit $x $y?| |
| 540 | :: next exit |
| 541 | |
| 542 | |:: check for actor $x $y :actors: $what $x $y?| |
| 543 | :: rollback actors |
| 544 | :: touched $what |
| 545 | :player touching: $what |
| 546 | |:: check for actor $x $y :no more actors:| |
| 547 | :: rollback actors |
| 548 | |:: check for actor $x $y?| |
| 549 | :: next actor |
nova.py
· 36 KiB · Python
Brut
Playground
#!/usr/bin/env python3
import sys, time, random, copy
try:
import warnings
warnings.filterwarnings("ignore")
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
import pygame
import pygame.midi
pygame_supported = True
except:
pygame_supported = False
pass
try:
import nfc, ndef, zlib, base64
from nfc.clf import RemoteTarget
nfc_supported = True
except:
nfc_supported = False
pass
def parse_container(string):
left = []
right = []
target = left
characters = (character for character in list(string) + [None])
delimiter = None
for character in characters:
if character in {' ', '\n', '\r', '\t'}:
continue
delimiter = character
break
if delimiter is None:
return None
for character in characters:
if character in {delimiter, None}:
if target is left:
target = right
else:
target = left
yield (left.copy(), right.copy())
left.clear()
right.clear()
else:
target.append(character)
def parse_labels(rules):
left = []
right = []
pair = ([], [])
target = left
delimiter = None
for condition, result in rules:
for side in (condition, result):
characters = (character for character in side + [None])
state = "find delimiter"
for character in characters:
if character == None:
label = "".join(pair[0]).strip()
pattern = "".join(pair[1]).strip()
if not(len(label) == 0 and len(pattern) == 0):
target.append((label, pattern))
pair[0].clear()
pair[1].clear()
break
elif state == "find delimiter":
if character in {' ', '\n', '\r', '\t'}:
continue
delimiter = character
state = "parse label"
elif state == "parse label":
if character == delimiter:
state = "parse pattern"
continue
pair[0].append(character)
elif state == "parse pattern":
if character in {delimiter, None}:
state = "parse label"
label = "".join(pair[0]).strip()
pattern = "".join(pair[1]).strip()
if not(len(label) == 0 and len(pattern) == 0):
target.append((label, pattern))
pair[0].clear()
pair[1].clear()
continue
pair[1].append(character)
if target is left:
target = right
else:
target = left
yield (left.copy(), right.copy())
left.clear()
right.clear()
def parse_patterns(rules):
for condition, result in rules:
left = []
right = []
preserves = []
for label, pattern in condition:
preserve = False
if pattern.endswith('?'):
pattern = pattern[:-1]
preserve = True
if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")):
for character in pattern[1:-1]:
left.append((label, [str(ord(character))]))
if preserve:
right.append((label, [str(ord(character))]))
elif pattern.startswith('(') and pattern.endswith(')'):
for token in pattern[1:-1].split():
left.append((label, [token]))
if preserve:
right.append((label, [token]))
elif pattern.startswith('.'):
for tuple in pattern.split('.'):
tuple = tuple.strip()
if len(tuple) != 0:
left.append((label, tuple.split()))
if preserve:
right.append((label, tuple.split()))
else:
left.append((label, pattern.split()))
if preserve:
preserves.append((label, pattern.split()))
for label, pattern in result:
if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")):
for character in pattern[1:-1]:
right.append((label, [str(ord(character))]))
elif pattern.startswith('(') and pattern.endswith(')'):
for token in pattern[1:-1].split():
right.append((label, [token]))
elif pattern.startswith('.'):
for tuple in pattern.split('.'):
tuple = tuple.strip()
if len(tuple) != 0:
right.append((label, tuple.split()))
else:
right.append((label, pattern.split()))
yield (left.copy(), right.copy() + preserves.copy())
left.clear()
right.clear()
preserves.clear()
def parse(string):
return parse_patterns(parse_labels(parse_container(string)))
def facts(ruleset):
for left, right in ruleset:
if not left:
for element in right:
yield element
def rules(ruleset):
for left, right in ruleset:
if left:
yield (left, right)
def match(pattern, knowledge, variables=None):
if variables is None:
variables = {}
label, elements = pattern
if label not in knowledge or not knowledge[label][0]:
return None
if knowledge[label][1] >= len(knowledge[label][0]):
knowledge[label][1] = 0
return None
top = knowledge[label][0][(-1 - knowledge[label][1])]
len_top = len(top)
if len_top != len(elements):
knowledge[label][1] = 0
return None
for index in range(len_top):
left = elements[index]
right = top[index]
if left[0] == '$':
variable = left[1:]
if variable not in variables:
variables[variable] = right
elif variables[variable] != right:
knowledge[label][1] = 0
return None
elif left != right:
knowledge[label][1] = 0
return None
knowledge[label][1] += 1
return variables
def consume(pattern, knowledge):
if type(pattern) == str:
knowledge[pattern][0].pop()
knowledge[pattern][1] = 0
else:
label, _ = pattern
knowledge[label][0].pop()
knowledge[label][1] = 0
def produce(pattern, knowledge, variables=None):
if variables is None:
variables = {}
label, elements = pattern
len_elements = len(elements)
fact = [None] * len_elements
for index in range(len_elements):
element = elements[index]
if element[0] == '$':
variable = element[1:]
if variable in variables:
element = variables[variable]
fact[index] = element
if label not in knowledge:
knowledge[label] = [[], 0]
knowledge[label][0].append(fact)
knowledge[label][1] = 0
def reset(pattern, knowledge):
label, _ = pattern
if label in knowledge:
knowledge[label][1] = 0
def prepare(ruleset, knowledge=None):
if knowledge == None:
knowledge = {}
for fact in reversed(list(facts(ruleset))):
label, elements = fact
if label not in knowledge:
knowledge[label] = [[], 0]
knowledge[label][0].append(elements)
return knowledge
def print_knowledge(knowledge, debug=0, maximum=10):
for label, facts in knowledge.items():
facts = facts[0]
if not facts:
continue
if not label:
label = ":"
print(label + ': ', end="")
if len(facts) >= maximum and debug % 2:
print("...", len(facts), "...")
continue
if facts:
if len(facts[-1]) == 0:
print("<blank>")
else:
print(" ".join(facts[-1]))
for fact in reversed(facts[:-1]):
if len(fact) == 0:
fact = ["<blank>"]
print(' ' * (len(label) + 1), " ".join(fact))
def step(ruleset, primitives=[], knowledge=None, snapshots=[], debug=0, trace=False):
variables = {}
steps = 0
if debug >= 3:
print_knowledge(knowledge, debug)
print("")
matched = False
for primitive in primitives:
steps += 1
if primitive(ruleset, knowledge, snapshots):
matched = True
break
if matched:
return (True, steps, knowledge)
for left, right in ruleset:
steps += 1
matched = True
variables.clear()
for pattern in left:
if match(pattern, knowledge, variables) is None:
matched = False
for pattern in left:
reset(pattern, knowledge)
break
if matched:
for pattern in left:
consume(pattern, knowledge)
for pattern in reversed(right):
produce(pattern, knowledge, variables)
break
return (matched, steps, knowledge)
def run(ruleset, primitives=[], knowledge=None, debug=0, trace=False):
knowledge = prepare(ruleset, knowledge)
ruleset = list(rules(ruleset))
matched = True
steps = 0
snapshots = []
while matched:
matched, completed, knowledge = step(ruleset, primitives, knowledge, snapshots, debug, trace)
steps += completed
if trace:
snapshots.append(copy.deepcopy(knowledge))
if debug:
print("Took", steps, "steps.")
return (knowledge, snapshots)
def usage(name):
print(name, ":: a nova interpreter")
print("usage:")
print('\t' + name, "<file> [-d] [-r]")
def math(ruleset, knowledge, snapshots=[], trace=False):
if (variables := match(("@math", ["$operation", "$x", "$y"]), knowledge)) is not None:
try:
operation = variables["operation"]
x = int(variables["x"])
y = int(variables["y"])
if operation == "add":
variables["z"] = str(x + y)
elif operation == "subtract":
variables["z"] = str(x - y)
elif operation == "multiply":
variables["z"] = str(x * y)
elif operation == "divide":
variables["z"] = str(x // y)
elif operation == "modulo":
variables["z"] = str(x % y)
elif operation == "compare":
if x < y:
variables["z"] = "less"
elif x == y:
variables["z"] = "equal"
else:
variables["z"] = "greater"
elif operation == "random":
if y < x:
x, y = y, x
variables["z"] = str(random.randrange(x, y))
consume("@math", knowledge)
produce(("@math", ["$z"]), knowledge, variables)
return True
except Exception as e:
print(e)
pass
return False
def arrays():
arrays = {}
def create(ruleset, knowledge, snapshots=[], trace=False):
nonlocal arrays
if (variables := match(("@array", ["create", "$array", "$size"]), knowledge)) is not None:
try:
array = variables["array"]
size = int(variables["size"])
consume("@array", knowledge)
if array in arrays:
produce(("@array", ["$array", "exists"]), knowledge, variables)
else:
arrays[array] = [None] * size
return True
except:
pass
return False
def set(ruleset, knowledge, snapshots=[], trace=False):
nonlocal arrays
if (variables := match(("@array", ["set", "$array", "$index", "$value"]), knowledge)) is not None:
try:
array = variables["array"]
index = int(variables["index"])
value = variables["value"]
consume("@array", knowledge)
if array not in arrays:
produce(("@array", ["$array", "not", "found"]), knowledge, variables)
elif index >= len(arrays[array]):
produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables)
else:
arrays[array][index] = value
return True
except:
pass
return False
def get(ruleset, knowledge, snapshots=[], trace=False):
nonlocal arrays
if (variables := match(("@array", ["get", "$array", "$index"]), knowledge)) is not None:
try:
array = variables["array"]
index = int(variables["index"])
consume("@array", knowledge)
if array not in arrays:
produce(("@array", ["$array", "not", "found"]), knowledge, variables)
elif index >= len(arrays[array]):
produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables)
else:
if arrays[array][index] == None:
produce(("@array", ["$array", "$index"]), knowledge, variables)
else:
variables["value"] = str(arrays[array][index])
produce(("@array", ["$array", "$index", "$value"]), knowledge, variables)
return True
except:
pass
return False
return [
create,
set,
get
]
def graphics():
keys = {}
mouse_buttons = {}
screen = None
pixels = None
clock = None
font = None
resolution = (0, 0)
def poll_input(ruleset, knowledge, snapshots=[]):
nonlocal keys
nonlocal mouse_buttons
if match(("@input", ["poll-input"]), knowledge) is not None:
consume("@input", knowledge)
for event in pygame.event.get():
if event.type == pygame.QUIT:
produce(("@signal", ["quit"]), knowledge)
keys = pygame.key.get_pressed()
mouse_buttons = pygame.mouse.get_pressed()
return True
return False
def check_key(ruleset, knowledge, snapshots=[]):
nonlocal keys
if (variables := match(("@input", ["check-key", "$key"]), knowledge)) is not None:
consume("@input", knowledge)
keycode = pygame.key.key_code(variables["key"])
if keys[keycode]:
produce(("@input", ["key-pressed", "$key"]), knowledge, variables)
else:
produce(("@input", ["key-released", "$key"]), knowledge, variables)
return True
return False
def check_mouse_button(ruleset, knowledge, snapshots=[]):
nonlocal mouse_buttons
if (variables := match(("@input", ["check-mouse-button", "$button"]), knowledge)) is not None:
consume("@input", knowledge)
button = int(variables["button"])
if mouse_buttons[button]:
produce(("@input", ["mouse-button-pressed", "$button"]), knowledge, variables)
else:
produce(("@input", ["mouse-button-released", "$button"]), knowledge, variables)
return True
return False
def get_mouse_position(ruleset, knowledge, snapshots=[]):
if (variables := match(("@input", ["get-mouse-position"]), knowledge)) is not None:
consume("@input", knowledge)
position = pygame.mouse.get_pos()
variables["x"], variables["y"] = (str(position[0]), str(position[1]))
produce(("@input", ["mouse-position", "$x", "$y"]), knowledge, variables)
return True
return False
def set_pixel(ruleset, knowledge, snapshots=[]):
nonlocal pixels
if (variables := match(("@graphics", ["set-pixel", "$x", "$y", "$r", "$g", "$b"]), knowledge)) is not None:
try:
x = int(variables["x"])
y = int(variables["y"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
pixels[x % resolution[0], y % resolution[1]] = (r % 256, g % 256, b % 256)
consume("@graphics", knowledge)
return True
except:
pass
return False
def draw_line(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["draw-line", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None:
try:
x1 = int(variables["x1"])
y1 = int(variables["y1"])
x2 = int(variables["x2"])
y2 = int(variables["y2"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
w = int(variables["w"])
pygame.draw.line(screen, (r % 256, g % 256, b % 256), (x1, y1), (x2, y2), w)
consume("@graphics", knowledge)
return True
except:
pass
return False
def draw_rect(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["draw-rect", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None:
try:
x1 = int(variables["x1"])
y1 = int(variables["y1"])
x2 = int(variables["x2"])
y2 = int(variables["y2"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
w = int(variables["w"])
pygame.draw.rect(screen, (r % 256, g % 256, b % 256), (x1, y1, x2, y2), w)
consume("@graphics", knowledge)
return True
except:
pass
return False
def draw_circle(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["draw-circle", "$x", "$y", "$d", "$r", "$g", "$b", "$w"]), knowledge)) is not None:
try:
x = int(variables["x"])
y = int(variables["y"])
d = int(variables["d"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
w = int(variables["w"])
pygame.draw.circle(screen, (r % 256, g % 256, b % 256), (x, y), d, w)
consume("@graphics", knowledge)
return True
except:
pass
return False
def clear_screen(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["clear-screen", "$r", "$g", "$b"]), knowledge)) is not None:
try:
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
screen.fill((r % 256, g % 256, b % 256))
consume("@graphics", knowledge)
return True
except:
pass
return False
def draw_fps(ruleset, knowledge, snapshots=[]):
nonlocal pixels
nonlocal screen
nonlocal clock
nonlocal font
if match(("@graphics", ["draw-fps"]), knowledge) is not None:
if clock is None:
clock = pygame.time.Clock()
clock.tick()
if font is None:
font = pygame.font.SysFont("m3x6", 16)
framerate = clock.get_fps()
if framerate <= 1_000_000:
fps = font.render(str(int(clock.get_fps())) , 1, pygame.Color("RED"))
pixels.close()
screen.blit(fps, (0, 0))
pixels = pygame.PixelArray(screen)
consume("@graphics", knowledge)
return True
return False
def display(ruleset, knowledge, snapshots=[]):
nonlocal pixels
if match(("@graphics", ["display"]), knowledge) is not None:
pixels.close()
pygame.display.flip()
pixels = pygame.PixelArray(screen)
consume("@graphics", knowledge)
return True
return False
def draw_text(ruleset, knowledge, snapshots=[]):
nonlocal font
nonlocal screen
nonlocal pixels
if (variables := match(("@graphics", ["draw-text", "$x", "$y"]), knowledge)) is not None:
x = int(variables["x"])
y = int(variables["y"])
if font is None:
font = pygame.font.SysFont("m3x6", 16)
text = ''.join(chr(int(n[0])) for n in reversed(knowledge["@text to draw"][0]))
knowledge["@text to draw"] = [[], 0]
drawn_text = font.render(text, False, pygame.Color("WHITE"))
pixels.close()
pygame.draw.rect(screen, (0, 0, 0), (0, 48, 64, 64))
screen.blit(drawn_text, (x, y))
pixels = pygame.PixelArray(screen)
consume("@graphics", knowledge)
return True
return False
def set_resolution(ruleset, knowledge, snapshots=[]):
nonlocal screen
nonlocal pixels
nonlocal resolution
if (variables := match(("@graphics", ["set-resolution", "$x", "$y"]), knowledge)) is not None:
try:
if screen is None:
pygame.init()
x = int(variables["x"])
y = int(variables["y"])
screen = pygame.display.set_mode((x, y))
pygame.display.set_caption("Nova")
icon = pygame.Surface((32, 32))
icon.fill("black")
icon.set_colorkey((0, 0, 0))
pygame.display.set_icon(icon)
pixels = pygame.PixelArray(screen)
resolution = (x, y)
consume("@graphics", knowledge)
return True
except:
pass
elif (variables := match(("@graphics", ["set-resolution", "fullscreen", "$x", "$y"]), knowledge)) is not None:
try:
if screen is None:
pygame.init()
x = int(variables["x"])
y = int(variables["y"])
screen = pygame.display.set_mode((x, y), flags=pygame.FULLSCREEN | pygame.SCALED)
pygame.display.set_caption("Nova")
icon = pygame.Surface((32, 32))
icon.fill("black")
icon.set_colorkey((0, 0, 0))
pygame.display.set_icon(icon)
pixels = pygame.PixelArray(screen)
resolution = (x, y)
consume("@graphics", knowledge)
return True
except:
pass
elif (variables := match(("@graphics", ["set-resolution", "fullscreen"]), knowledge)) is not None:
try:
if screen is None:
pygame.init()
screen = pygame.display.set_mode((0, 0), flags=pygame.FULLSCREEN)
pygame.display.set_caption("Nova")
icon = pygame.Surface((32, 32))
icon.fill("black")
icon.set_colorkey((0, 0, 0))
pygame.display.set_icon(icon)
pixels = pygame.PixelArray(screen)
resolution = pygame.display.get_surface().get_size()
consume("@graphics", knowledge)
return True
except:
pass
return False
return [
set_pixel,
draw_line,
draw_rect,
draw_circle,
draw_text,
display,
poll_input,
draw_fps,
clear_screen,
check_key,
check_mouse_button,
get_mouse_position,
set_resolution
]
def midi():
output = None
def set_output(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["set-output", "$x"]), knowledge)) is not None:
try:
if output is None:
pygame.midi.init()
else:
del output
output = pygame.midi.Output(int(variables["x"]))
consume("@midi", knowledge)
return True
except:
pass
return False
def abort(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["abort"]), knowledge)) is not None:
try:
output.abort()
consume("@midi", knowledge)
return True
except:
pass
return False
def set_instrument(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["set-instrument", "$x"]), knowledge)) is not None:
try:
output.set_instrument(int(variables["x"]))
consume("@midi", knowledge)
return True
except:
pass
return False
def note(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["note", "$x", "$y", "$z", "$w"]), knowledge)) is not None:
try:
state = variables["x"]
note = int(variables["y"])
velocity = int(variables["z"])
channel = int(variables["w"])
if state == "on":
output.note_on(note, velocity, channel)
elif state == "off":
output.note_off(note, velocity, channel)
else:
return False
consume("@midi", knowledge)
return True
except:
pass
elif (variables := match(("@midi", ["note", "$x", "$y", "$z"]), knowledge)) is not None:
try:
state = variables["x"]
note = int(variables["y"])
velocity = int(variables["z"])
if state == "on":
output.note_on(note, velocity)
elif state == "off":
output.note_off(note, velocity)
else:
return False
consume("@midi", knowledge)
return True
except:
pass
return False
return [
set_output,
abort,
set_instrument,
note
]
def _time():
def sleep(ruleset, knowledge, snapshots=[]):
if (variables := match(("@time", ["sleep", "$x", "$y"]), knowledge)) is not None:
try:
duration = int(variables["x"])
unit = variables["y"]
if unit == "seconds":
time.sleep(duration)
elif unit == "milliseconds":
time.sleep(0.001 * duration)
else:
return False
consume("@time", knowledge)
return True
except:
pass
return False
return [
sleep
]
def stdio():
def read(ruleset, knowledge, snapshots=[]):
if (variables := match(("@stdio", ["read", "$x"]), knowledge)) is not None:
try:
amount = int(variables["x"])
bytes = sys.stdin.read(amount)
consume("@stdio", knowledge)
for byte in reversed(bytes.encode()):
variables["x"] = str(byte)
produce(("@stdio", ["$x"]), knowledge, variables)
return True
except KeyboardInterrupt:
consume("@stdio", knowledge)
return True
except:
pass
elif (variables := match(("@stdio", ["read"]), knowledge)) is not None:
try:
variables["x"] = str(ord(sys.stdin.read(1)))
consume("@stdio", knowledge)
produce(("@stdio", ["$x"]), knowledge, variables)
return True
except KeyboardInterrupt:
consume("@stdio", knowledge)
return True
except:
pass
return False
def write(ruleset, knowledge, snapshots=[]):
if (variables := match(("@stdio", ["write", "$x", "$y"]), knowledge)) is not None:
try:
byte = int(variables["x"])
length = int(variables["y"])
if byte < 0:
sys.stdout.buffer.write(byte.to_bytes(length, signed=True))
else:
sys.stdout.buffer.write(byte.to_bytes(length))
sys.stdout.buffer.flush()
consume("@stdio", knowledge)
variables["x"] = str(length)
produce(("@stdio", ["wrote", "$x"]), knowledge, variables)
return True
except OverflowError:
length = (byte.bit_length() + 7) // 8
if length == 0:
length = 1
if byte < 0:
sys.stdout.buffer.write(byte.to_bytes(length, signed=True))
else:
sys.stdout.buffer.write(byte.to_bytes(length))
sys.stdout.buffer.flush()
consume("@stdio", knowledge)
variables["x"] = str(length)
produce(("@stdio", ["wrote", "$x"]), knowledge, variables)
return True
except:
pass
elif (variables := match(("@stdio", ["write", "$x"]), knowledge)) is not None:
try:
byte = int(variables["x"])
length = (byte.bit_length() + 7) // 8
if length == 0:
length = 1
if byte < 0:
sys.stdout.buffer.write(byte.to_bytes(length, signed=True))
else:
sys.stdout.buffer.write(byte.to_bytes(length))
sys.stdout.buffer.flush()
consume("@stdio", knowledge)
variables["x"] = str(length)
produce(("@stdio", ["wrote", "$x"]), knowledge, variables)
return True
except:
pass
return False
return [
read, write
]
def _nfc():
clf = None
last_card = None
def nfc_read_card():
nonlocal clf
payload = []
identifier = None
card_types = [RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F')]
target = clf.sense(*card_types)
if target is not None:
tag = nfc.tag.activate(clf, target)
if tag is not None and tag.ndef is not None:
for record in tag.ndef.records:
text = zlib.decompress(base64.b64decode(record.text)).decode()
payload += list(parse(text))
identifier = tag.identifier
return (payload, identifier)
def open(ruleset, knowledge, snapshots=[]):
nonlocal clf
if (variables := match(("@nfc", ["open"]), knowledge)) is not None:
try:
consume("@nfc", knowledge)
clf = nfc.ContactlessFrontend('usb')
return True
except:
produce(("@nfc", ["no-device"]), knowledge)
return True
return False
def close(ruleset, knowledge, snapshots=[]):
nonlocal clf
if (variables := match(("@nfc", ["close"]), knowledge)) is not None:
try:
consume("@nfc", knowledge)
clf.close()
return True
except:
produce(("@nfc", ["no-device"]), knowledge)
return True
return False
def read_card(ruleset, knowledge, snapshots=[]):
nonlocal last_card
if (variables := match(("@nfc", ["read-card"]), knowledge)) is not None:
try:
consume("@nfc", knowledge)
(payload, identifier) = nfc_read_card()
variables["x"] = "".join([hex(byte)[2:] for byte in identifier])
last_card = payload
produce(("@nfc", ["card", "$x"]), knowledge, variables)
return True
except:
produce(("@nfc", ["read-failed"]), knowledge)
return True
return False
def load_card_rules(ruleset, knowledge, snapshots=[]):
nonlocal last_card
if (variables := match(("@nfc", ["load-rules"]), knowledge)) is not None:
try:
for rule in rules(reversed(last_card)):
ruleset.insert(0, rule)
consume("@nfc", knowledge)
return True
except:
pass
return False
def load_card_facts(ruleset, knowledge, snapshots=[]):
if (variables := match(("@nfc", ["load-facts"]), knowledge)) is not None:
try:
prepare(last_card, knowledge)
consume("@nfc", knowledge)
return True
except:
pass
return False
return [
open,
close,
read_card,
load_card_rules,
load_card_facts
]
def repl(rulesets, primitives, debug):
try:
knowledge, _ = run(rulesets)
print_knowledge(knowledge, debug)
current_stack = ""
command = input("::> ")
while command != "@quit":
if command in {"@run", '!'}:
knowledge, _ = run(rulesets, primitives, knowledge)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
elif command.startswith('@'):
current_stack = command[1:]
elif command.startswith('.'):
command = command[1:]
knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
elif command.startswith(':'):
command = command[1:]
rulesets = list(parse(command)) + rulesets
knowledge, _ = run(rulesets, primitives, knowledge, debug)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
elif len(command) != 0:
command = "||:" + current_stack + ':' + command
knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
else:
if debug:
print_knowledge(knowledge, debug)
command = input(":" + current_stack + ":> ")
except EOFError:
pass
except KeyboardInterrupt:
pass
def main(name, arguments):
primitives = _time() + [math] + arrays() + stdio()
if pygame_supported:
primitives += graphics() + midi()
if nfc_supported:
primitives += _nfc()
rulesets = []
debug = 0
prompt = False
trace = False
if not arguments:
usage(name)
return
for argument in arguments:
if argument == '-d':
debug += 1
elif argument == '-r':
prompt = True
elif argument == '-t':
trace = True
else:
with open(argument) as file:
rulesets += list(parse(file.read()))
if prompt:
repl(rulesets, primitives, debug)
else:
if len(rulesets) == 0:
usage(name)
return
if debug:
knowledge, snapshots = run(rulesets, primitives, None, debug, trace)
if trace:
for index in range(len(snapshots)):
print("Snapshot", str(index) + ':')
print("----------------")
print_knowledge(snapshots[index], debug)
print("")
else:
print_knowledge(knowledge, debug)
else:
run(rulesets, primitives, None, debug, trace)
if __name__ == "__main__":
main(sys.argv[0], sys.argv[1:])
| 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import sys, time, random, copy |
| 4 | |
| 5 | try: |
| 6 | import warnings |
| 7 | warnings.filterwarnings("ignore") |
| 8 | from os import environ |
| 9 | environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' |
| 10 | import pygame |
| 11 | import pygame.midi |
| 12 | pygame_supported = True |
| 13 | except: |
| 14 | pygame_supported = False |
| 15 | pass |
| 16 | |
| 17 | try: |
| 18 | import nfc, ndef, zlib, base64 |
| 19 | from nfc.clf import RemoteTarget |
| 20 | nfc_supported = True |
| 21 | except: |
| 22 | nfc_supported = False |
| 23 | pass |
| 24 | |
| 25 | def parse_container(string): |
| 26 | left = [] |
| 27 | right = [] |
| 28 | target = left |
| 29 | characters = (character for character in list(string) + [None]) |
| 30 | delimiter = None |
| 31 | for character in characters: |
| 32 | if character in {' ', '\n', '\r', '\t'}: |
| 33 | continue |
| 34 | delimiter = character |
| 35 | break |
| 36 | if delimiter is None: |
| 37 | return None |
| 38 | for character in characters: |
| 39 | if character in {delimiter, None}: |
| 40 | if target is left: |
| 41 | target = right |
| 42 | else: |
| 43 | target = left |
| 44 | yield (left.copy(), right.copy()) |
| 45 | left.clear() |
| 46 | right.clear() |
| 47 | else: |
| 48 | target.append(character) |
| 49 | |
| 50 | def parse_labels(rules): |
| 51 | left = [] |
| 52 | right = [] |
| 53 | pair = ([], []) |
| 54 | target = left |
| 55 | delimiter = None |
| 56 | for condition, result in rules: |
| 57 | for side in (condition, result): |
| 58 | characters = (character for character in side + [None]) |
| 59 | state = "find delimiter" |
| 60 | for character in characters: |
| 61 | if character == None: |
| 62 | label = "".join(pair[0]).strip() |
| 63 | pattern = "".join(pair[1]).strip() |
| 64 | if not(len(label) == 0 and len(pattern) == 0): |
| 65 | target.append((label, pattern)) |
| 66 | pair[0].clear() |
| 67 | pair[1].clear() |
| 68 | break |
| 69 | elif state == "find delimiter": |
| 70 | if character in {' ', '\n', '\r', '\t'}: |
| 71 | continue |
| 72 | delimiter = character |
| 73 | state = "parse label" |
| 74 | elif state == "parse label": |
| 75 | if character == delimiter: |
| 76 | state = "parse pattern" |
| 77 | continue |
| 78 | pair[0].append(character) |
| 79 | elif state == "parse pattern": |
| 80 | if character in {delimiter, None}: |
| 81 | state = "parse label" |
| 82 | label = "".join(pair[0]).strip() |
| 83 | pattern = "".join(pair[1]).strip() |
| 84 | if not(len(label) == 0 and len(pattern) == 0): |
| 85 | target.append((label, pattern)) |
| 86 | pair[0].clear() |
| 87 | pair[1].clear() |
| 88 | continue |
| 89 | pair[1].append(character) |
| 90 | if target is left: |
| 91 | target = right |
| 92 | else: |
| 93 | target = left |
| 94 | yield (left.copy(), right.copy()) |
| 95 | left.clear() |
| 96 | right.clear() |
| 97 | |
| 98 | def parse_patterns(rules): |
| 99 | for condition, result in rules: |
| 100 | left = [] |
| 101 | right = [] |
| 102 | preserves = [] |
| 103 | for label, pattern in condition: |
| 104 | preserve = False |
| 105 | if pattern.endswith('?'): |
| 106 | pattern = pattern[:-1] |
| 107 | preserve = True |
| 108 | if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")): |
| 109 | for character in pattern[1:-1]: |
| 110 | left.append((label, [str(ord(character))])) |
| 111 | if preserve: |
| 112 | right.append((label, [str(ord(character))])) |
| 113 | elif pattern.startswith('(') and pattern.endswith(')'): |
| 114 | for token in pattern[1:-1].split(): |
| 115 | left.append((label, [token])) |
| 116 | if preserve: |
| 117 | right.append((label, [token])) |
| 118 | elif pattern.startswith('.'): |
| 119 | for tuple in pattern.split('.'): |
| 120 | tuple = tuple.strip() |
| 121 | if len(tuple) != 0: |
| 122 | left.append((label, tuple.split())) |
| 123 | if preserve: |
| 124 | right.append((label, tuple.split())) |
| 125 | else: |
| 126 | left.append((label, pattern.split())) |
| 127 | if preserve: |
| 128 | preserves.append((label, pattern.split())) |
| 129 | for label, pattern in result: |
| 130 | if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")): |
| 131 | for character in pattern[1:-1]: |
| 132 | right.append((label, [str(ord(character))])) |
| 133 | elif pattern.startswith('(') and pattern.endswith(')'): |
| 134 | for token in pattern[1:-1].split(): |
| 135 | right.append((label, [token])) |
| 136 | elif pattern.startswith('.'): |
| 137 | for tuple in pattern.split('.'): |
| 138 | tuple = tuple.strip() |
| 139 | if len(tuple) != 0: |
| 140 | right.append((label, tuple.split())) |
| 141 | else: |
| 142 | right.append((label, pattern.split())) |
| 143 | yield (left.copy(), right.copy() + preserves.copy()) |
| 144 | left.clear() |
| 145 | right.clear() |
| 146 | preserves.clear() |
| 147 | |
| 148 | def parse(string): |
| 149 | return parse_patterns(parse_labels(parse_container(string))) |
| 150 | |
| 151 | def facts(ruleset): |
| 152 | for left, right in ruleset: |
| 153 | if not left: |
| 154 | for element in right: |
| 155 | yield element |
| 156 | |
| 157 | def rules(ruleset): |
| 158 | for left, right in ruleset: |
| 159 | if left: |
| 160 | yield (left, right) |
| 161 | |
| 162 | def match(pattern, knowledge, variables=None): |
| 163 | if variables is None: |
| 164 | variables = {} |
| 165 | label, elements = pattern |
| 166 | if label not in knowledge or not knowledge[label][0]: |
| 167 | return None |
| 168 | if knowledge[label][1] >= len(knowledge[label][0]): |
| 169 | knowledge[label][1] = 0 |
| 170 | return None |
| 171 | top = knowledge[label][0][(-1 - knowledge[label][1])] |
| 172 | len_top = len(top) |
| 173 | if len_top != len(elements): |
| 174 | knowledge[label][1] = 0 |
| 175 | return None |
| 176 | for index in range(len_top): |
| 177 | left = elements[index] |
| 178 | right = top[index] |
| 179 | if left[0] == '$': |
| 180 | variable = left[1:] |
| 181 | if variable not in variables: |
| 182 | variables[variable] = right |
| 183 | elif variables[variable] != right: |
| 184 | knowledge[label][1] = 0 |
| 185 | return None |
| 186 | elif left != right: |
| 187 | knowledge[label][1] = 0 |
| 188 | return None |
| 189 | knowledge[label][1] += 1 |
| 190 | return variables |
| 191 | |
| 192 | def consume(pattern, knowledge): |
| 193 | if type(pattern) == str: |
| 194 | knowledge[pattern][0].pop() |
| 195 | knowledge[pattern][1] = 0 |
| 196 | else: |
| 197 | label, _ = pattern |
| 198 | knowledge[label][0].pop() |
| 199 | knowledge[label][1] = 0 |
| 200 | |
| 201 | def produce(pattern, knowledge, variables=None): |
| 202 | if variables is None: |
| 203 | variables = {} |
| 204 | label, elements = pattern |
| 205 | len_elements = len(elements) |
| 206 | fact = [None] * len_elements |
| 207 | for index in range(len_elements): |
| 208 | element = elements[index] |
| 209 | if element[0] == '$': |
| 210 | variable = element[1:] |
| 211 | if variable in variables: |
| 212 | element = variables[variable] |
| 213 | fact[index] = element |
| 214 | if label not in knowledge: |
| 215 | knowledge[label] = [[], 0] |
| 216 | knowledge[label][0].append(fact) |
| 217 | knowledge[label][1] = 0 |
| 218 | |
| 219 | def reset(pattern, knowledge): |
| 220 | label, _ = pattern |
| 221 | if label in knowledge: |
| 222 | knowledge[label][1] = 0 |
| 223 | |
| 224 | def prepare(ruleset, knowledge=None): |
| 225 | if knowledge == None: |
| 226 | knowledge = {} |
| 227 | for fact in reversed(list(facts(ruleset))): |
| 228 | label, elements = fact |
| 229 | if label not in knowledge: |
| 230 | knowledge[label] = [[], 0] |
| 231 | knowledge[label][0].append(elements) |
| 232 | return knowledge |
| 233 | |
| 234 | def print_knowledge(knowledge, debug=0, maximum=10): |
| 235 | for label, facts in knowledge.items(): |
| 236 | facts = facts[0] |
| 237 | if not facts: |
| 238 | continue |
| 239 | if not label: |
| 240 | label = ":" |
| 241 | print(label + ': ', end="") |
| 242 | if len(facts) >= maximum and debug % 2: |
| 243 | print("...", len(facts), "...") |
| 244 | continue |
| 245 | if facts: |
| 246 | if len(facts[-1]) == 0: |
| 247 | print("<blank>") |
| 248 | else: |
| 249 | print(" ".join(facts[-1])) |
| 250 | for fact in reversed(facts[:-1]): |
| 251 | if len(fact) == 0: |
| 252 | fact = ["<blank>"] |
| 253 | print(' ' * (len(label) + 1), " ".join(fact)) |
| 254 | |
| 255 | def step(ruleset, primitives=[], knowledge=None, snapshots=[], debug=0, trace=False): |
| 256 | variables = {} |
| 257 | steps = 0 |
| 258 | if debug >= 3: |
| 259 | print_knowledge(knowledge, debug) |
| 260 | print("") |
| 261 | matched = False |
| 262 | for primitive in primitives: |
| 263 | steps += 1 |
| 264 | if primitive(ruleset, knowledge, snapshots): |
| 265 | matched = True |
| 266 | break |
| 267 | if matched: |
| 268 | return (True, steps, knowledge) |
| 269 | for left, right in ruleset: |
| 270 | steps += 1 |
| 271 | matched = True |
| 272 | variables.clear() |
| 273 | for pattern in left: |
| 274 | if match(pattern, knowledge, variables) is None: |
| 275 | matched = False |
| 276 | for pattern in left: |
| 277 | reset(pattern, knowledge) |
| 278 | break |
| 279 | if matched: |
| 280 | for pattern in left: |
| 281 | consume(pattern, knowledge) |
| 282 | for pattern in reversed(right): |
| 283 | produce(pattern, knowledge, variables) |
| 284 | break |
| 285 | return (matched, steps, knowledge) |
| 286 | |
| 287 | def run(ruleset, primitives=[], knowledge=None, debug=0, trace=False): |
| 288 | knowledge = prepare(ruleset, knowledge) |
| 289 | ruleset = list(rules(ruleset)) |
| 290 | matched = True |
| 291 | steps = 0 |
| 292 | snapshots = [] |
| 293 | while matched: |
| 294 | matched, completed, knowledge = step(ruleset, primitives, knowledge, snapshots, debug, trace) |
| 295 | steps += completed |
| 296 | if trace: |
| 297 | snapshots.append(copy.deepcopy(knowledge)) |
| 298 | if debug: |
| 299 | print("Took", steps, "steps.") |
| 300 | return (knowledge, snapshots) |
| 301 | |
| 302 | def usage(name): |
| 303 | print(name, ":: a nova interpreter") |
| 304 | print("usage:") |
| 305 | print('\t' + name, "<file> [-d] [-r]") |
| 306 | |
| 307 | def math(ruleset, knowledge, snapshots=[], trace=False): |
| 308 | if (variables := match(("@math", ["$operation", "$x", "$y"]), knowledge)) is not None: |
| 309 | try: |
| 310 | operation = variables["operation"] |
| 311 | x = int(variables["x"]) |
| 312 | y = int(variables["y"]) |
| 313 | if operation == "add": |
| 314 | variables["z"] = str(x + y) |
| 315 | elif operation == "subtract": |
| 316 | variables["z"] = str(x - y) |
| 317 | elif operation == "multiply": |
| 318 | variables["z"] = str(x * y) |
| 319 | elif operation == "divide": |
| 320 | variables["z"] = str(x // y) |
| 321 | elif operation == "modulo": |
| 322 | variables["z"] = str(x % y) |
| 323 | elif operation == "compare": |
| 324 | if x < y: |
| 325 | variables["z"] = "less" |
| 326 | elif x == y: |
| 327 | variables["z"] = "equal" |
| 328 | else: |
| 329 | variables["z"] = "greater" |
| 330 | elif operation == "random": |
| 331 | if y < x: |
| 332 | x, y = y, x |
| 333 | variables["z"] = str(random.randrange(x, y)) |
| 334 | consume("@math", knowledge) |
| 335 | produce(("@math", ["$z"]), knowledge, variables) |
| 336 | return True |
| 337 | except Exception as e: |
| 338 | print(e) |
| 339 | pass |
| 340 | return False |
| 341 | |
| 342 | def arrays(): |
| 343 | arrays = {} |
| 344 | def create(ruleset, knowledge, snapshots=[], trace=False): |
| 345 | nonlocal arrays |
| 346 | if (variables := match(("@array", ["create", "$array", "$size"]), knowledge)) is not None: |
| 347 | try: |
| 348 | array = variables["array"] |
| 349 | size = int(variables["size"]) |
| 350 | consume("@array", knowledge) |
| 351 | if array in arrays: |
| 352 | produce(("@array", ["$array", "exists"]), knowledge, variables) |
| 353 | else: |
| 354 | arrays[array] = [None] * size |
| 355 | return True |
| 356 | except: |
| 357 | pass |
| 358 | return False |
| 359 | def set(ruleset, knowledge, snapshots=[], trace=False): |
| 360 | nonlocal arrays |
| 361 | if (variables := match(("@array", ["set", "$array", "$index", "$value"]), knowledge)) is not None: |
| 362 | try: |
| 363 | array = variables["array"] |
| 364 | index = int(variables["index"]) |
| 365 | value = variables["value"] |
| 366 | consume("@array", knowledge) |
| 367 | if array not in arrays: |
| 368 | produce(("@array", ["$array", "not", "found"]), knowledge, variables) |
| 369 | elif index >= len(arrays[array]): |
| 370 | produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables) |
| 371 | else: |
| 372 | arrays[array][index] = value |
| 373 | return True |
| 374 | except: |
| 375 | pass |
| 376 | return False |
| 377 | def get(ruleset, knowledge, snapshots=[], trace=False): |
| 378 | nonlocal arrays |
| 379 | if (variables := match(("@array", ["get", "$array", "$index"]), knowledge)) is not None: |
| 380 | try: |
| 381 | array = variables["array"] |
| 382 | index = int(variables["index"]) |
| 383 | consume("@array", knowledge) |
| 384 | if array not in arrays: |
| 385 | produce(("@array", ["$array", "not", "found"]), knowledge, variables) |
| 386 | elif index >= len(arrays[array]): |
| 387 | produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables) |
| 388 | else: |
| 389 | if arrays[array][index] == None: |
| 390 | produce(("@array", ["$array", "$index"]), knowledge, variables) |
| 391 | else: |
| 392 | variables["value"] = str(arrays[array][index]) |
| 393 | produce(("@array", ["$array", "$index", "$value"]), knowledge, variables) |
| 394 | return True |
| 395 | except: |
| 396 | pass |
| 397 | return False |
| 398 | return [ |
| 399 | create, |
| 400 | set, |
| 401 | get |
| 402 | ] |
| 403 | |
| 404 | def graphics(): |
| 405 | keys = {} |
| 406 | mouse_buttons = {} |
| 407 | screen = None |
| 408 | pixels = None |
| 409 | clock = None |
| 410 | font = None |
| 411 | resolution = (0, 0) |
| 412 | def poll_input(ruleset, knowledge, snapshots=[]): |
| 413 | nonlocal keys |
| 414 | nonlocal mouse_buttons |
| 415 | if match(("@input", ["poll-input"]), knowledge) is not None: |
| 416 | consume("@input", knowledge) |
| 417 | for event in pygame.event.get(): |
| 418 | if event.type == pygame.QUIT: |
| 419 | produce(("@signal", ["quit"]), knowledge) |
| 420 | keys = pygame.key.get_pressed() |
| 421 | mouse_buttons = pygame.mouse.get_pressed() |
| 422 | return True |
| 423 | return False |
| 424 | def check_key(ruleset, knowledge, snapshots=[]): |
| 425 | nonlocal keys |
| 426 | if (variables := match(("@input", ["check-key", "$key"]), knowledge)) is not None: |
| 427 | consume("@input", knowledge) |
| 428 | keycode = pygame.key.key_code(variables["key"]) |
| 429 | if keys[keycode]: |
| 430 | produce(("@input", ["key-pressed", "$key"]), knowledge, variables) |
| 431 | else: |
| 432 | produce(("@input", ["key-released", "$key"]), knowledge, variables) |
| 433 | return True |
| 434 | return False |
| 435 | def check_mouse_button(ruleset, knowledge, snapshots=[]): |
| 436 | nonlocal mouse_buttons |
| 437 | if (variables := match(("@input", ["check-mouse-button", "$button"]), knowledge)) is not None: |
| 438 | consume("@input", knowledge) |
| 439 | button = int(variables["button"]) |
| 440 | if mouse_buttons[button]: |
| 441 | produce(("@input", ["mouse-button-pressed", "$button"]), knowledge, variables) |
| 442 | else: |
| 443 | produce(("@input", ["mouse-button-released", "$button"]), knowledge, variables) |
| 444 | return True |
| 445 | return False |
| 446 | def get_mouse_position(ruleset, knowledge, snapshots=[]): |
| 447 | if (variables := match(("@input", ["get-mouse-position"]), knowledge)) is not None: |
| 448 | consume("@input", knowledge) |
| 449 | position = pygame.mouse.get_pos() |
| 450 | variables["x"], variables["y"] = (str(position[0]), str(position[1])) |
| 451 | produce(("@input", ["mouse-position", "$x", "$y"]), knowledge, variables) |
| 452 | return True |
| 453 | return False |
| 454 | def set_pixel(ruleset, knowledge, snapshots=[]): |
| 455 | nonlocal pixels |
| 456 | if (variables := match(("@graphics", ["set-pixel", "$x", "$y", "$r", "$g", "$b"]), knowledge)) is not None: |
| 457 | try: |
| 458 | x = int(variables["x"]) |
| 459 | y = int(variables["y"]) |
| 460 | r = int(variables["r"]) |
| 461 | g = int(variables["g"]) |
| 462 | b = int(variables["b"]) |
| 463 | pixels[x % resolution[0], y % resolution[1]] = (r % 256, g % 256, b % 256) |
| 464 | consume("@graphics", knowledge) |
| 465 | return True |
| 466 | except: |
| 467 | pass |
| 468 | return False |
| 469 | def draw_line(ruleset, knowledge, snapshots=[]): |
| 470 | nonlocal screen |
| 471 | if (variables := match(("@graphics", ["draw-line", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None: |
| 472 | try: |
| 473 | x1 = int(variables["x1"]) |
| 474 | y1 = int(variables["y1"]) |
| 475 | x2 = int(variables["x2"]) |
| 476 | y2 = int(variables["y2"]) |
| 477 | r = int(variables["r"]) |
| 478 | g = int(variables["g"]) |
| 479 | b = int(variables["b"]) |
| 480 | w = int(variables["w"]) |
| 481 | pygame.draw.line(screen, (r % 256, g % 256, b % 256), (x1, y1), (x2, y2), w) |
| 482 | consume("@graphics", knowledge) |
| 483 | return True |
| 484 | except: |
| 485 | pass |
| 486 | return False |
| 487 | def draw_rect(ruleset, knowledge, snapshots=[]): |
| 488 | nonlocal screen |
| 489 | if (variables := match(("@graphics", ["draw-rect", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None: |
| 490 | try: |
| 491 | x1 = int(variables["x1"]) |
| 492 | y1 = int(variables["y1"]) |
| 493 | x2 = int(variables["x2"]) |
| 494 | y2 = int(variables["y2"]) |
| 495 | r = int(variables["r"]) |
| 496 | g = int(variables["g"]) |
| 497 | b = int(variables["b"]) |
| 498 | w = int(variables["w"]) |
| 499 | pygame.draw.rect(screen, (r % 256, g % 256, b % 256), (x1, y1, x2, y2), w) |
| 500 | consume("@graphics", knowledge) |
| 501 | return True |
| 502 | except: |
| 503 | pass |
| 504 | return False |
| 505 | def draw_circle(ruleset, knowledge, snapshots=[]): |
| 506 | nonlocal screen |
| 507 | if (variables := match(("@graphics", ["draw-circle", "$x", "$y", "$d", "$r", "$g", "$b", "$w"]), knowledge)) is not None: |
| 508 | try: |
| 509 | x = int(variables["x"]) |
| 510 | y = int(variables["y"]) |
| 511 | d = int(variables["d"]) |
| 512 | r = int(variables["r"]) |
| 513 | g = int(variables["g"]) |
| 514 | b = int(variables["b"]) |
| 515 | w = int(variables["w"]) |
| 516 | pygame.draw.circle(screen, (r % 256, g % 256, b % 256), (x, y), d, w) |
| 517 | consume("@graphics", knowledge) |
| 518 | return True |
| 519 | except: |
| 520 | pass |
| 521 | return False |
| 522 | def clear_screen(ruleset, knowledge, snapshots=[]): |
| 523 | nonlocal screen |
| 524 | if (variables := match(("@graphics", ["clear-screen", "$r", "$g", "$b"]), knowledge)) is not None: |
| 525 | try: |
| 526 | r = int(variables["r"]) |
| 527 | g = int(variables["g"]) |
| 528 | b = int(variables["b"]) |
| 529 | screen.fill((r % 256, g % 256, b % 256)) |
| 530 | consume("@graphics", knowledge) |
| 531 | return True |
| 532 | except: |
| 533 | pass |
| 534 | return False |
| 535 | def draw_fps(ruleset, knowledge, snapshots=[]): |
| 536 | nonlocal pixels |
| 537 | nonlocal screen |
| 538 | nonlocal clock |
| 539 | nonlocal font |
| 540 | if match(("@graphics", ["draw-fps"]), knowledge) is not None: |
| 541 | if clock is None: |
| 542 | clock = pygame.time.Clock() |
| 543 | clock.tick() |
| 544 | if font is None: |
| 545 | font = pygame.font.SysFont("m3x6", 16) |
| 546 | framerate = clock.get_fps() |
| 547 | if framerate <= 1_000_000: |
| 548 | fps = font.render(str(int(clock.get_fps())) , 1, pygame.Color("RED")) |
| 549 | pixels.close() |
| 550 | screen.blit(fps, (0, 0)) |
| 551 | pixels = pygame.PixelArray(screen) |
| 552 | consume("@graphics", knowledge) |
| 553 | return True |
| 554 | return False |
| 555 | def display(ruleset, knowledge, snapshots=[]): |
| 556 | nonlocal pixels |
| 557 | if match(("@graphics", ["display"]), knowledge) is not None: |
| 558 | pixels.close() |
| 559 | pygame.display.flip() |
| 560 | pixels = pygame.PixelArray(screen) |
| 561 | consume("@graphics", knowledge) |
| 562 | return True |
| 563 | return False |
| 564 | def draw_text(ruleset, knowledge, snapshots=[]): |
| 565 | nonlocal font |
| 566 | nonlocal screen |
| 567 | nonlocal pixels |
| 568 | if (variables := match(("@graphics", ["draw-text", "$x", "$y"]), knowledge)) is not None: |
| 569 | x = int(variables["x"]) |
| 570 | y = int(variables["y"]) |
| 571 | if font is None: |
| 572 | font = pygame.font.SysFont("m3x6", 16) |
| 573 | text = ''.join(chr(int(n[0])) for n in reversed(knowledge["@text to draw"][0])) |
| 574 | knowledge["@text to draw"] = [[], 0] |
| 575 | drawn_text = font.render(text, False, pygame.Color("WHITE")) |
| 576 | pixels.close() |
| 577 | pygame.draw.rect(screen, (0, 0, 0), (0, 48, 64, 64)) |
| 578 | screen.blit(drawn_text, (x, y)) |
| 579 | pixels = pygame.PixelArray(screen) |
| 580 | consume("@graphics", knowledge) |
| 581 | return True |
| 582 | return False |
| 583 | |
| 584 | |
| 585 | def set_resolution(ruleset, knowledge, snapshots=[]): |
| 586 | nonlocal screen |
| 587 | nonlocal pixels |
| 588 | nonlocal resolution |
| 589 | if (variables := match(("@graphics", ["set-resolution", "$x", "$y"]), knowledge)) is not None: |
| 590 | try: |
| 591 | if screen is None: |
| 592 | pygame.init() |
| 593 | x = int(variables["x"]) |
| 594 | y = int(variables["y"]) |
| 595 | screen = pygame.display.set_mode((x, y)) |
| 596 | pygame.display.set_caption("Nova") |
| 597 | icon = pygame.Surface((32, 32)) |
| 598 | icon.fill("black") |
| 599 | icon.set_colorkey((0, 0, 0)) |
| 600 | pygame.display.set_icon(icon) |
| 601 | pixels = pygame.PixelArray(screen) |
| 602 | resolution = (x, y) |
| 603 | consume("@graphics", knowledge) |
| 604 | return True |
| 605 | except: |
| 606 | pass |
| 607 | elif (variables := match(("@graphics", ["set-resolution", "fullscreen", "$x", "$y"]), knowledge)) is not None: |
| 608 | try: |
| 609 | if screen is None: |
| 610 | pygame.init() |
| 611 | x = int(variables["x"]) |
| 612 | y = int(variables["y"]) |
| 613 | screen = pygame.display.set_mode((x, y), flags=pygame.FULLSCREEN | pygame.SCALED) |
| 614 | pygame.display.set_caption("Nova") |
| 615 | icon = pygame.Surface((32, 32)) |
| 616 | icon.fill("black") |
| 617 | icon.set_colorkey((0, 0, 0)) |
| 618 | pygame.display.set_icon(icon) |
| 619 | pixels = pygame.PixelArray(screen) |
| 620 | resolution = (x, y) |
| 621 | consume("@graphics", knowledge) |
| 622 | return True |
| 623 | except: |
| 624 | pass |
| 625 | elif (variables := match(("@graphics", ["set-resolution", "fullscreen"]), knowledge)) is not None: |
| 626 | try: |
| 627 | if screen is None: |
| 628 | pygame.init() |
| 629 | screen = pygame.display.set_mode((0, 0), flags=pygame.FULLSCREEN) |
| 630 | pygame.display.set_caption("Nova") |
| 631 | icon = pygame.Surface((32, 32)) |
| 632 | icon.fill("black") |
| 633 | icon.set_colorkey((0, 0, 0)) |
| 634 | pygame.display.set_icon(icon) |
| 635 | pixels = pygame.PixelArray(screen) |
| 636 | resolution = pygame.display.get_surface().get_size() |
| 637 | consume("@graphics", knowledge) |
| 638 | return True |
| 639 | except: |
| 640 | pass |
| 641 | return False |
| 642 | return [ |
| 643 | set_pixel, |
| 644 | draw_line, |
| 645 | draw_rect, |
| 646 | draw_circle, |
| 647 | draw_text, |
| 648 | display, |
| 649 | poll_input, |
| 650 | draw_fps, |
| 651 | clear_screen, |
| 652 | check_key, |
| 653 | check_mouse_button, |
| 654 | get_mouse_position, |
| 655 | set_resolution |
| 656 | ] |
| 657 | |
| 658 | def midi(): |
| 659 | output = None |
| 660 | def set_output(ruleset, knowledge, snapshots=[]): |
| 661 | nonlocal output |
| 662 | if (variables := match(("@midi", ["set-output", "$x"]), knowledge)) is not None: |
| 663 | try: |
| 664 | if output is None: |
| 665 | pygame.midi.init() |
| 666 | else: |
| 667 | del output |
| 668 | output = pygame.midi.Output(int(variables["x"])) |
| 669 | consume("@midi", knowledge) |
| 670 | return True |
| 671 | except: |
| 672 | pass |
| 673 | return False |
| 674 | def abort(ruleset, knowledge, snapshots=[]): |
| 675 | nonlocal output |
| 676 | if (variables := match(("@midi", ["abort"]), knowledge)) is not None: |
| 677 | try: |
| 678 | output.abort() |
| 679 | consume("@midi", knowledge) |
| 680 | return True |
| 681 | except: |
| 682 | pass |
| 683 | return False |
| 684 | def set_instrument(ruleset, knowledge, snapshots=[]): |
| 685 | nonlocal output |
| 686 | if (variables := match(("@midi", ["set-instrument", "$x"]), knowledge)) is not None: |
| 687 | try: |
| 688 | output.set_instrument(int(variables["x"])) |
| 689 | consume("@midi", knowledge) |
| 690 | return True |
| 691 | except: |
| 692 | pass |
| 693 | return False |
| 694 | def note(ruleset, knowledge, snapshots=[]): |
| 695 | nonlocal output |
| 696 | if (variables := match(("@midi", ["note", "$x", "$y", "$z", "$w"]), knowledge)) is not None: |
| 697 | try: |
| 698 | state = variables["x"] |
| 699 | note = int(variables["y"]) |
| 700 | velocity = int(variables["z"]) |
| 701 | channel = int(variables["w"]) |
| 702 | if state == "on": |
| 703 | output.note_on(note, velocity, channel) |
| 704 | elif state == "off": |
| 705 | output.note_off(note, velocity, channel) |
| 706 | else: |
| 707 | return False |
| 708 | consume("@midi", knowledge) |
| 709 | return True |
| 710 | except: |
| 711 | pass |
| 712 | elif (variables := match(("@midi", ["note", "$x", "$y", "$z"]), knowledge)) is not None: |
| 713 | try: |
| 714 | state = variables["x"] |
| 715 | note = int(variables["y"]) |
| 716 | velocity = int(variables["z"]) |
| 717 | if state == "on": |
| 718 | output.note_on(note, velocity) |
| 719 | elif state == "off": |
| 720 | output.note_off(note, velocity) |
| 721 | else: |
| 722 | return False |
| 723 | consume("@midi", knowledge) |
| 724 | return True |
| 725 | except: |
| 726 | pass |
| 727 | return False |
| 728 | return [ |
| 729 | set_output, |
| 730 | abort, |
| 731 | set_instrument, |
| 732 | note |
| 733 | ] |
| 734 | |
| 735 | def _time(): |
| 736 | def sleep(ruleset, knowledge, snapshots=[]): |
| 737 | if (variables := match(("@time", ["sleep", "$x", "$y"]), knowledge)) is not None: |
| 738 | try: |
| 739 | duration = int(variables["x"]) |
| 740 | unit = variables["y"] |
| 741 | if unit == "seconds": |
| 742 | time.sleep(duration) |
| 743 | elif unit == "milliseconds": |
| 744 | time.sleep(0.001 * duration) |
| 745 | else: |
| 746 | return False |
| 747 | consume("@time", knowledge) |
| 748 | return True |
| 749 | except: |
| 750 | pass |
| 751 | return False |
| 752 | return [ |
| 753 | sleep |
| 754 | ] |
| 755 | |
| 756 | def stdio(): |
| 757 | def read(ruleset, knowledge, snapshots=[]): |
| 758 | if (variables := match(("@stdio", ["read", "$x"]), knowledge)) is not None: |
| 759 | try: |
| 760 | amount = int(variables["x"]) |
| 761 | bytes = sys.stdin.read(amount) |
| 762 | consume("@stdio", knowledge) |
| 763 | for byte in reversed(bytes.encode()): |
| 764 | variables["x"] = str(byte) |
| 765 | produce(("@stdio", ["$x"]), knowledge, variables) |
| 766 | return True |
| 767 | except KeyboardInterrupt: |
| 768 | consume("@stdio", knowledge) |
| 769 | return True |
| 770 | except: |
| 771 | pass |
| 772 | elif (variables := match(("@stdio", ["read"]), knowledge)) is not None: |
| 773 | try: |
| 774 | variables["x"] = str(ord(sys.stdin.read(1))) |
| 775 | consume("@stdio", knowledge) |
| 776 | produce(("@stdio", ["$x"]), knowledge, variables) |
| 777 | return True |
| 778 | except KeyboardInterrupt: |
| 779 | consume("@stdio", knowledge) |
| 780 | return True |
| 781 | except: |
| 782 | pass |
| 783 | return False |
| 784 | def write(ruleset, knowledge, snapshots=[]): |
| 785 | if (variables := match(("@stdio", ["write", "$x", "$y"]), knowledge)) is not None: |
| 786 | try: |
| 787 | byte = int(variables["x"]) |
| 788 | length = int(variables["y"]) |
| 789 | if byte < 0: |
| 790 | sys.stdout.buffer.write(byte.to_bytes(length, signed=True)) |
| 791 | else: |
| 792 | sys.stdout.buffer.write(byte.to_bytes(length)) |
| 793 | sys.stdout.buffer.flush() |
| 794 | consume("@stdio", knowledge) |
| 795 | variables["x"] = str(length) |
| 796 | produce(("@stdio", ["wrote", "$x"]), knowledge, variables) |
| 797 | return True |
| 798 | except OverflowError: |
| 799 | length = (byte.bit_length() + 7) // 8 |
| 800 | if length == 0: |
| 801 | length = 1 |
| 802 | if byte < 0: |
| 803 | sys.stdout.buffer.write(byte.to_bytes(length, signed=True)) |
| 804 | else: |
| 805 | sys.stdout.buffer.write(byte.to_bytes(length)) |
| 806 | sys.stdout.buffer.flush() |
| 807 | consume("@stdio", knowledge) |
| 808 | variables["x"] = str(length) |
| 809 | produce(("@stdio", ["wrote", "$x"]), knowledge, variables) |
| 810 | return True |
| 811 | except: |
| 812 | pass |
| 813 | elif (variables := match(("@stdio", ["write", "$x"]), knowledge)) is not None: |
| 814 | try: |
| 815 | byte = int(variables["x"]) |
| 816 | length = (byte.bit_length() + 7) // 8 |
| 817 | if length == 0: |
| 818 | length = 1 |
| 819 | if byte < 0: |
| 820 | sys.stdout.buffer.write(byte.to_bytes(length, signed=True)) |
| 821 | else: |
| 822 | sys.stdout.buffer.write(byte.to_bytes(length)) |
| 823 | sys.stdout.buffer.flush() |
| 824 | consume("@stdio", knowledge) |
| 825 | variables["x"] = str(length) |
| 826 | produce(("@stdio", ["wrote", "$x"]), knowledge, variables) |
| 827 | return True |
| 828 | except: |
| 829 | pass |
| 830 | return False |
| 831 | return [ |
| 832 | read, write |
| 833 | ] |
| 834 | |
| 835 | def _nfc(): |
| 836 | clf = None |
| 837 | last_card = None |
| 838 | def nfc_read_card(): |
| 839 | nonlocal clf |
| 840 | payload = [] |
| 841 | identifier = None |
| 842 | card_types = [RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F')] |
| 843 | target = clf.sense(*card_types) |
| 844 | if target is not None: |
| 845 | tag = nfc.tag.activate(clf, target) |
| 846 | if tag is not None and tag.ndef is not None: |
| 847 | for record in tag.ndef.records: |
| 848 | text = zlib.decompress(base64.b64decode(record.text)).decode() |
| 849 | payload += list(parse(text)) |
| 850 | identifier = tag.identifier |
| 851 | return (payload, identifier) |
| 852 | def open(ruleset, knowledge, snapshots=[]): |
| 853 | nonlocal clf |
| 854 | if (variables := match(("@nfc", ["open"]), knowledge)) is not None: |
| 855 | try: |
| 856 | consume("@nfc", knowledge) |
| 857 | clf = nfc.ContactlessFrontend('usb') |
| 858 | return True |
| 859 | except: |
| 860 | produce(("@nfc", ["no-device"]), knowledge) |
| 861 | return True |
| 862 | return False |
| 863 | def close(ruleset, knowledge, snapshots=[]): |
| 864 | nonlocal clf |
| 865 | if (variables := match(("@nfc", ["close"]), knowledge)) is not None: |
| 866 | try: |
| 867 | consume("@nfc", knowledge) |
| 868 | clf.close() |
| 869 | return True |
| 870 | except: |
| 871 | produce(("@nfc", ["no-device"]), knowledge) |
| 872 | return True |
| 873 | return False |
| 874 | def read_card(ruleset, knowledge, snapshots=[]): |
| 875 | nonlocal last_card |
| 876 | if (variables := match(("@nfc", ["read-card"]), knowledge)) is not None: |
| 877 | try: |
| 878 | consume("@nfc", knowledge) |
| 879 | (payload, identifier) = nfc_read_card() |
| 880 | variables["x"] = "".join([hex(byte)[2:] for byte in identifier]) |
| 881 | last_card = payload |
| 882 | produce(("@nfc", ["card", "$x"]), knowledge, variables) |
| 883 | return True |
| 884 | except: |
| 885 | produce(("@nfc", ["read-failed"]), knowledge) |
| 886 | return True |
| 887 | return False |
| 888 | def load_card_rules(ruleset, knowledge, snapshots=[]): |
| 889 | nonlocal last_card |
| 890 | if (variables := match(("@nfc", ["load-rules"]), knowledge)) is not None: |
| 891 | try: |
| 892 | for rule in rules(reversed(last_card)): |
| 893 | ruleset.insert(0, rule) |
| 894 | consume("@nfc", knowledge) |
| 895 | return True |
| 896 | except: |
| 897 | pass |
| 898 | return False |
| 899 | def load_card_facts(ruleset, knowledge, snapshots=[]): |
| 900 | if (variables := match(("@nfc", ["load-facts"]), knowledge)) is not None: |
| 901 | try: |
| 902 | prepare(last_card, knowledge) |
| 903 | consume("@nfc", knowledge) |
| 904 | return True |
| 905 | except: |
| 906 | pass |
| 907 | return False |
| 908 | return [ |
| 909 | open, |
| 910 | close, |
| 911 | read_card, |
| 912 | load_card_rules, |
| 913 | load_card_facts |
| 914 | ] |
| 915 | |
| 916 | def repl(rulesets, primitives, debug): |
| 917 | try: |
| 918 | knowledge, _ = run(rulesets) |
| 919 | print_knowledge(knowledge, debug) |
| 920 | current_stack = "" |
| 921 | command = input("::> ") |
| 922 | while command != "@quit": |
| 923 | if command in {"@run", '!'}: |
| 924 | knowledge, _ = run(rulesets, primitives, knowledge) |
| 925 | if debug: |
| 926 | print_knowledge(knowledge, debug) |
| 927 | if match(("@signal", ["quit"]), knowledge) != None: |
| 928 | break |
| 929 | elif command.startswith('@'): |
| 930 | current_stack = command[1:] |
| 931 | elif command.startswith('.'): |
| 932 | command = command[1:] |
| 933 | knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug) |
| 934 | if debug: |
| 935 | print_knowledge(knowledge, debug) |
| 936 | if match(("@signal", ["quit"]), knowledge) != None: |
| 937 | break |
| 938 | elif command.startswith(':'): |
| 939 | command = command[1:] |
| 940 | rulesets = list(parse(command)) + rulesets |
| 941 | knowledge, _ = run(rulesets, primitives, knowledge, debug) |
| 942 | if debug: |
| 943 | print_knowledge(knowledge, debug) |
| 944 | if match(("@signal", ["quit"]), knowledge) != None: |
| 945 | break |
| 946 | elif len(command) != 0: |
| 947 | command = "||:" + current_stack + ':' + command |
| 948 | knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug) |
| 949 | if debug: |
| 950 | print_knowledge(knowledge, debug) |
| 951 | if match(("@signal", ["quit"]), knowledge) != None: |
| 952 | break |
| 953 | else: |
| 954 | if debug: |
| 955 | print_knowledge(knowledge, debug) |
| 956 | command = input(":" + current_stack + ":> ") |
| 957 | except EOFError: |
| 958 | pass |
| 959 | except KeyboardInterrupt: |
| 960 | pass |
| 961 | |
| 962 | def main(name, arguments): |
| 963 | primitives = _time() + [math] + arrays() + stdio() |
| 964 | if pygame_supported: |
| 965 | primitives += graphics() + midi() |
| 966 | if nfc_supported: |
| 967 | primitives += _nfc() |
| 968 | rulesets = [] |
| 969 | debug = 0 |
| 970 | prompt = False |
| 971 | trace = False |
| 972 | if not arguments: |
| 973 | usage(name) |
| 974 | return |
| 975 | for argument in arguments: |
| 976 | if argument == '-d': |
| 977 | debug += 1 |
| 978 | elif argument == '-r': |
| 979 | prompt = True |
| 980 | elif argument == '-t': |
| 981 | trace = True |
| 982 | else: |
| 983 | with open(argument) as file: |
| 984 | rulesets += list(parse(file.read())) |
| 985 | if prompt: |
| 986 | repl(rulesets, primitives, debug) |
| 987 | else: |
| 988 | if len(rulesets) == 0: |
| 989 | usage(name) |
| 990 | return |
| 991 | if debug: |
| 992 | knowledge, snapshots = run(rulesets, primitives, None, debug, trace) |
| 993 | if trace: |
| 994 | for index in range(len(snapshots)): |
| 995 | print("Snapshot", str(index) + ':') |
| 996 | print("----------------") |
| 997 | print_knowledge(snapshots[index], debug) |
| 998 | print("") |
| 999 | else: |
| 1000 | print_knowledge(knowledge, debug) |
| 1001 | else: |
| 1002 | run(rulesets, primitives, None, debug, trace) |
| 1003 | |
| 1004 | if __name__ == "__main__": |
| 1005 | main(sys.argv[0], sys.argv[1:]) |
| 1006 |