sonolus.py 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +77 -69
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +29 -3
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/optimize/allocate.py +126 -0
- sonolus/backend/optimize/constant_evaluation.py +374 -0
- sonolus/backend/optimize/copy_coalesce.py +85 -0
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/optimize/dominance.py +96 -0
- sonolus/backend/{flow.py → optimize/flow.py} +122 -92
- sonolus/backend/optimize/inlining.py +137 -0
- sonolus/backend/optimize/liveness.py +177 -0
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/optimize/passes.py +52 -0
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/optimize/ssa.py +200 -0
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +58 -48
- sonolus/backend/visitor.py +1151 -882
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +10 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +438 -139
- sonolus/script/array.py +27 -10
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +253 -191
- sonolus/script/containers.py +257 -51
- sonolus/script/debug.py +26 -10
- sonolus/script/easing.py +365 -0
- sonolus/script/effect.py +191 -131
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +303 -269
- sonolus/script/instruction.py +205 -151
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +255 -144
- sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +26 -9
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +17 -14
- sonolus/script/internal/math_impls.py +121 -0
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +338 -112
- sonolus/script/iterator.py +167 -214
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +80 -48
- sonolus/script/options.py +257 -191
- sonolus/script/particle.py +190 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/print.py +102 -81
- sonolus/script/project.py +8 -0
- sonolus/script/quad.py +263 -0
- sonolus/script/record.py +47 -16
- sonolus/script/runtime.py +52 -1
- sonolus/script/sprite.py +418 -333
- sonolus/script/text.py +409 -407
- sonolus/script/timing.py +114 -42
- sonolus/script/transform.py +332 -48
- sonolus/script/ui.py +216 -160
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +196 -78
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
- sonolus/backend/allocate.py +0 -51
- sonolus/backend/optimize.py +0 -9
- sonolus/backend/passes.py +0 -6
- sonolus/backend/simplify.py +0 -30
- sonolus/script/comptime.py +0 -160
- sonolus/script/graphics.py +0 -150
- sonolus/script/math.py +0 -92
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.3.dist-info/RECORD +0 -75
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/script/bucket.py
CHANGED
|
@@ -1,191 +1,253 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from enum import IntEnum
|
|
5
|
-
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
6
|
-
|
|
7
|
-
from sonolus.backend.mode import Mode
|
|
8
|
-
from sonolus.backend.ops import Op
|
|
9
|
-
from sonolus.script.internal.context import ctx
|
|
10
|
-
from sonolus.script.internal.impl import meta_fn
|
|
11
|
-
from sonolus.script.internal.introspection import get_field_specifiers
|
|
12
|
-
from sonolus.script.internal.native import native_function
|
|
13
|
-
from sonolus.script.interval import Interval
|
|
14
|
-
from sonolus.script.pointer import
|
|
15
|
-
from sonolus.script.record import Record
|
|
16
|
-
from sonolus.script.sprite import Sprite
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class JudgmentWindow(Record):
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
6
|
+
|
|
7
|
+
from sonolus.backend.mode import Mode
|
|
8
|
+
from sonolus.backend.ops import Op
|
|
9
|
+
from sonolus.script.internal.context import ctx
|
|
10
|
+
from sonolus.script.internal.impl import meta_fn
|
|
11
|
+
from sonolus.script.internal.introspection import get_field_specifiers
|
|
12
|
+
from sonolus.script.internal.native import native_function
|
|
13
|
+
from sonolus.script.interval import Interval
|
|
14
|
+
from sonolus.script.pointer import _deref
|
|
15
|
+
from sonolus.script.record import Record
|
|
16
|
+
from sonolus.script.sprite import Sprite
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JudgmentWindow(Record):
|
|
20
|
+
"""The window for judging the accuracy of a hit.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
```
|
|
24
|
+
JudgmentWindow(perfect: Interval, great: Interval, good: Interval)
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
perfect: Interval
|
|
29
|
+
"""Interval for a perfect hit."""
|
|
30
|
+
|
|
31
|
+
great: Interval
|
|
32
|
+
"""Interval for a great hit."""
|
|
33
|
+
|
|
34
|
+
good: Interval
|
|
35
|
+
"""Interval for a good hit."""
|
|
36
|
+
|
|
37
|
+
def update(
|
|
38
|
+
self,
|
|
39
|
+
perfect: Interval | None = None,
|
|
40
|
+
great: Interval | None = None,
|
|
41
|
+
good: Interval | None = None,
|
|
42
|
+
):
|
|
43
|
+
"""Update the window with the given intervals.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
perfect: The interval for a perfect hit.
|
|
47
|
+
great: The interval for a great hit.
|
|
48
|
+
good: The interval for a good hit.
|
|
49
|
+
"""
|
|
50
|
+
if perfect is not None:
|
|
51
|
+
self.perfect = perfect
|
|
52
|
+
if great is not None:
|
|
53
|
+
self.great = great
|
|
54
|
+
if good is not None:
|
|
55
|
+
self.good = good
|
|
56
|
+
|
|
57
|
+
def judge(self, actual: float, target: float) -> Judgment:
|
|
58
|
+
"""Judge the accuracy of a hit.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
actual: The actual time of the hit.
|
|
62
|
+
target: The target time of the hit.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
The judgment of the hit.
|
|
66
|
+
"""
|
|
67
|
+
return _judge(
|
|
68
|
+
actual,
|
|
69
|
+
target,
|
|
70
|
+
*self.perfect.tuple,
|
|
71
|
+
*self.great.tuple,
|
|
72
|
+
*self.good.tuple,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def __mul__(self, other: float | int) -> JudgmentWindow:
|
|
76
|
+
"""Multiply the intervals by a scalar."""
|
|
77
|
+
return JudgmentWindow(
|
|
78
|
+
self.perfect * other,
|
|
79
|
+
self.great * other,
|
|
80
|
+
self.good * other,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Judgment(IntEnum):
|
|
85
|
+
"""The judgment of a hit."""
|
|
86
|
+
|
|
87
|
+
MISS = 0
|
|
88
|
+
PERFECT = 1
|
|
89
|
+
GREAT = 2
|
|
90
|
+
GOOD = 3
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@native_function(Op.Judge)
|
|
94
|
+
def _judge(
|
|
95
|
+
actual: float,
|
|
96
|
+
target: float,
|
|
97
|
+
perfect_min: float,
|
|
98
|
+
perfect_max: float,
|
|
99
|
+
great_min: float,
|
|
100
|
+
great_max: float,
|
|
101
|
+
good_min: float,
|
|
102
|
+
good_max: float,
|
|
103
|
+
) -> Judgment:
|
|
104
|
+
diff = actual - target
|
|
105
|
+
if perfect_min <= diff <= perfect_max:
|
|
106
|
+
return Judgment.PERFECT
|
|
107
|
+
if great_min <= diff <= great_max:
|
|
108
|
+
return Judgment.GREAT
|
|
109
|
+
if good_min <= diff <= good_max:
|
|
110
|
+
return Judgment.GOOD
|
|
111
|
+
return Judgment.MISS
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Bucket(Record):
|
|
115
|
+
"""A bucket for entity judgment results.
|
|
116
|
+
|
|
117
|
+
Usage:
|
|
118
|
+
```Bucket(id: int)```
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
id: int
|
|
122
|
+
"""Bucket ID."""
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
@meta_fn
|
|
126
|
+
def window(self) -> JudgmentWindow:
|
|
127
|
+
"""The judgment window of the bucket."""
|
|
128
|
+
if not ctx():
|
|
129
|
+
raise RuntimeError("Bucket window access outside of compilation")
|
|
130
|
+
match ctx().global_state.mode:
|
|
131
|
+
case Mode.PLAY:
|
|
132
|
+
return _deref(ctx().blocks.LevelBucket, self.id * JudgmentWindow._size_(), JudgmentWindow)
|
|
133
|
+
case Mode.WATCH:
|
|
134
|
+
return _deref(ctx().blocks.LevelBucket, self.id * JudgmentWindow._size_(), JudgmentWindow)
|
|
135
|
+
case _:
|
|
136
|
+
raise RuntimeError("Invalid mode for bucket window access")
|
|
137
|
+
|
|
138
|
+
@window.setter
|
|
139
|
+
@meta_fn
|
|
140
|
+
def window(self, value: JudgmentWindow):
|
|
141
|
+
if not ctx():
|
|
142
|
+
raise RuntimeError("Bucket window access outside of compilation")
|
|
143
|
+
self.window.update(value.perfect, value.great, value.good)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class _BucketSprite:
|
|
148
|
+
id: int
|
|
149
|
+
fallback_id: int | None
|
|
150
|
+
x: int
|
|
151
|
+
y: int
|
|
152
|
+
w: int
|
|
153
|
+
h: int
|
|
154
|
+
rotation: float
|
|
155
|
+
|
|
156
|
+
def to_dict(self):
|
|
157
|
+
results = {
|
|
158
|
+
"id": self.id,
|
|
159
|
+
"x": self.x,
|
|
160
|
+
"y": self.y,
|
|
161
|
+
"w": self.w,
|
|
162
|
+
"h": self.h,
|
|
163
|
+
"rotation": self.rotation,
|
|
164
|
+
}
|
|
165
|
+
if self.fallback_id is not None:
|
|
166
|
+
results["fallbackId"] = self.fallback_id
|
|
167
|
+
return results
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@dataclass
|
|
171
|
+
class _BucketInfo:
|
|
172
|
+
sprites: list[_BucketSprite]
|
|
173
|
+
unit: str | None = None
|
|
174
|
+
|
|
175
|
+
def to_dict(self):
|
|
176
|
+
results = {
|
|
177
|
+
"sprites": [sprite.to_dict() for sprite in self.sprites],
|
|
178
|
+
}
|
|
179
|
+
if self.unit is not None:
|
|
180
|
+
results["unit"] = self.unit
|
|
181
|
+
return results
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def bucket_sprite(
|
|
185
|
+
*,
|
|
186
|
+
sprite: Sprite,
|
|
187
|
+
fallback_sprite: Sprite | None = None,
|
|
188
|
+
x: int,
|
|
189
|
+
y: int,
|
|
190
|
+
w: int,
|
|
191
|
+
h: int,
|
|
192
|
+
rotation: float = 0,
|
|
193
|
+
) -> _BucketSprite:
|
|
194
|
+
"""Define a sprite for a bucket."""
|
|
195
|
+
return _BucketSprite(sprite.id, fallback_sprite.id if fallback_sprite else None, x, y, w, h, rotation)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def bucket(*, sprites: list[_BucketSprite], unit: str | None = None) -> Any:
|
|
199
|
+
"""Define a bucket with the given sprites and unit."""
|
|
200
|
+
return _BucketInfo(sprites, unit)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
type Buckets = NewType("Buckets", Any)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass_transform()
|
|
207
|
+
def buckets[T](cls: type[T]) -> T | Buckets:
|
|
208
|
+
"""Decorator to define a buckets class.
|
|
209
|
+
|
|
210
|
+
Usage:
|
|
211
|
+
```python
|
|
212
|
+
@buckets
|
|
213
|
+
class Buckets:
|
|
214
|
+
note: Bucket = bucket(
|
|
215
|
+
sprites=[
|
|
216
|
+
bucket_sprite(
|
|
217
|
+
sprite=Skin.note,
|
|
218
|
+
x=0,
|
|
219
|
+
y=0,
|
|
220
|
+
w=2,
|
|
221
|
+
h=2,
|
|
222
|
+
)
|
|
223
|
+
],
|
|
224
|
+
unit=StandardText.MILLISECOND_UNIT,
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
"""
|
|
228
|
+
if len(cls.__bases__) != 1:
|
|
229
|
+
raise ValueError("Buckets class must not inherit from any class (except object)")
|
|
230
|
+
instance = cls()
|
|
231
|
+
bucket_info = []
|
|
232
|
+
for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
|
|
233
|
+
if get_origin(annotation) is not Annotated:
|
|
234
|
+
raise TypeError(f"Invalid annotation for buckets: {annotation}")
|
|
235
|
+
annotation_type = annotation.__args__[0]
|
|
236
|
+
annotation_values = annotation.__metadata__
|
|
237
|
+
if annotation_type is not Bucket:
|
|
238
|
+
raise TypeError(f"Invalid annotation for buckets: {annotation}, expected annotation of type Bucket")
|
|
239
|
+
if len(annotation_values) != 1 or not isinstance(annotation_values[0], _BucketInfo):
|
|
240
|
+
raise TypeError(
|
|
241
|
+
f"Invalid annotation for buckets: {annotation}, expected a single BucketInfo annotation value"
|
|
242
|
+
)
|
|
243
|
+
info = annotation_values[0]
|
|
244
|
+
bucket_info.append(info)
|
|
245
|
+
setattr(instance, name, Bucket(i))
|
|
246
|
+
instance._buckets_ = bucket_info
|
|
247
|
+
instance._is_comptime_value_ = True
|
|
248
|
+
return instance
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@buckets
|
|
252
|
+
class EmptyBuckets:
|
|
253
|
+
pass
|