Compare commits
71 Commits
20531d1154
...
cat-town
Author | SHA1 | Date | |
---|---|---|---|
09ee33a751
|
|||
ee9fb1955c
|
|||
41f53c34c0
|
|||
6c0083e094
|
|||
7f2f579241
|
|||
7d2ac20ce1
|
|||
8f38a3b271
|
|||
96f5f85a27
|
|||
6cf80e09b1
|
|||
30ed115ed0
|
|||
8376608a1e | |||
3de96b813f
|
|||
a73acedc6d
|
|||
92e3d6fe5b
|
|||
395006cdb8
|
|||
622d47071a
|
|||
1452d544bb
|
|||
56c6a8cf09
|
|||
cc29386cee
|
|||
95453bf197
|
|||
a6526af6ff
|
|||
3ea30a997a
|
|||
03edacc8ec
|
|||
d1ffbe25a3
|
|||
dc3a40b19d
|
|||
f2e0de1b66
|
|||
58842504c4
|
|||
1080737931
|
|||
de6e907c60
|
|||
9989801e62
|
|||
8c280c303e
|
|||
b2e61cd0d2
|
|||
c15bafe55d
|
|||
af7d5c6e14
|
|||
512eddc1ab
|
|||
2bc9788441
|
|||
c52d9b9563
|
|||
fbb04df86d
|
|||
e30cbe2aca
|
|||
116ad347db
|
|||
1493deac96
|
|||
a7e45b8add
|
|||
f1cc614836
|
|||
a2661f4369
|
|||
b020618d57
|
|||
d2405f75d8
|
|||
1d827b517b
|
|||
47d658f04f
|
|||
6c45a1390b
|
|||
b25e99a632
|
|||
abd957d42f
|
|||
d326a055f7
|
|||
6d6e600720
|
|||
d2563484f9
|
|||
eac3c2d8a6
|
|||
052fb87667
|
|||
9f431fcb11
|
|||
7e6a2ea52b
|
|||
f45663f248
|
|||
951c3bcebe
|
|||
f57429613a
|
|||
7e976ccc6f
|
|||
278ec88944
|
|||
f40981544c
|
|||
cbcf9ce5fc
|
|||
30ef5d8c91
|
|||
5d0811636f
|
|||
90f6fdad97
|
|||
c57baae2cc
|
|||
013a8faa2f
|
|||
798575d9ef
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,9 @@
|
|||||||
*.hex
|
*.hex
|
||||||
*.log
|
*.log
|
||||||
|
*.out
|
||||||
*env
|
*env
|
||||||
.dep
|
.dep
|
||||||
**/target/
|
**/target/
|
||||||
/hello_world/main
|
/hello_world/main
|
||||||
/aoc*/**/input
|
/aoc*/**/input
|
||||||
|
/arduino/build-*/
|
||||||
|
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (C) 2020 Dan Buch
|
Copyright (C) 2022 Dan Buch
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
[[source]]
|
|
||||||
url = "https://pypi.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
mypy = ""
|
|
||||||
pytest = ""
|
|
||||||
|
|
||||||
[requires]
|
|
||||||
python_version = "3.9"
|
|
156
aoc2020/py/Pipfile.lock
generated
156
aoc2020/py/Pipfile.lock
generated
@@ -1,156 +0,0 @@
|
|||||||
{
|
|
||||||
"_meta": {
|
|
||||||
"hash": {
|
|
||||||
"sha256": "ed02d1728cc686824535903ae2f5f3956ba104434c4c6e532237df55bcd69a12"
|
|
||||||
},
|
|
||||||
"pipfile-spec": 6,
|
|
||||||
"requires": {
|
|
||||||
"python_version": "3.9"
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"name": "pypi",
|
|
||||||
"url": "https://pypi.org/simple",
|
|
||||||
"verify_ssl": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default": {},
|
|
||||||
"develop": {
|
|
||||||
"attrs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
|
|
||||||
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==20.3.0"
|
|
||||||
},
|
|
||||||
"iniconfig": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
|
|
||||||
"sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
|
|
||||||
],
|
|
||||||
"version": "==1.1.1"
|
|
||||||
},
|
|
||||||
"mypy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324",
|
|
||||||
"sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc",
|
|
||||||
"sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802",
|
|
||||||
"sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122",
|
|
||||||
"sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975",
|
|
||||||
"sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7",
|
|
||||||
"sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666",
|
|
||||||
"sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669",
|
|
||||||
"sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178",
|
|
||||||
"sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01",
|
|
||||||
"sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea",
|
|
||||||
"sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de",
|
|
||||||
"sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1",
|
|
||||||
"sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"markers": "python_version >= '3.5'",
|
|
||||||
"version": "==0.790"
|
|
||||||
},
|
|
||||||
"mypy-extensions": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
|
||||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
|
||||||
],
|
|
||||||
"version": "==0.4.3"
|
|
||||||
},
|
|
||||||
"packaging": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
|
|
||||||
"sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==20.8"
|
|
||||||
},
|
|
||||||
"pluggy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
|
||||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==0.13.1"
|
|
||||||
},
|
|
||||||
"py": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
|
||||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==1.10.0"
|
|
||||||
},
|
|
||||||
"pyparsing": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
|
||||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==2.4.7"
|
|
||||||
},
|
|
||||||
"pytest": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8",
|
|
||||||
"sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"markers": "python_version >= '3.6'",
|
|
||||||
"version": "==6.2.1"
|
|
||||||
},
|
|
||||||
"toml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
|
||||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==0.10.2"
|
|
||||||
},
|
|
||||||
"typed-ast": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1",
|
|
||||||
"sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d",
|
|
||||||
"sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6",
|
|
||||||
"sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd",
|
|
||||||
"sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37",
|
|
||||||
"sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151",
|
|
||||||
"sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07",
|
|
||||||
"sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440",
|
|
||||||
"sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70",
|
|
||||||
"sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496",
|
|
||||||
"sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea",
|
|
||||||
"sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400",
|
|
||||||
"sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc",
|
|
||||||
"sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606",
|
|
||||||
"sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc",
|
|
||||||
"sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581",
|
|
||||||
"sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412",
|
|
||||||
"sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a",
|
|
||||||
"sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2",
|
|
||||||
"sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787",
|
|
||||||
"sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f",
|
|
||||||
"sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937",
|
|
||||||
"sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64",
|
|
||||||
"sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487",
|
|
||||||
"sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b",
|
|
||||||
"sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41",
|
|
||||||
"sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a",
|
|
||||||
"sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3",
|
|
||||||
"sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166",
|
|
||||||
"sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"
|
|
||||||
],
|
|
||||||
"version": "==1.4.2"
|
|
||||||
},
|
|
||||||
"typing-extensions": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
|
|
||||||
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
|
|
||||||
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
|
|
||||||
],
|
|
||||||
"version": "==3.7.4.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
inputs = [int(s) for s in sys.stdin.read().split()]
|
|
||||||
for i, j in _find_2020_pairs(inputs):
|
|
||||||
print(f"pair: {i} * {j} == {i * j}")
|
|
||||||
|
|
||||||
for i, j, k in _find_2020_triplets(inputs):
|
|
||||||
print(f"triplet: {i} * {j} * {k} == {i * j * k}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def _find_2020_pairs(
|
|
||||||
inputs: typing.List[int],
|
|
||||||
) -> typing.Generator[typing.Tuple[int, ...], None, None]:
|
|
||||||
found = []
|
|
||||||
|
|
||||||
for i, in0 in enumerate(inputs):
|
|
||||||
for j, in1 in enumerate(inputs):
|
|
||||||
if i == j:
|
|
||||||
continue
|
|
||||||
if in0 + in1 == 2020:
|
|
||||||
to_yield = tuple(sorted([in0, in1]))
|
|
||||||
if to_yield not in found:
|
|
||||||
yield to_yield
|
|
||||||
found.append(to_yield)
|
|
||||||
|
|
||||||
|
|
||||||
def _find_2020_triplets(
|
|
||||||
inputs: typing.List[int],
|
|
||||||
) -> typing.Generator[typing.Tuple[int, ...], None, None]:
|
|
||||||
found = []
|
|
||||||
|
|
||||||
for i, in0 in enumerate(inputs):
|
|
||||||
for j, in1 in enumerate(inputs):
|
|
||||||
for k, in2 in enumerate(inputs):
|
|
||||||
if i == j or i == k:
|
|
||||||
continue
|
|
||||||
if in0 + in1 + in2 == 2020:
|
|
||||||
to_yield = tuple(sorted([in0, in1, in2]))
|
|
||||||
if to_yield not in found:
|
|
||||||
yield to_yield
|
|
||||||
found.append(to_yield)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,53 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
n_valid = 0
|
|
||||||
total = 0
|
|
||||||
|
|
||||||
for pol_pas in [PolicyPassword.fromstring(s) for s in sys.stdin.readlines(False)]:
|
|
||||||
if pol_pas.is_valid():
|
|
||||||
n_valid += 1
|
|
||||||
total += 1
|
|
||||||
|
|
||||||
print(f"{n_valid}/{total} valid")
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class Policy:
|
|
||||||
def __init__(self, char: str, pos1: int, pos2: int):
|
|
||||||
self.char = char
|
|
||||||
self.pos1 = pos1
|
|
||||||
self.pos2 = pos2
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromstring(cls, input_string) -> "Policy":
|
|
||||||
parts = [s.strip() for s in input_string.split(" ")][:2]
|
|
||||||
pos = [int(s) for s in parts[0].split("-")][:2]
|
|
||||||
return cls(parts[1], pos[0], pos[1])
|
|
||||||
|
|
||||||
def is_valid(self, password: str) -> bool:
|
|
||||||
matches = 0
|
|
||||||
for pos in (self.pos1 - 1, self.pos2 - 1):
|
|
||||||
if password[pos] == self.char:
|
|
||||||
matches += 1
|
|
||||||
return matches == 1
|
|
||||||
|
|
||||||
|
|
||||||
class PolicyPassword:
|
|
||||||
def __init__(self, policy: "Policy", password: str):
|
|
||||||
self.policy = policy
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromstring(cls, input_string: str) -> "PolicyPassword":
|
|
||||||
parts = [s.strip() for s in input_string.split(":")][:2]
|
|
||||||
return cls(Policy.fromstring(parts[0]), parts[1])
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
return self.policy.is_valid(self.password)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,52 +0,0 @@
|
|||||||
import functools
|
|
||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
class Loc(typing.NamedTuple):
|
|
||||||
x: int
|
|
||||||
y: int
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
forest_frame = [list(line.strip()) for line in sys.stdin.readlines()]
|
|
||||||
frame_width = len(forest_frame[0])
|
|
||||||
frame_height = len(forest_frame)
|
|
||||||
|
|
||||||
all_trees_encountered = []
|
|
||||||
|
|
||||||
for slope in [
|
|
||||||
Loc(x=1, y=1),
|
|
||||||
Loc(x=3, y=1),
|
|
||||||
Loc(x=5, y=1),
|
|
||||||
Loc(x=7, y=1),
|
|
||||||
Loc(x=1, y=2),
|
|
||||||
]:
|
|
||||||
loc = Loc(x=0, y=0)
|
|
||||||
|
|
||||||
trees_encountered = 0
|
|
||||||
|
|
||||||
while loc.y <= (frame_height - 1):
|
|
||||||
at_loc = forest_frame[loc.y][loc.x]
|
|
||||||
if at_loc == "#":
|
|
||||||
trees_encountered += 1
|
|
||||||
|
|
||||||
next_x = (loc.x + slope.x) % frame_width
|
|
||||||
next_y = loc.y + slope.y
|
|
||||||
next_loc = Loc(x=next_x, y=next_y)
|
|
||||||
loc = next_loc
|
|
||||||
|
|
||||||
print(
|
|
||||||
f"(slope right={slope.x} down={slope.y}) trees encountered: {trees_encountered}"
|
|
||||||
)
|
|
||||||
all_trees_encountered.append(trees_encountered)
|
|
||||||
|
|
||||||
trees_encountered_product = functools.reduce(
|
|
||||||
lambda x, y: x * y, all_trees_encountered
|
|
||||||
)
|
|
||||||
print(f"trees encountered product: {trees_encountered_product}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,115 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
checked = []
|
|
||||||
for passport in _read_passports(sys.stdin):
|
|
||||||
if passport is None:
|
|
||||||
checked.append(False)
|
|
||||||
continue
|
|
||||||
checked.append(passport.is_valid())
|
|
||||||
|
|
||||||
print(f"total={len(checked)} valid={checked.count(True)}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
NoneString = typing.Optional[str]
|
|
||||||
|
|
||||||
VALID_EYE_COLORS = ("amb", "blu", "brn", "gry", "grn", "hzl", "oth")
|
|
||||||
|
|
||||||
|
|
||||||
class Passport:
|
|
||||||
byr: NoneString = None
|
|
||||||
cid: NoneString = None
|
|
||||||
ecl: NoneString = None
|
|
||||||
eyr: NoneString = None
|
|
||||||
hcl: NoneString = None
|
|
||||||
hgt: NoneString = None
|
|
||||||
iyr: NoneString = None
|
|
||||||
pid: NoneString = None
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
return (
|
|
||||||
self._is_year_in_range(self.byr, range(1920, 2003))
|
|
||||||
and self._is_year_in_range(self.iyr, range(2010, 2021))
|
|
||||||
and self._is_year_in_range(self.eyr, range(2020, 2031))
|
|
||||||
and self._has_valid_height()
|
|
||||||
and self._has_valid_hair_color()
|
|
||||||
and self.ecl in VALID_EYE_COLORS
|
|
||||||
and self._has_valid_passport_id()
|
|
||||||
)
|
|
||||||
|
|
||||||
def _has_valid_height(self) -> bool:
|
|
||||||
if self.hgt is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
height_value = 0
|
|
||||||
|
|
||||||
height_value_string = self.hgt.replace("cm", "").replace("in", "")
|
|
||||||
if not height_value_string.isdigit():
|
|
||||||
return False
|
|
||||||
|
|
||||||
height = int(height_value_string)
|
|
||||||
|
|
||||||
if self.hgt.endswith("cm") and height in range(150, 194):
|
|
||||||
return True
|
|
||||||
elif self.hgt.endswith("in") and height in range(59, 77):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _has_valid_hair_color(self) -> bool:
|
|
||||||
if self.hcl is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not self.hcl.startswith("#"):
|
|
||||||
return False
|
|
||||||
|
|
||||||
hair_value = self.hcl.replace("#", "")
|
|
||||||
if len(hair_value) != 6:
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
_ = int(hair_value, 16)
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _has_valid_passport_id(self) -> bool:
|
|
||||||
return (
|
|
||||||
self.pid is not None
|
|
||||||
and len(self.pid) == 9
|
|
||||||
and all([s.isdigit() for s in list(self.pid)])
|
|
||||||
)
|
|
||||||
|
|
||||||
def _is_year_in_range(self, value: NoneString, yr_range: range) -> bool:
|
|
||||||
if value is None:
|
|
||||||
return False
|
|
||||||
if len(value) != 4:
|
|
||||||
return False
|
|
||||||
if not value.isdigit():
|
|
||||||
return False
|
|
||||||
return int(value) in yr_range
|
|
||||||
|
|
||||||
|
|
||||||
def _read_passports(
|
|
||||||
instream: typing.TextIO,
|
|
||||||
) -> typing.Generator[typing.Optional[Passport], None, None]:
|
|
||||||
cur = Passport()
|
|
||||||
|
|
||||||
for i, line in enumerate(instream):
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
yield cur
|
|
||||||
cur = Passport()
|
|
||||||
|
|
||||||
for pair in line.split():
|
|
||||||
attr, value = pair.split(":", 1)
|
|
||||||
setattr(cur, attr, value)
|
|
||||||
|
|
||||||
yield cur
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,77 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
highest_seat = 0
|
|
||||||
|
|
||||||
taken_seats = set()
|
|
||||||
|
|
||||||
for line in sys.stdin:
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
continue
|
|
||||||
|
|
||||||
seat = _locate_seat(line)
|
|
||||||
|
|
||||||
if seat.number > highest_seat:
|
|
||||||
highest_seat = seat.number
|
|
||||||
|
|
||||||
taken_seats.add(seat.number)
|
|
||||||
|
|
||||||
print(f"bp={line} row={seat.row} column={seat.col} seat={seat.number}")
|
|
||||||
|
|
||||||
seat_number: typing.Optional[int] = None
|
|
||||||
n_found = 0
|
|
||||||
|
|
||||||
for candidate_seat in range(0, (127 * 8) + 1):
|
|
||||||
if candidate_seat in taken_seats:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if (candidate_seat - 1) in taken_seats and (candidate_seat + 1) in taken_seats:
|
|
||||||
seat_number = candidate_seat
|
|
||||||
n_found += 1
|
|
||||||
|
|
||||||
print(f"highest_seat={highest_seat} seat_number={seat_number} n_found={n_found}")
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class Seat:
|
|
||||||
row: int
|
|
||||||
column: int
|
|
||||||
|
|
||||||
def __init__(self, row: int = 0, col: int = 0):
|
|
||||||
self.row = row
|
|
||||||
self.col = col
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self) -> int:
|
|
||||||
return (self.row * 8) + self.col
|
|
||||||
|
|
||||||
|
|
||||||
def _locate_seat(bp: str) -> Seat:
|
|
||||||
rows = list(range(0, 128))
|
|
||||||
cols = list(range(0, 8))
|
|
||||||
|
|
||||||
row_part = list(bp[:7])
|
|
||||||
col_part = list(bp[7:])
|
|
||||||
|
|
||||||
return Seat(
|
|
||||||
row=_bisect(rows, [{"F": 0, "B": 1}[s] for s in row_part]),
|
|
||||||
col=_bisect(cols, [{"L": 0, "R": 1}[s] for s in col_part]),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _bisect(initial_selection: typing.List[int], bisections: typing.List[int]) -> int:
|
|
||||||
selection = initial_selection[:]
|
|
||||||
|
|
||||||
for bisection in bisections:
|
|
||||||
halfway = int(len(selection) / 2)
|
|
||||||
selection = [selection[:halfway], selection[halfway:]][bisection]
|
|
||||||
|
|
||||||
return selection[0]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,3 +0,0 @@
|
|||||||
BFFFBBFRRR
|
|
||||||
FFFBBBFRRR
|
|
||||||
BBFFBBFRLL
|
|
@@ -1,3 +0,0 @@
|
|||||||
bp=BFFFBBFRRR row=70 column=7 seat=567
|
|
||||||
bp=FFFBBBFRRR row=14 column=7 seat=119
|
|
||||||
bp=BBFFBBFRLL row=102 column=4 seat=820
|
|
@@ -1,21 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from solution import main
|
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).absolute().parent
|
|
||||||
|
|
||||||
|
|
||||||
def test_solution(capsys):
|
|
||||||
with (HERE / "test-input").open() as infile:
|
|
||||||
sys.stdin = infile
|
|
||||||
main()
|
|
||||||
|
|
||||||
expected_output = (HERE / "test-output").read_text().splitlines()
|
|
||||||
assert expected_output == [
|
|
||||||
l for l in capsys.readouterr().out.splitlines() if l.startswith("counts_sum=")
|
|
||||||
]
|
|
@@ -1,42 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
counts_sum = sum([c for c in _iter_group_counts(sys.stdin)])
|
|
||||||
print(f"counts_sum={counts_sum}")
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_group_counts(instream: typing.TextIO) -> typing.Generator[int, None, None]:
|
|
||||||
for i, group in enumerate(_iter_groups(instream)):
|
|
||||||
answers = set(list(group[0]))
|
|
||||||
print(f"i={i} initial={answers}")
|
|
||||||
|
|
||||||
for answers_text in group[1:]:
|
|
||||||
to_add = set(list(answers_text))
|
|
||||||
answers = answers.intersection(set(list(answers_text)))
|
|
||||||
print(f"i={i} added={to_add} result={answers}")
|
|
||||||
|
|
||||||
print(f"i={i} final={answers} n={len(answers)}")
|
|
||||||
yield len(answers)
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_groups(instream):
|
|
||||||
cur_group = []
|
|
||||||
|
|
||||||
for line in instream:
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
yield cur_group
|
|
||||||
cur_group = []
|
|
||||||
continue
|
|
||||||
|
|
||||||
cur_group.append(line)
|
|
||||||
|
|
||||||
yield cur_group
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,15 +0,0 @@
|
|||||||
abc
|
|
||||||
|
|
||||||
a
|
|
||||||
b
|
|
||||||
c
|
|
||||||
|
|
||||||
ab
|
|
||||||
ac
|
|
||||||
|
|
||||||
a
|
|
||||||
a
|
|
||||||
a
|
|
||||||
a
|
|
||||||
|
|
||||||
b
|
|
@@ -1 +0,0 @@
|
|||||||
counts_sum=6
|
|
@@ -1,21 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from solution import main
|
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).absolute().parent
|
|
||||||
|
|
||||||
|
|
||||||
def test_solution(capsys):
|
|
||||||
with (HERE / "test-input").open() as infile:
|
|
||||||
sys.stdin = infile
|
|
||||||
main()
|
|
||||||
|
|
||||||
expected_output = (HERE / "test-output").read_text().splitlines()
|
|
||||||
assert expected_output == [
|
|
||||||
l for l in capsys.readouterr().out.splitlines() if l.startswith("counts_sum")
|
|
||||||
]
|
|
2
arduino/.envrc
Normal file
2
arduino/.envrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export ARDMK_DIR=/usr/share/arduino
|
||||||
|
export ARDMK_VENDOR=archlinux-arduino
|
3
arduino/Makefile
Normal file
3
arduino/Makefile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
BOARD_TAG = uno
|
||||||
|
|
||||||
|
include $(ARDMK_DIR)/Arduino.mk
|
31
arduino/sos.ino
Normal file
31
arduino/sos.ino
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#define DIT_DURATION_MS 88
|
||||||
|
#define LETTER_PAUSE_MS 1000
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(13, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dit() {
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
delay(DIT_DURATION_MS);
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
delay(DIT_DURATION_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dah() {
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
delay(DIT_DURATION_MS * 3);
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
delay(DIT_DURATION_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
dit(); dit(); dit();
|
||||||
|
delay(LETTER_PAUSE_MS);
|
||||||
|
|
||||||
|
dah();
|
||||||
|
delay(LETTER_PAUSE_MS);
|
||||||
|
|
||||||
|
dit(); dit(); dit();
|
||||||
|
delay(LETTER_PAUSE_MS);
|
||||||
|
}
|
3
cat-town/.gitignore
vendored
Normal file
3
cat-town/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/node_modules/
|
||||||
|
/src/*.js
|
||||||
|
/dist/
|
4838
cat-town/package-lock.json
generated
Normal file
4838
cat-town/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
cat-town/package.json
Normal file
30
cat-town/package.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "cat-town",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"webpack": "webpack",
|
||||||
|
"dev": "webpack serve --config webpack.development.js",
|
||||||
|
"start": "npm run dev",
|
||||||
|
"build:dev": "webpack --config webpack.development.js",
|
||||||
|
"build:prod": "webpack --config webpack.production.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@excaliburjs/testing": "^0.25.1",
|
||||||
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
|
"compression-webpack-plugin": "^7.1.2",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"source-map-loader": "^2.0.2",
|
||||||
|
"terser-webpack-plugin": "^5.3.6",
|
||||||
|
"ts-loader": "^9.4.2",
|
||||||
|
"typescript": "^4.9.4",
|
||||||
|
"webpack": "^5.75.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-dev-server": "^4.11.1",
|
||||||
|
"webpack-merge": "^5.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"excalibur": "^0.27.0"
|
||||||
|
}
|
||||||
|
}
|
17
cat-town/src/actors/player.ts
Normal file
17
cat-town/src/actors/player.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Actor, Color, vec } from 'excalibur'
|
||||||
|
import { Resources } from '../resources'
|
||||||
|
|
||||||
|
export class Player extends Actor {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
pos: vec(150, 150),
|
||||||
|
width: 25,
|
||||||
|
height: 25,
|
||||||
|
color: new Color(255, 100, 100)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onInitialize() {
|
||||||
|
this.graphics.use(Resources.Cat.toSprite())
|
||||||
|
}
|
||||||
|
}
|
16
cat-town/src/cat.ts
Normal file
16
cat-town/src/cat.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// type Cat = {
|
||||||
|
// name string;
|
||||||
|
// years int;
|
||||||
|
// centimeters int;
|
||||||
|
// kilograms int;
|
||||||
|
// coloring Coloring;
|
||||||
|
// pattern Pattern;
|
||||||
|
// mood Mood;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// type Coloring = 'orange' | 'black' | 'brown' | 'blue';
|
||||||
|
//
|
||||||
|
// type Pattern = 'plain' | 'striped' | 'spotted';
|
||||||
|
//
|
||||||
|
// type Mood = 'happy' | 'sad' | 'purring' |
|
||||||
|
// 'screaming' | 'mad' | 'scratchy' | 'curious' | 'concerned';
|
BIN
cat-town/src/images/cat.png
Normal file
BIN
cat-town/src/images/cat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
cat-town/src/images/cat.png-autosave.kra
Normal file
BIN
cat-town/src/images/cat.png-autosave.kra
Normal file
Binary file not shown.
30
cat-town/src/index.ts
Normal file
30
cat-town/src/index.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Engine, Loader, DisplayMode } from 'excalibur'
|
||||||
|
import { Beginning } from './scenes/beginning'
|
||||||
|
import { Player } from './actors/player'
|
||||||
|
import { Resources } from './resources'
|
||||||
|
|
||||||
|
class Game extends Engine {
|
||||||
|
private player: Player
|
||||||
|
private beginning: Beginning
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({ displayMode: DisplayMode.FitScreen })
|
||||||
|
}
|
||||||
|
|
||||||
|
public start() {
|
||||||
|
this.beginning = new Beginning()
|
||||||
|
this.player = new Player()
|
||||||
|
this.beginning.add(this.player)
|
||||||
|
|
||||||
|
game.add('beginning', this.beginning)
|
||||||
|
|
||||||
|
const loader = new Loader(Object.values(Resources))
|
||||||
|
|
||||||
|
return super.start(loader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = new Game()
|
||||||
|
game.start().then(() => {
|
||||||
|
game.goToScene('beginning')
|
||||||
|
})
|
8
cat-town/src/resources.ts
Normal file
8
cat-town/src/resources.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ImageSource } from 'excalibur'
|
||||||
|
import catImage from './images/cat.png'
|
||||||
|
|
||||||
|
const Resources = {
|
||||||
|
Cat: new ImageSource(catImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Resources }
|
7
cat-town/src/scenes/beginning.ts
Normal file
7
cat-town/src/scenes/beginning.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Engine, Scene } from 'excalibur'
|
||||||
|
|
||||||
|
export class Beginning extends Scene {
|
||||||
|
public onInitialize(engine: Engine) {}
|
||||||
|
public onActivate() {}
|
||||||
|
public onDeactivate() {}
|
||||||
|
}
|
22
cat-town/src/town.ts
Normal file
22
cat-town/src/town.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// type Town = {
|
||||||
|
// grid Grid;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// type Grid = {
|
||||||
|
// squares Map<Coords, Square>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// type Coords = {
|
||||||
|
// x int;
|
||||||
|
// y int;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// type Square = {
|
||||||
|
// description string;
|
||||||
|
// things Map<string, Thing>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// type Thing = {
|
||||||
|
// name string;
|
||||||
|
// description string;
|
||||||
|
// };
|
14
cat-town/tsconfig.json
Normal file
14
cat-town/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "es6",
|
||||||
|
"types": ["excalibur"],
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
47
cat-town/webpack.common.js
Normal file
47
cat-town/webpack.common.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
const HtmlWebPackPlugin = require("html-webpack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: "./src/index.ts",
|
||||||
|
target: "web",
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
sourceMapFilename: "[file].map",
|
||||||
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
},
|
||||||
|
devtool: "source-map",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(png|svg|jpg|jpeg|gif)$/i,
|
||||||
|
type: "asset/resource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
use: ["source-map-loader"],
|
||||||
|
exclude: [path.resolve(__dirname, "node_modules/excalibur")],
|
||||||
|
enforce: "pre",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: "ts-loader",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".tsx", ".ts", ".js"],
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
chunks: "all",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
new HtmlWebPackPlugin({
|
||||||
|
title: "Cat Town",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
12
cat-town/webpack.development.js
Normal file
12
cat-town/webpack.development.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const common = require("./webpack.common");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: "development",
|
||||||
|
devtool: "inline-source-map",
|
||||||
|
devServer: {
|
||||||
|
static: {
|
||||||
|
directory: "./dist",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
13
cat-town/webpack.production.js
Normal file
13
cat-town/webpack.production.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const CompressionWebpackPlugin = require("compression-webpack-plugin");
|
||||||
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
|
const common = require("./webpack.common");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: "production",
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [new TerserPlugin()],
|
||||||
|
},
|
||||||
|
plugins: [new CompressionWebpackPlugin()],
|
||||||
|
});
|
116
explode_mbox.py
116
explode_mbox.py
@@ -1,116 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import gzip
|
|
||||||
import hashlib
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import pathlib
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main(sysargs=sys.argv[:]) -> int:
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("mbox", type=argparse.FileType("rb"))
|
|
||||||
parser.add_argument("output_directory", type=pathlib.Path)
|
|
||||||
parser.add_argument(
|
|
||||||
"-s",
|
|
||||||
"--zzz",
|
|
||||||
default=0.005,
|
|
||||||
type=float,
|
|
||||||
help="sleep seconds in between messages",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-D",
|
|
||||||
"--debug",
|
|
||||||
action="store_true",
|
|
||||||
help="increase logging verbosity to debug level",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args(sysargs[1:])
|
|
||||||
|
|
||||||
log_level = logging.INFO
|
|
||||||
if os.environ.get("DEBUG") == "enabled":
|
|
||||||
log_level = logging.DEBUG
|
|
||||||
|
|
||||||
logging.basicConfig(level=log_level)
|
|
||||||
|
|
||||||
MBoxExploder().explode(
|
|
||||||
mbox=args.mbox, output_directory=args.output_directory, pause_seconds=args.zzz
|
|
||||||
)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class MBoxMessage:
|
|
||||||
def __init__(self):
|
|
||||||
self.lines = []
|
|
||||||
|
|
||||||
def as_gz_bytes(self) -> bytes:
|
|
||||||
return gzip.compress(self.as_bytes())
|
|
||||||
|
|
||||||
def as_bytes(self) -> bytes:
|
|
||||||
return b"".join([l for l in self.lines])
|
|
||||||
|
|
||||||
def signature(self) -> str:
|
|
||||||
return hashlib.sha512(self.as_bytes()).hexdigest()
|
|
||||||
|
|
||||||
def relpath(self) -> str:
|
|
||||||
sig = self.signature()
|
|
||||||
return os.path.sep.join([sig[0:2], sig[2:4], sig])
|
|
||||||
|
|
||||||
def gz_relpath(self) -> str:
|
|
||||||
return self.relpath() + ".gz"
|
|
||||||
|
|
||||||
|
|
||||||
class MBoxExploder:
|
|
||||||
def __init__(self):
|
|
||||||
self._log = logging.getLogger().getChild("mbox-exploder")
|
|
||||||
|
|
||||||
def explode(
|
|
||||||
self,
|
|
||||||
mbox: typing.BinaryIO,
|
|
||||||
output_directory: pathlib.Path,
|
|
||||||
pause_seconds: float,
|
|
||||||
):
|
|
||||||
for i, msg in enumerate(self._iter_mbox(mbox)):
|
|
||||||
if len(msg.lines) < 2:
|
|
||||||
self._log.warn("skipping invalid message (%r)", i)
|
|
||||||
continue
|
|
||||||
dest = output_directory / msg.gz_relpath()
|
|
||||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
self._log.info("writing message to %s", str(dest))
|
|
||||||
dest.write_bytes(msg.as_gz_bytes())
|
|
||||||
time.sleep(pause_seconds)
|
|
||||||
|
|
||||||
def _iter_mbox(
|
|
||||||
self, mbox: typing.BinaryIO
|
|
||||||
) -> typing.Generator[MBoxMessage, None, None]:
|
|
||||||
msg = MBoxMessage()
|
|
||||||
cur_line = b""
|
|
||||||
|
|
||||||
while True:
|
|
||||||
byte = mbox.read(1)
|
|
||||||
if len(byte) == 0:
|
|
||||||
self._log.debug("reached EOF")
|
|
||||||
msg.lines.append(cur_line)
|
|
||||||
yield msg
|
|
||||||
return
|
|
||||||
|
|
||||||
cur_line += byte
|
|
||||||
|
|
||||||
if byte != b"\n":
|
|
||||||
continue
|
|
||||||
|
|
||||||
if cur_line.startswith(b"From ") and len(msg.lines) > 1:
|
|
||||||
self._log.debug("reached new msg")
|
|
||||||
yield msg
|
|
||||||
msg = MBoxMessage()
|
|
||||||
|
|
||||||
self._log.debug("appending line %r", cur_line)
|
|
||||||
msg.lines.append(cur_line)
|
|
||||||
cur_line = b""
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
178
guessing_game/Cargo.lock
generated
178
guessing_game/Cargo.lock
generated
@@ -1,178 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cloudabi"
|
|
||||||
version = "0.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fuchsia-cprng"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "guessing_game"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"rand",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.80"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"libc",
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
"rand_hc",
|
|
||||||
"rand_isaac",
|
|
||||||
"rand_jitter",
|
|
||||||
"rand_os",
|
|
||||||
"rand_pcg",
|
|
||||||
"rand_xorshift",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_isaac"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_jitter"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_os"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
|
||||||
dependencies = [
|
|
||||||
"cloudabi",
|
|
||||||
"fuchsia-cprng",
|
|
||||||
"libc",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
"rdrand",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_pcg"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_xorshift"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rdrand"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
207
hyrule/README.md
Normal file
207
hyrule/README.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# Hyrule
|
||||||
|
|
||||||
|
A card game for 2-7 players.
|
||||||
|
|
||||||
|
## requirements
|
||||||
|
|
||||||
|
Use a standard card playing deck, keeping Jokers. Shuffle however
|
||||||
|
you like.
|
||||||
|
|
||||||
|
## dealing
|
||||||
|
|
||||||
|
Each player is dealt 7 cards, kept secret from other players.
|
||||||
|
|
||||||
|
## turns
|
||||||
|
|
||||||
|
There are two variations for taking turns. "Chance rules" is
|
||||||
|
strictly a matter of chance, and is recommended for impatient
|
||||||
|
players because it tends to be faster-paced and has more of a
|
||||||
|
*surprise!* element. "Strategy rules" introduces an element of
|
||||||
|
strategy and is better suited for players with more _patience_.
|
||||||
|
|
||||||
|
### chance rules
|
||||||
|
|
||||||
|
Each player selects one card and places it face down in the middle
|
||||||
|
playing area between themselves and other players.
|
||||||
|
|
||||||
|
On the count of 3, all said together in rhythm, each player flips
|
||||||
|
over their own card. Precedence applies and the winning player
|
||||||
|
takes all cards.
|
||||||
|
|
||||||
|
### strategy rules
|
||||||
|
|
||||||
|
On the first turn or when an equal number of cards have been
|
||||||
|
captured by each player, the tallest player goes first. On each
|
||||||
|
subsequent turn, the player with the most captured cards plays
|
||||||
|
first. The play order then proceeds counter-clockwise.
|
||||||
|
|
||||||
|
When taking one's turn, the player may choose to either *play* or
|
||||||
|
*swap*.
|
||||||
|
|
||||||
|
#### strategy *play*
|
||||||
|
|
||||||
|
When choosing to *play*, the player selects a card and places it
|
||||||
|
face-up in the center play area. The remaining players take turns
|
||||||
|
counter-clockwise, each placing a card face-up in the center play
|
||||||
|
area. Precedence rules apply and the winning player takes all
|
||||||
|
cards.
|
||||||
|
|
||||||
|
#### strategy *swap*
|
||||||
|
|
||||||
|
When choosing to *swap*, the player discards one card face-up in
|
||||||
|
the discard pile (next to the stock pile) and then draws a card
|
||||||
|
from the stock, which should be laying face-down. The goal here may
|
||||||
|
be to take a chance at getting a better card than one is
|
||||||
|
discarding, or to force the next player in the rotation to play, or
|
||||||
|
both.
|
||||||
|
|
||||||
|
> **NOTE**: a player may only choose to *swap* if the player on the
|
||||||
|
> previous turn _did not swap_.
|
||||||
|
|
||||||
|
## precedence
|
||||||
|
|
||||||
|
The following rules apply across suits with the exception of
|
||||||
|
*jokers and fives* (explained below). Cards within a single suit
|
||||||
|
are compared with higher-value cards winning. Cards are counted
|
||||||
|
from ace (1) through king (13).
|
||||||
|
|
||||||
|
Ways to think about these rules could include:
|
||||||
|
|
||||||
|
- "rupee buys bomb" / money buys weapon
|
||||||
|
- "rupee buys sword" / money buys weapon
|
||||||
|
- "bomb blows up sword" / range weapon beats melee weapon
|
||||||
|
- "bomb blows up heart" / range weapon beats unarmed
|
||||||
|
- "sword cuts through heart" / melee weapon beats unarmed
|
||||||
|
- "heart is stronger than rupee" / love conquers money
|
||||||
|
|
||||||
|
**NOTE:** In the case of a turn that involves 3 or more cards, the
|
||||||
|
presence of both diamonds *and* hearts will result in the
|
||||||
|
highest-value heart winning the turn.
|
||||||
|
|
||||||
|
### diamonds ("rupees")
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- clubs ("bombs")
|
||||||
|
- spades ("swords")
|
||||||
|
|
||||||
|
### clubs ("bombs")
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- spades ("swords")
|
||||||
|
- hearts
|
||||||
|
|
||||||
|
### spades ("swords")
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- hearts
|
||||||
|
|
||||||
|
### hearts
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- diamonds ("rupees")
|
||||||
|
|
||||||
|
### jokers ("tingles")
|
||||||
|
|
||||||
|
Beats everything except *fives*
|
||||||
|
|
||||||
|
### fives
|
||||||
|
|
||||||
|
The 5 of a given suit will be granted special status *only* when
|
||||||
|
played against a joker ("tingle") and will win with the following
|
||||||
|
sub-precedence, which is roughly the inverse of the main
|
||||||
|
precedence:
|
||||||
|
|
||||||
|
- 5 of hearts
|
||||||
|
- 5 of spades / "master sword"
|
||||||
|
- 5 of clubs / "bomb cluster"
|
||||||
|
- 5 of diamonds / "blue rupee"
|
||||||
|
|
||||||
|
## scoring
|
||||||
|
|
||||||
|
At the conclusion of a round of 7 cards, the player with the most
|
||||||
|
cards is the winner. The face values of the cards are not
|
||||||
|
considered at scoring time. A draw may be handled in a "run-off
|
||||||
|
game" or ignored as you like.
|
||||||
|
|
||||||
|
## examples
|
||||||
|
|
||||||
|
In the following examples, the **winning card** is highlighted at
|
||||||
|
the top of each list, followed by an explanation for the outcome.
|
||||||
|
|
||||||
|
### 2-player
|
||||||
|
|
||||||
|
- **2 of clubs**
|
||||||
|
- 2 of spades
|
||||||
|
|
||||||
|
Clubs are higher-value than spades, or "bomb beats sword".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **2 of clubs**
|
||||||
|
- ace of clubs
|
||||||
|
|
||||||
|
Cards within the same suit are compared at face value with aces
|
||||||
|
being *1*.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **ace of hearts**
|
||||||
|
- king of diamonds
|
||||||
|
|
||||||
|
Hearts take precedence over diamonds, or "love is stronger than
|
||||||
|
money".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **joker**
|
||||||
|
- king of hearts
|
||||||
|
|
||||||
|
The opposing card is not a *five*, or "tingle takes _(thing)_".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **5 of spades**
|
||||||
|
- joker
|
||||||
|
|
||||||
|
The 5 of any suit will beat a joker given its special item status
|
||||||
|
in that scenario, or "tingle is distracted by the beauty of
|
||||||
|
_(thing)_".
|
||||||
|
|
||||||
|
### 3-player
|
||||||
|
|
||||||
|
- **2 of diamonds**
|
||||||
|
- 2 of clubs
|
||||||
|
- 2 of spades
|
||||||
|
|
||||||
|
Diamonds have higher precedence than clubs and spades, and there
|
||||||
|
are no hearts present.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **6 of clubs**
|
||||||
|
- 6 of spades
|
||||||
|
- 6 of hearts
|
||||||
|
|
||||||
|
Clubs have higher precedence than spades and hearts, and there are
|
||||||
|
no diamonds present.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **8 of hearts**
|
||||||
|
- 8 of diamonds
|
||||||
|
- 8 of spades
|
||||||
|
|
||||||
|
When both diamonds and hearts are present, hearts is highest
|
||||||
|
precedence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **king of diamonds**
|
||||||
|
- jack of diamonds
|
||||||
|
- queen of diamonds
|
||||||
|
|
||||||
|
Within the same suit, cards are compared by face value.
|
BIN
hyrule/README.pdf
Normal file
BIN
hyrule/README.pdf
Normal file
Binary file not shown.
86
hyrule/cards.py
Normal file
86
hyrule/cards.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import dataclasses
|
||||||
|
import enum
|
||||||
|
import random
|
||||||
|
|
||||||
|
Suit = enum.Enum(
|
||||||
|
"Suit",
|
||||||
|
"""
|
||||||
|
CLUBS
|
||||||
|
DIAMONDS
|
||||||
|
HEARTS
|
||||||
|
SPADES
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
FaceValue = enum.Enum(
|
||||||
|
"FaceValue",
|
||||||
|
"""
|
||||||
|
ACE
|
||||||
|
TWO
|
||||||
|
THREE
|
||||||
|
FOUR
|
||||||
|
FIVE
|
||||||
|
SIX
|
||||||
|
SEVEN
|
||||||
|
EIGHT
|
||||||
|
NINE
|
||||||
|
TEN
|
||||||
|
JACK
|
||||||
|
QUEEN
|
||||||
|
KING
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Card:
|
||||||
|
suit: Suit
|
||||||
|
face_value: FaceValue
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.face_value} of {self.suit}"
|
||||||
|
|
||||||
|
|
||||||
|
class Joker:
|
||||||
|
def __str__(self):
|
||||||
|
return "JOKER"
|
||||||
|
|
||||||
|
|
||||||
|
class Deck:
|
||||||
|
def __init__(self, n_jokers=2):
|
||||||
|
self._cards = list(Deck.generate(n_jokers=n_jokers))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Deck len={len(self)}>"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
as_string = []
|
||||||
|
for card in self._cards:
|
||||||
|
as_string.append(str(card))
|
||||||
|
return "\n".join(as_string)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._cards)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
if len(self._cards) > 0:
|
||||||
|
return self._cards.pop()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def shuffle(self):
|
||||||
|
random.shuffle(self._cards)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def cut(self):
|
||||||
|
cut_point = random.randint(0, len(self))
|
||||||
|
self._cards = self._cards[cut_point:] + self._cards[:cut_point]
|
||||||
|
return self
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate(n_jokers=2):
|
||||||
|
for _ in range(n_jokers):
|
||||||
|
yield Joker()
|
||||||
|
|
||||||
|
for suit in Suit:
|
||||||
|
for face_value in FaceValue:
|
||||||
|
yield Card(suit=suit, face_value=face_value)
|
66
hyrule/hyrule.py
Normal file
66
hyrule/hyrule.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import dataclasses
|
||||||
|
import pprint
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import cards
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--player-count", "-c", default=2, type=int, help="The number of players"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--turn-count",
|
||||||
|
"-n",
|
||||||
|
default=5,
|
||||||
|
type=int,
|
||||||
|
help="The number of turns to play",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
Hyrule(player_count=args.player_count, turn_count=args.turn_count).play()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Player:
|
||||||
|
index: int
|
||||||
|
hand: typing.Set[cards.Card]
|
||||||
|
captured: typing.Set[cards.Card]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def score(self):
|
||||||
|
return len(self.captured)
|
||||||
|
|
||||||
|
|
||||||
|
class Hyrule:
|
||||||
|
def __init__(self, player_count=2, turn_count=5):
|
||||||
|
self._players = [
|
||||||
|
Player(index=i, hand=set(), captured=set()) for i in range(player_count)
|
||||||
|
]
|
||||||
|
self._turn_count = turn_count
|
||||||
|
|
||||||
|
def play(self):
|
||||||
|
for turn in self._each_turn():
|
||||||
|
self._show_turn(turn)
|
||||||
|
|
||||||
|
def _each_turn(self):
|
||||||
|
for turn_number in range(self._turn_count):
|
||||||
|
yield self._simulate_turn(turn_number)
|
||||||
|
|
||||||
|
def _simulate_turn(self, turn_number):
|
||||||
|
...
|
||||||
|
|
||||||
|
def _show_turn(self, turn):
|
||||||
|
print(turn)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
2
hyrule/justfile
Normal file
2
hyrule/justfile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
build:
|
||||||
|
pandoc -r markdown -w pdf -o README.pdf README.md
|
1876
piston-tutorials/getting-started/Cargo.lock
generated
Normal file
1876
piston-tutorials/getting-started/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
piston-tutorials/getting-started/Cargo.toml
Normal file
17
piston-tutorials/getting-started/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "spinning-square"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = [
|
||||||
|
"Dan Buch <dan@meatballhat.com>",
|
||||||
|
"TyOverby <ty@pre-alpha.com>",
|
||||||
|
"Nikita Pekin <contact@nikitapek.in>"
|
||||||
|
]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "spinning-square"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
piston = "0.53.0"
|
||||||
|
piston2d-graphics = "0.43.0"
|
||||||
|
pistoncore-glutin_window = "0.70.1"
|
||||||
|
piston2d-opengl_graphics = "0.82.0"
|
70
piston-tutorials/getting-started/src/main.rs
Normal file
70
piston-tutorials/getting-started/src/main.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
extern crate glutin_window;
|
||||||
|
extern crate graphics;
|
||||||
|
extern crate opengl_graphics;
|
||||||
|
extern crate piston;
|
||||||
|
|
||||||
|
use glutin_window::GlutinWindow as Window;
|
||||||
|
use opengl_graphics::{GlGraphics, OpenGL};
|
||||||
|
use piston::event_loop::{EventSettings, Events};
|
||||||
|
use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent};
|
||||||
|
use piston::window::WindowSettings;
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
gl: GlGraphics,
|
||||||
|
rotation: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn render(&mut self, args: &RenderArgs) {
|
||||||
|
use graphics::*;
|
||||||
|
|
||||||
|
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
|
||||||
|
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||||
|
|
||||||
|
let square = rectangle::square(0.0, 0.0, 50.0);
|
||||||
|
let rotation = self.rotation;
|
||||||
|
let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0);
|
||||||
|
|
||||||
|
self.gl.draw(args.viewport(), |c, gl| {
|
||||||
|
clear(GREEN, gl);
|
||||||
|
|
||||||
|
let transform = c
|
||||||
|
.transform
|
||||||
|
.trans(x, y)
|
||||||
|
.rot_rad(rotation)
|
||||||
|
.trans(-25.0, -25.0);
|
||||||
|
|
||||||
|
rectangle(RED, square, transform, gl);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, args: &UpdateArgs) {
|
||||||
|
self.rotation += 2.0 * args.dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opengl = OpenGL::V3_2;
|
||||||
|
|
||||||
|
let mut window: Window = WindowSettings::new("spinning-square", [200, 200])
|
||||||
|
.graphics_api(opengl)
|
||||||
|
.exit_on_esc(true)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut app = App {
|
||||||
|
gl: GlGraphics::new(opengl),
|
||||||
|
rotation: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut events = Events::new(EventSettings::new());
|
||||||
|
while let Some(e) = events.next(&mut window) {
|
||||||
|
if let Some(args) = e.render_args() {
|
||||||
|
app.render(&args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(args) = e.update_args() {
|
||||||
|
app.update(&args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1876
piston-tutorials/sudoku/Cargo.lock
generated
Normal file
1876
piston-tutorials/sudoku/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
piston-tutorials/sudoku/Cargo.toml
Normal file
12
piston-tutorials/sudoku/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "sudoku"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
piston = "0.53.2"
|
||||||
|
piston2d-graphics = "0.43.0"
|
||||||
|
piston2d-opengl_graphics = "0.82.0"
|
||||||
|
pistoncore-glutin_window = "0.70.1"
|
BIN
piston-tutorials/sudoku/assets/FiraSans-Regular.ttf
Normal file
BIN
piston-tutorials/sudoku/assets/FiraSans-Regular.ttf
Normal file
Binary file not shown.
99
piston-tutorials/sudoku/assets/LICENSE
Normal file
99
piston-tutorials/sudoku/assets/LICENSE
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
|
||||||
|
with Reserved Font Name Fira Sans.
|
||||||
|
|
||||||
|
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
|
||||||
|
with Reserved Font Name Fira Mono.
|
||||||
|
|
||||||
|
Copyright (c) 2014, Telefonica S.A.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
155
piston-tutorials/sudoku/src/gameboard.rs
Normal file
155
piston-tutorials/sudoku/src/gameboard.rs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
//! Game board logic.
|
||||||
|
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
|
const SIZE: usize = 9;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct Cell {
|
||||||
|
pub value: u8,
|
||||||
|
pub loaded: bool,
|
||||||
|
pub invalid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Gameboard {
|
||||||
|
pub cells: [[Cell; SIZE]; SIZE],
|
||||||
|
pub completed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gameboard {
|
||||||
|
pub fn new() -> Gameboard {
|
||||||
|
Gameboard {
|
||||||
|
cells: [[Cell::default(); SIZE]; SIZE],
|
||||||
|
completed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_cells(cells: [[u8; SIZE]; SIZE]) -> Gameboard {
|
||||||
|
let mut ret = Gameboard::new();
|
||||||
|
for (i, row) in cells.iter().enumerate() {
|
||||||
|
for (j, &col) in row.iter().enumerate() {
|
||||||
|
ret.cells[i][j] = Cell {
|
||||||
|
value: col,
|
||||||
|
loaded: col != 0,
|
||||||
|
invalid: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn char(&self, ind: [usize; 2]) -> Option<char> {
|
||||||
|
Some(match self.cells[ind[1]][ind[0]].value {
|
||||||
|
1 => '1',
|
||||||
|
2 => '2',
|
||||||
|
3 => '3',
|
||||||
|
4 => '4',
|
||||||
|
5 => '5',
|
||||||
|
6 => '6',
|
||||||
|
7 => '7',
|
||||||
|
8 => '8',
|
||||||
|
9 => '9',
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, ind: [usize; 2], val: u8) {
|
||||||
|
if !self.cells[ind[1]][ind[0]].loaded {
|
||||||
|
self.validate(ind, val);
|
||||||
|
self.cells[ind[1]][ind[0]].value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.completed = self
|
||||||
|
.cells
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.all(|cell| !cell.invalid && cell.value != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_sdm(filename: &str) -> Self {
|
||||||
|
let data = read_to_string(filename).expect("failed to read SDM file");
|
||||||
|
let mut cells = [[Cell::default(); SIZE]; SIZE];
|
||||||
|
let mut row = 0;
|
||||||
|
let mut col = 0;
|
||||||
|
for c in data.chars() {
|
||||||
|
if col == SIZE {
|
||||||
|
col = 0;
|
||||||
|
row += 1;
|
||||||
|
}
|
||||||
|
if let Some(v) = c.to_digit(10) {
|
||||||
|
let value = v as u8;
|
||||||
|
cells[row][col] = Cell {
|
||||||
|
value,
|
||||||
|
loaded: value != 0,
|
||||||
|
invalid: false,
|
||||||
|
};
|
||||||
|
col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
cells,
|
||||||
|
completed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&mut self, ind: [usize; 2], val: u8) {
|
||||||
|
let [b, a] = ind;
|
||||||
|
for i in 0..SIZE {
|
||||||
|
if i == a {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self.cells[a][i].value == val {
|
||||||
|
self.cells[a][b].invalid = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..SIZE {
|
||||||
|
if i == b {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self.cells[i][b].value == val {
|
||||||
|
self.cells[a][b].invalid = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (row, col) = (a / 3, b / 3);
|
||||||
|
for i in 3 * row..3 * row + 3 {
|
||||||
|
for j in 3 * col..3 * col + 3 {
|
||||||
|
if i == a && j == b {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self.cells[i][j].value == val {
|
||||||
|
self.cells[a][b].invalid = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.cells[a][b].invalid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_sdm() {
|
||||||
|
let got = Gameboard::load_sdm("static/puzzle.sdm");
|
||||||
|
let want = Gameboard::from_cells([
|
||||||
|
[0, 1, 6, 4, 0, 0, 0, 0, 0],
|
||||||
|
[2, 0, 0, 0, 0, 9, 0, 0, 0],
|
||||||
|
[4, 0, 0, 0, 0, 0, 0, 6, 2],
|
||||||
|
[0, 7, 0, 2, 3, 0, 1, 0, 0],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 3],
|
||||||
|
[0, 0, 3, 0, 8, 7, 0, 4, 0],
|
||||||
|
[9, 6, 0, 0, 0, 0, 0, 0, 5],
|
||||||
|
[0, 0, 0, 8, 0, 0, 0, 0, 7],
|
||||||
|
[0, 0, 0, 0, 0, 6, 8, 2, 0],
|
||||||
|
]);
|
||||||
|
assert_eq!(got, want);
|
||||||
|
}
|
||||||
|
}
|
57
piston-tutorials/sudoku/src/gameboard_controller.rs
Normal file
57
piston-tutorials/sudoku/src/gameboard_controller.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//! Gameboard controller
|
||||||
|
|
||||||
|
use piston::GenericEvent;
|
||||||
|
|
||||||
|
use crate::Gameboard;
|
||||||
|
|
||||||
|
pub struct GameboardController {
|
||||||
|
pub gameboard: Gameboard,
|
||||||
|
pub selected_cell: Option<[usize; 2]>,
|
||||||
|
cursor_pos: [f64; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameboardController {
|
||||||
|
pub fn new(gameboard: Gameboard) -> GameboardController {
|
||||||
|
GameboardController {
|
||||||
|
gameboard: gameboard,
|
||||||
|
selected_cell: None,
|
||||||
|
cursor_pos: [0.0; 2],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event<E: GenericEvent>(&mut self, pos: [f64; 2], size: f64, e: &E) {
|
||||||
|
use piston::input::{Button, Key, MouseButton};
|
||||||
|
|
||||||
|
if let Some(pos) = e.mouse_cursor_args() {
|
||||||
|
self.cursor_pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
|
||||||
|
let x = self.cursor_pos[0] - pos[0];
|
||||||
|
let y = self.cursor_pos[1] - pos[1];
|
||||||
|
|
||||||
|
if x >= 0.0 && x < size && y >= 0.0 && y < size {
|
||||||
|
let cell_x = (x / size * 9.0) as usize;
|
||||||
|
let cell_y = (y / size * 9.0) as usize;
|
||||||
|
self.selected_cell = Some([cell_x, cell_y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Button::Keyboard(key)) = e.press_args() {
|
||||||
|
if let Some(ind) = self.selected_cell {
|
||||||
|
match key {
|
||||||
|
Key::D1 => self.gameboard.set(ind, 1),
|
||||||
|
Key::D2 => self.gameboard.set(ind, 2),
|
||||||
|
Key::D3 => self.gameboard.set(ind, 3),
|
||||||
|
Key::D4 => self.gameboard.set(ind, 4),
|
||||||
|
Key::D5 => self.gameboard.set(ind, 5),
|
||||||
|
Key::D6 => self.gameboard.set(ind, 6),
|
||||||
|
Key::D7 => self.gameboard.set(ind, 7),
|
||||||
|
Key::D8 => self.gameboard.set(ind, 8),
|
||||||
|
Key::D9 => self.gameboard.set(ind, 9),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
210
piston-tutorials/sudoku/src/gameboard_view.rs
Normal file
210
piston-tutorials/sudoku/src/gameboard_view.rs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
//! Gameboard view.
|
||||||
|
|
||||||
|
use graphics::character::CharacterCache;
|
||||||
|
use graphics::types::Color;
|
||||||
|
use graphics::{Context, Graphics};
|
||||||
|
|
||||||
|
use crate::gameboard_controller::GameboardController;
|
||||||
|
|
||||||
|
pub struct GameboardViewSettings {
|
||||||
|
pub position: [f64; 2],
|
||||||
|
pub size: f64,
|
||||||
|
pub background_color: Color,
|
||||||
|
pub border_color: Color,
|
||||||
|
pub board_edge_color: Color,
|
||||||
|
pub section_edge_color: Color,
|
||||||
|
pub cell_edge_color: Color,
|
||||||
|
pub board_edge_radius: f64,
|
||||||
|
pub section_edge_radius: f64,
|
||||||
|
pub cell_edge_radius: f64,
|
||||||
|
pub selected_cell_background_color: Color,
|
||||||
|
pub text_color: Color,
|
||||||
|
pub loaded_cell_background_color: Color,
|
||||||
|
pub invalid_cell_background_color: Color,
|
||||||
|
pub invalid_selected_cell_background_color: Color,
|
||||||
|
pub completed_background_color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameboardViewSettings {
|
||||||
|
pub fn new() -> GameboardViewSettings {
|
||||||
|
GameboardViewSettings {
|
||||||
|
position: [10.0; 2],
|
||||||
|
size: 400.0,
|
||||||
|
background_color: [0.8, 0.8, 1.0, 1.0],
|
||||||
|
border_color: [0.0, 0.0, 0.2, 1.0],
|
||||||
|
board_edge_color: [0.0, 0.0, 0.2, 1.0],
|
||||||
|
section_edge_color: [0.0, 0.0, 0.2, 1.0],
|
||||||
|
cell_edge_color: [0.0, 0.0, 0.2, 1.0],
|
||||||
|
board_edge_radius: 3.0,
|
||||||
|
section_edge_radius: 2.0,
|
||||||
|
cell_edge_radius: 1.0,
|
||||||
|
selected_cell_background_color: [0.9, 0.9, 1.0, 1.0],
|
||||||
|
text_color: [0.0, 0.0, 0.1, 1.0],
|
||||||
|
loaded_cell_background_color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
invalid_cell_background_color: [1.0, 0.0, 0.0, 1.0],
|
||||||
|
invalid_selected_cell_background_color: [1.0, 0.0, 0.5, 1.0],
|
||||||
|
completed_background_color: [0.0, 1.0, 0.0, 1.0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GameboardView {
|
||||||
|
pub settings: GameboardViewSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameboardView {
|
||||||
|
pub fn new(settings: GameboardViewSettings) -> GameboardView {
|
||||||
|
GameboardView { settings: settings }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw<G: Graphics, C>(
|
||||||
|
&self,
|
||||||
|
controller: &GameboardController,
|
||||||
|
glyphs: &mut C,
|
||||||
|
c: &Context,
|
||||||
|
g: &mut G,
|
||||||
|
) where
|
||||||
|
C: CharacterCache<Texture = G::Texture>,
|
||||||
|
{
|
||||||
|
use graphics::{Image, Line, Rectangle, Transformed};
|
||||||
|
|
||||||
|
let ref settings = self.settings;
|
||||||
|
let board_rect = [
|
||||||
|
settings.position[0],
|
||||||
|
settings.position[1],
|
||||||
|
settings.size,
|
||||||
|
settings.size,
|
||||||
|
];
|
||||||
|
|
||||||
|
if controller.gameboard.completed {
|
||||||
|
Rectangle::new(settings.completed_background_color).draw(
|
||||||
|
board_rect,
|
||||||
|
&c.draw_state,
|
||||||
|
c.transform,
|
||||||
|
g,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Rectangle::new(settings.background_color).draw(
|
||||||
|
board_rect,
|
||||||
|
&c.draw_state,
|
||||||
|
c.transform,
|
||||||
|
g,
|
||||||
|
);
|
||||||
|
|
||||||
|
for i in 0..9 {
|
||||||
|
for j in 0..9 {
|
||||||
|
if controller.gameboard.cells[i][j].loaded {
|
||||||
|
color_cell(
|
||||||
|
settings,
|
||||||
|
[j, i],
|
||||||
|
settings.loaded_cell_background_color,
|
||||||
|
c,
|
||||||
|
g,
|
||||||
|
);
|
||||||
|
} else if controller.gameboard.cells[i][j].invalid {
|
||||||
|
color_cell(
|
||||||
|
settings,
|
||||||
|
[j, i],
|
||||||
|
settings.invalid_cell_background_color,
|
||||||
|
c,
|
||||||
|
g,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ind) = controller.selected_cell {
|
||||||
|
let cell = controller.gameboard.cells[ind[1]][ind[0]];
|
||||||
|
let color = if !cell.loaded {
|
||||||
|
if !cell.invalid {
|
||||||
|
settings.selected_cell_background_color
|
||||||
|
} else {
|
||||||
|
settings.invalid_selected_cell_background_color
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
settings.loaded_cell_background_color
|
||||||
|
};
|
||||||
|
color_cell(settings, ind, color, c, g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let text_image = Image::new_color(settings.text_color);
|
||||||
|
let cell_size = settings.size / 9.0;
|
||||||
|
|
||||||
|
for j in 0..9 {
|
||||||
|
for i in 0..9 {
|
||||||
|
if let Some(ch) = controller.gameboard.char([i, j]) {
|
||||||
|
let pos = [
|
||||||
|
settings.position[0] + i as f64 * cell_size + 15.0,
|
||||||
|
settings.position[1] + j as f64 * cell_size + 34.0,
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Ok(character) = glyphs.character(34, ch) {
|
||||||
|
let ch_x = pos[0] + character.left();
|
||||||
|
let ch_y = pos[1] - character.top();
|
||||||
|
let text_image = text_image.src_rect([
|
||||||
|
character.atlas_offset[0],
|
||||||
|
character.atlas_offset[1],
|
||||||
|
character.atlas_size[0],
|
||||||
|
character.atlas_size[1],
|
||||||
|
]);
|
||||||
|
text_image.draw(
|
||||||
|
character.texture,
|
||||||
|
&c.draw_state,
|
||||||
|
c.transform.trans(ch_x, ch_y),
|
||||||
|
g,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius);
|
||||||
|
let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius);
|
||||||
|
|
||||||
|
for i in 0..9 {
|
||||||
|
let x = settings.position[0] + i as f64 / 9.0 * settings.size;
|
||||||
|
let y = settings.position[1] + i as f64 / 9.0 * settings.size;
|
||||||
|
let x2 = settings.position[0] + settings.size;
|
||||||
|
let y2 = settings.position[1] + settings.size;
|
||||||
|
|
||||||
|
let vline = [x, settings.position[1], x, y2];
|
||||||
|
let hline = [settings.position[0], y, x2, y];
|
||||||
|
|
||||||
|
if (i % 3) == 0 {
|
||||||
|
section_edge.draw(vline, &c.draw_state, c.transform, g);
|
||||||
|
section_edge.draw(hline, &c.draw_state, c.transform, g);
|
||||||
|
} else {
|
||||||
|
cell_edge.draw(vline, &c.draw_state, c.transform, g);
|
||||||
|
cell_edge.draw(hline, &c.draw_state, c.transform, g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius).draw(
|
||||||
|
board_rect,
|
||||||
|
&c.draw_state,
|
||||||
|
c.transform,
|
||||||
|
g,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn color_cell<G: Graphics>(
|
||||||
|
settings: &GameboardViewSettings,
|
||||||
|
ind: [usize; 2],
|
||||||
|
color: [f32; 4],
|
||||||
|
c: &Context,
|
||||||
|
g: &mut G,
|
||||||
|
) {
|
||||||
|
use graphics::Rectangle;
|
||||||
|
|
||||||
|
let cell_size = settings.size / 9.0;
|
||||||
|
let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size];
|
||||||
|
let cell_rect = [
|
||||||
|
settings.position[0] + pos[0],
|
||||||
|
settings.position[1] + pos[1],
|
||||||
|
cell_size,
|
||||||
|
cell_size,
|
||||||
|
];
|
||||||
|
Rectangle::new(color).draw(cell_rect, &c.draw_state, c.transform, g);
|
||||||
|
}
|
57
piston-tutorials/sudoku/src/main.rs
Normal file
57
piston-tutorials/sudoku/src/main.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//////#![deny(missing_docs)]
|
||||||
|
|
||||||
|
//! An Sudoko please.
|
||||||
|
|
||||||
|
extern crate glutin_window;
|
||||||
|
|
||||||
|
use glutin_window::GlutinWindow;
|
||||||
|
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
|
||||||
|
use piston::event_loop::{EventSettings, Events};
|
||||||
|
use piston::{EventLoop, RenderEvent, WindowSettings};
|
||||||
|
|
||||||
|
pub use crate::gameboard::Gameboard;
|
||||||
|
pub use crate::gameboard_controller::GameboardController;
|
||||||
|
pub use crate::gameboard_view::{GameboardView, GameboardViewSettings};
|
||||||
|
|
||||||
|
mod gameboard;
|
||||||
|
mod gameboard_controller;
|
||||||
|
mod gameboard_view;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opengl = OpenGL::V3_2;
|
||||||
|
let settings = WindowSettings::new("Sudoku", (640, 480))
|
||||||
|
.exit_on_esc(true)
|
||||||
|
.graphics_api(opengl)
|
||||||
|
.vsync(true);
|
||||||
|
let mut window: GlutinWindow = settings.build().expect("could not create window");
|
||||||
|
let mut events = Events::new(EventSettings::new().lazy(true));
|
||||||
|
let mut gl = GlGraphics::new(opengl);
|
||||||
|
|
||||||
|
let args: Vec<_> = std::env::args().collect();
|
||||||
|
let infile = args.get(1).expect("usage: sudoku <sdm-file>");
|
||||||
|
|
||||||
|
let gameboard = Gameboard::load_sdm(infile);
|
||||||
|
let mut gameboard_controller = GameboardController::new(gameboard);
|
||||||
|
let gameboard_view_settings = GameboardViewSettings::new();
|
||||||
|
let gameboard_view = GameboardView::new(gameboard_view_settings);
|
||||||
|
|
||||||
|
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
|
||||||
|
let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings)
|
||||||
|
.expect("Could not load font");
|
||||||
|
|
||||||
|
while let Some(e) = events.next(&mut window) {
|
||||||
|
gameboard_controller.event(
|
||||||
|
gameboard_view.settings.position,
|
||||||
|
gameboard_view.settings.size,
|
||||||
|
&e,
|
||||||
|
);
|
||||||
|
if let Some(args) = e.render_args() {
|
||||||
|
gl.draw(args.viewport(), |c, g| {
|
||||||
|
use graphics::clear;
|
||||||
|
|
||||||
|
clear([1.0; 4], g);
|
||||||
|
gameboard_view.draw(&gameboard_controller, glyphs, &c, g);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
piston-tutorials/sudoku/static/puzzle-almost-solved.sdm
Normal file
1
piston-tutorials/sudoku/static/puzzle-almost-solved.sdm
Normal file
@@ -0,0 +1 @@
|
|||||||
|
517962483236847915498351762371695248654218397829734156765129834142583679983476520
|
1
piston-tutorials/sudoku/static/puzzle.sdm
Normal file
1
piston-tutorials/sudoku/static/puzzle.sdm
Normal file
@@ -0,0 +1 @@
|
|||||||
|
016400000200009000400000062070230100100000003003087040960000005000800007000006820
|
85
rustbook/guessing_game/Cargo.lock
generated
Normal file
85
rustbook/guessing_game/Cargo.lock
generated
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "guessing_game"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
@@ -7,4 +7,4 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.6.0"
|
rand = "0.8.3"
|
@@ -5,7 +5,7 @@ use std::io;
|
|||||||
fn main() {
|
fn main() {
|
||||||
println!("Guess the number!");
|
println!("Guess the number!");
|
||||||
|
|
||||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
let secret_number = rand::thread_rng().gen_range(1..=100);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
println!("Please input your guess.");
|
println!("Please input your guess.");
|
27
rustbook/hello_cargo/src/main.rs
Normal file
27
rustbook/hello_cargo/src/main.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const SEP: &str = "\n---- bleh ----\n";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
|
||||||
|
println!("{}", SEP);
|
||||||
|
|
||||||
|
println!("0x{:x}", 0b1111_1010_1111_1010_1111_1010_1111);
|
||||||
|
println!("0b{:b}", 0xfafafaf);
|
||||||
|
println!("{}", 0xfafafaf);
|
||||||
|
println!("{}", 0xfeefee);
|
||||||
|
println!("0b{:b}", 0xfeefee);
|
||||||
|
|
||||||
|
println!("{}", SEP);
|
||||||
|
|
||||||
|
let sum = 5 + 10;
|
||||||
|
let difference = 95.5 - 4.3;
|
||||||
|
let product = 4 * 30;
|
||||||
|
let quotient = 56.7 / 32.2;
|
||||||
|
let remainder = 43 % 5;
|
||||||
|
|
||||||
|
println!("sum: {}", sum);
|
||||||
|
println!("difference: {}", difference);
|
||||||
|
println!("product: {}", product);
|
||||||
|
println!("quotient: {}", quotient);
|
||||||
|
println!("remainder: {}", remainder);
|
||||||
|
}
|
BIN
rustbook/hello_world/main
Executable file
BIN
rustbook/hello_world/main
Executable file
Binary file not shown.
7
rustbyexample/.gitignore
vendored
7
rustbyexample/.gitignore
vendored
@@ -1,4 +1,11 @@
|
|||||||
|
/custom_types/*
|
||||||
/hello/*
|
/hello/*
|
||||||
/primitives/*
|
/primitives/*
|
||||||
|
/variable_bindings/*
|
||||||
|
/types/*
|
||||||
|
|
||||||
|
!/custom_types/*.rs
|
||||||
!/hello/*.rs
|
!/hello/*.rs
|
||||||
!/primitives/*.rs
|
!/primitives/*.rs
|
||||||
|
!/variable_bindings/*.rs
|
||||||
|
!/types/*.rs
|
||||||
|
16
rustbyexample/custom_types/custom_types_constants.rs
Normal file
16
rustbyexample/custom_types/custom_types_constants.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
static LANGUAGE: &str = "Rust";
|
||||||
|
const THRESHOLD: i32 = 10;
|
||||||
|
|
||||||
|
fn is_big(n: i32) -> bool {
|
||||||
|
n > THRESHOLD
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let n = 16;
|
||||||
|
|
||||||
|
println!("This is {}", LANGUAGE);
|
||||||
|
println!("The threshold is {}", THRESHOLD);
|
||||||
|
println!("{} is {}", n, if is_big(n) { "big" } else { "small" });
|
||||||
|
|
||||||
|
// THRESHOLD = 5;
|
||||||
|
}
|
33
rustbyexample/custom_types/custom_types_enum.rs
Normal file
33
rustbyexample/custom_types/custom_types_enum.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
enum WebEvent {
|
||||||
|
PageLoad,
|
||||||
|
PageUnload,
|
||||||
|
KeyPress(char),
|
||||||
|
Paste(String),
|
||||||
|
Click { x: i64, y: i64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inspect(event: WebEvent) {
|
||||||
|
match event {
|
||||||
|
WebEvent::PageLoad => println!("page loaded"),
|
||||||
|
WebEvent::PageUnload => println!("page unloaded"),
|
||||||
|
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
|
||||||
|
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
|
||||||
|
WebEvent::Click { x, y } => {
|
||||||
|
println!("clicked at x={}, y={}.", x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let pressed = WebEvent::KeyPress('x');
|
||||||
|
let pasted = WebEvent::Paste("my text".to_owned());
|
||||||
|
let click = WebEvent::Click { x: 20, y: 80 };
|
||||||
|
let load = WebEvent::PageLoad;
|
||||||
|
let unload = WebEvent::PageUnload;
|
||||||
|
|
||||||
|
inspect(pressed);
|
||||||
|
inspect(pasted);
|
||||||
|
inspect(click);
|
||||||
|
inspect(load);
|
||||||
|
inspect(unload);
|
||||||
|
}
|
21
rustbyexample/custom_types/custom_types_enum_c_like.rs
Normal file
21
rustbyexample/custom_types/custom_types_enum_c_like.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
enum Number {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
Red = 0xff0000,
|
||||||
|
Green = 0x00ff00,
|
||||||
|
Blue = 0x0000ff,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("zero is {}", Number::Zero as i32);
|
||||||
|
println!("one is {}", Number::One as i32);
|
||||||
|
|
||||||
|
println!("roses are #{:06x}", Color::Red as i32);
|
||||||
|
println!("violets are #{:06x}", Color::Blue as i32);
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
use crate::List::*;
|
||||||
|
|
||||||
|
enum List {
|
||||||
|
Cons(u32, Box<List>),
|
||||||
|
Nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
fn new() -> List {
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepend(self, elem: u32) -> List {
|
||||||
|
Cons(elem, Box::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> u32 {
|
||||||
|
match *self {
|
||||||
|
Cons(_, ref tail) => 1 + tail.len(),
|
||||||
|
Nil => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stringify(&self) -> String {
|
||||||
|
match *self {
|
||||||
|
Cons(head, ref tail) => {
|
||||||
|
format!("{}, {}", head, tail.stringify())
|
||||||
|
}
|
||||||
|
Nil => {
|
||||||
|
format!("Nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut list = List::new();
|
||||||
|
|
||||||
|
list = list.prepend(1);
|
||||||
|
list = list.prepend(2);
|
||||||
|
list = list.prepend(3);
|
||||||
|
|
||||||
|
println!("linked list has length: {}", list.len());
|
||||||
|
println!("{}", list.stringify());
|
||||||
|
}
|
29
rustbyexample/custom_types/custom_types_enum_use.rs
Normal file
29
rustbyexample/custom_types/custom_types_enum_use.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
Rich,
|
||||||
|
Poor,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Work {
|
||||||
|
Civilian,
|
||||||
|
Soldier,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
use crate::Status::{Poor, Rich};
|
||||||
|
use crate::Work::*;
|
||||||
|
|
||||||
|
let status = Poor;
|
||||||
|
let work = Civilian;
|
||||||
|
|
||||||
|
match status {
|
||||||
|
Rich => println!("The rich have lots of money!"),
|
||||||
|
Poor => println!("The poor have no money..."),
|
||||||
|
}
|
||||||
|
|
||||||
|
match work {
|
||||||
|
Civilian => println!("Civilians work!"),
|
||||||
|
Soldier => println!("Soldiers fight!"),
|
||||||
|
}
|
||||||
|
}
|
79
rustbyexample/custom_types/custom_types_structs.rs
Normal file
79
rustbyexample/custom_types/custom_types_structs.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
struct Person {
|
||||||
|
name: String,
|
||||||
|
age: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Unit;
|
||||||
|
|
||||||
|
struct Pair(i32, f32);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Rectangle {
|
||||||
|
top_left: Point,
|
||||||
|
bottom_right: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect_area(r: Rectangle) -> f32 {
|
||||||
|
(r.top_left.x - r.bottom_right.x) * (r.top_left.y - r.bottom_right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(p: &Point, d: f32) -> Rectangle {
|
||||||
|
Rectangle {
|
||||||
|
top_left: Point { x: p.x + d, y: p.y },
|
||||||
|
bottom_right: Point { x: p.x, y: p.y + d },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let name = String::from("Peter");
|
||||||
|
let age = 27;
|
||||||
|
let peter = Person { name, age };
|
||||||
|
|
||||||
|
println!("{:?}", peter);
|
||||||
|
|
||||||
|
let point: Point = Point { x: 10.3, y: 0.4 };
|
||||||
|
|
||||||
|
println!("point coordinates: ({}, {})", point.x, point.y);
|
||||||
|
|
||||||
|
let bottom_right = Point { x: 5.2, y: 0.2 };
|
||||||
|
|
||||||
|
println!("second point: ({}, {})", bottom_right.x, bottom_right.y);
|
||||||
|
|
||||||
|
let Point {
|
||||||
|
x: left_edge,
|
||||||
|
y: top_edge,
|
||||||
|
} = point;
|
||||||
|
|
||||||
|
let rect = Rectangle {
|
||||||
|
top_left: Point {
|
||||||
|
x: left_edge,
|
||||||
|
y: top_edge,
|
||||||
|
},
|
||||||
|
bottom_right: bottom_right,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _unit = Unit;
|
||||||
|
|
||||||
|
let pair = Pair(1, 0.1);
|
||||||
|
|
||||||
|
println!("pair contains {:?} and {:?}", pair.0, pair.1);
|
||||||
|
|
||||||
|
let Pair(integer, decimal) = pair;
|
||||||
|
|
||||||
|
println!("pair contains {:?} and {:?}", integer, decimal);
|
||||||
|
|
||||||
|
println!("rect in {:?}", rect);
|
||||||
|
println!("area of rect is {}", rect_area(rect));
|
||||||
|
|
||||||
|
let lower_left = Point { x: 4.1, y: 11.3 };
|
||||||
|
let d = 7.7f32;
|
||||||
|
let sq = square(&lower_left, d);
|
||||||
|
println!("sq from point={:?} of d={} is {:?}", lower_left, d, sq);
|
||||||
|
}
|
26
rustbyexample/primitives/primitives_array.rs
Normal file
26
rustbyexample/primitives/primitives_array.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use std::mem;
|
||||||
|
|
||||||
|
fn analyze_slice(slice: &[i32]) {
|
||||||
|
println!("first element of the slice: {}", slice[0]);
|
||||||
|
println!("the slice has {} elements", slice.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let xs: [i32; 5] = [1, 2, 3, 4, 5];
|
||||||
|
let ys: [i32; 500] = [0; 500];
|
||||||
|
|
||||||
|
println!("first element of the array: {}", xs[0]);
|
||||||
|
println!("second element of the array: {}", xs[1]);
|
||||||
|
|
||||||
|
println!("number of elements in the array: {}", xs.len());
|
||||||
|
|
||||||
|
println!("array occupies {} bytes", mem::size_of_val(&xs));
|
||||||
|
|
||||||
|
println!("borrow the whole array as a slice");
|
||||||
|
analyze_slice(&xs);
|
||||||
|
|
||||||
|
println!("borrow a section of the array as a slice");
|
||||||
|
analyze_slice(&ys[1..4]);
|
||||||
|
|
||||||
|
// println!("{}", xs[5]);
|
||||||
|
}
|
15
rustbyexample/types/alias.rs
Normal file
15
rustbyexample/types/alias.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
type NanoSecond = u64;
|
||||||
|
type Inch = u64;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type u64_t = u64;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nanoseconds: NanoSecond = 5 as u64_t;
|
||||||
|
let inches: Inch = 2 as u64_t;
|
||||||
|
|
||||||
|
println!("{} nanoseconds + {} inches = {} unit?",
|
||||||
|
nanoseconds,
|
||||||
|
inches,
|
||||||
|
nanoseconds + inches);
|
||||||
|
}
|
52
rustbyexample/types/cast.rs
Normal file
52
rustbyexample/types/cast.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#![allow(overflowing_literals)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let decimal = 65.4321_f32;
|
||||||
|
// let integer: u8 = decimal;
|
||||||
|
|
||||||
|
let integer = decimal as u8;
|
||||||
|
let character = integer as char;
|
||||||
|
|
||||||
|
// let character = decimal as char;
|
||||||
|
|
||||||
|
println!("Casting: {} -> {} -> {}", decimal, integer, character);
|
||||||
|
|
||||||
|
println!("1000 as u16 is: {}", 1000 as u16);
|
||||||
|
|
||||||
|
println!("1000 as u8 is: {}", 1000 as u8);
|
||||||
|
|
||||||
|
println!(" -1 as u8 is: {}", (-1i8) as u8);
|
||||||
|
|
||||||
|
println!("1000 mod 256 is: {}", 1000 % 256);
|
||||||
|
|
||||||
|
println!(" 128 as a i16 is: {}", 128 as i16);
|
||||||
|
|
||||||
|
println!(" 128 as a i8 is: {}", 128 as i8);
|
||||||
|
|
||||||
|
println!("1000 as a u8 is: {}", 1000 as u8);
|
||||||
|
|
||||||
|
println!(" 232 as a i8 is: {}", 232 as i8);
|
||||||
|
|
||||||
|
println!("300.0 as u8 is {}", 300.0_f32 as u8);
|
||||||
|
|
||||||
|
println!("-100.0 as u8 is {}", -100.0_f32 as u8);
|
||||||
|
|
||||||
|
println!("nan as u8 is {}", f32::NAN as u8);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
println!(
|
||||||
|
"300.0 as u8 is {} (unchecked)",
|
||||||
|
300.0_f32.to_int_unchecked::<u8>()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"-100.0 as u8 is {} (unchecked)",
|
||||||
|
(-100.0_f32).to_int_unchecked::<u8>()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"nan as u8 is {} (unchecked)",
|
||||||
|
f32::NAN.to_int_unchecked::<u8>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
9
rustbyexample/types/inference.rs
Normal file
9
rustbyexample/types/inference.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fn main() {
|
||||||
|
let elem = 5u8;
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
vec.push(elem);
|
||||||
|
|
||||||
|
println!("{:?}", vec);
|
||||||
|
}
|
14
rustbyexample/types/literals.rs
Normal file
14
rustbyexample/types/literals.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
fn main() {
|
||||||
|
let x = 1u8;
|
||||||
|
let y = 2u32;
|
||||||
|
let z = 3f32;
|
||||||
|
|
||||||
|
let i = 1;
|
||||||
|
let f = 1.0;
|
||||||
|
|
||||||
|
println!("size of `x` in bytes: {}", std::mem::size_of_val(&x));
|
||||||
|
println!("size of `y` in bytes: {}", std::mem::size_of_val(&y));
|
||||||
|
println!("size of `z` in bytes: {}", std::mem::size_of_val(&z));
|
||||||
|
println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));
|
||||||
|
println!("size of `f` in bytes: {}", std::mem::size_of_val(&f));
|
||||||
|
}
|
19
rustbyexample/variable_bindings/declare.rs
Normal file
19
rustbyexample/variable_bindings/declare.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
fn main() {
|
||||||
|
let a_binding;
|
||||||
|
|
||||||
|
{
|
||||||
|
let x = 2;
|
||||||
|
|
||||||
|
a_binding = x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("a binding: {}", a_binding);
|
||||||
|
|
||||||
|
let another_binding;
|
||||||
|
|
||||||
|
//println!("another binding: {}", another_binding);
|
||||||
|
|
||||||
|
another_binding = 1;
|
||||||
|
|
||||||
|
println!("another binding: {}", another_binding);
|
||||||
|
}
|
11
rustbyexample/variable_bindings/freeze.rs
Normal file
11
rustbyexample/variable_bindings/freeze.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fn main() {
|
||||||
|
let mut _mutable_integer = 7i32;
|
||||||
|
|
||||||
|
{
|
||||||
|
let _mutable_integer = _mutable_integer;
|
||||||
|
|
||||||
|
// _mutable_integer = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutable_integer = 3;
|
||||||
|
}
|
12
rustbyexample/variable_bindings/mut.rs
Normal file
12
rustbyexample/variable_bindings/mut.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fn main() {
|
||||||
|
let _immutable_binding = 1;
|
||||||
|
let mut mutable_binding = 1;
|
||||||
|
|
||||||
|
println!("Before mutation: {}", mutable_binding);
|
||||||
|
|
||||||
|
mutable_binding += 1;
|
||||||
|
|
||||||
|
println!("After mutation: {}", mutable_binding);
|
||||||
|
|
||||||
|
// _immutable_binding += 1;
|
||||||
|
}
|
12
rustbyexample/variable_bindings/scope.rs
Normal file
12
rustbyexample/variable_bindings/scope.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fn main() {
|
||||||
|
let long_lived_binding = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
let short_lived_binding = 2;
|
||||||
|
println!("inner short: {}", short_lived_binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("outer short: {}", short_lived_binding);
|
||||||
|
|
||||||
|
println!("outer long: {}", long_lived_binding);
|
||||||
|
}
|
16
rustbyexample/variable_bindings/scope2.rs
Normal file
16
rustbyexample/variable_bindings/scope2.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
fn main() {
|
||||||
|
let shadowed_binding = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
println!("before being shadowed: {}", shadowed_binding);
|
||||||
|
|
||||||
|
let shadowed_binding = "abc";
|
||||||
|
|
||||||
|
println!("shadowed in inner block: {}", shadowed_binding);
|
||||||
|
}
|
||||||
|
println!("outside inner block: {}", shadowed_binding);
|
||||||
|
|
||||||
|
let shadowed_binding = 2;
|
||||||
|
println!("shadowed in outer block: {}", shadowed_binding);
|
||||||
|
}
|
||||||
|
|
15
rustbyexample/variable_bindings/variable_bindings.rs
Normal file
15
rustbyexample/variable_bindings/variable_bindings.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
fn main() {
|
||||||
|
let an_integer = 1u32;
|
||||||
|
let a_boolean = true;
|
||||||
|
let unit = ();
|
||||||
|
|
||||||
|
let copied_integer = an_integer;
|
||||||
|
|
||||||
|
println!("An integer: {:?}", copied_integer);
|
||||||
|
println!("A boolean: {:?}", a_boolean);
|
||||||
|
println!("Meet the unit value: {:?}", unit);
|
||||||
|
|
||||||
|
let _unused_variable = 3u32;
|
||||||
|
|
||||||
|
let noisy_unused_variable = 2u32;
|
||||||
|
}
|
@@ -1,54 +0,0 @@
|
|||||||
#!/usr/bin/env oh-no
|
|
||||||
|
|
||||||
set shell := ["python", "-c"]
|
|
||||||
set something
|
|
||||||
|
|
||||||
alias d := dogs
|
|
||||||
|
|
||||||
export HOTDOGPARTY := hats
|
|
||||||
|
|
||||||
now_ish := `print("oh gee")` + `print(sum([1, 2, 5]))`
|
|
||||||
|
|
||||||
an_string := "sit ready \"for reals\" 'uh huh' {{an_raw_string + '{{what' }}"
|
|
||||||
|
|
||||||
an_raw_string := 'sit ready "wow" {{not_gonna}}'
|
|
||||||
|
|
||||||
hats := '''
|
|
||||||
very serious hat's
|
|
||||||
'''
|
|
||||||
|
|
||||||
wats := """
|
|
||||||
{{an_string}} \"yay\" 'ok'
|
|
||||||
|
|
||||||
{{ 'dog' + just_executable()}}
|
|
||||||
|
|
||||||
{{justfile()}}
|
|
||||||
|
|
||||||
{{justfile_directory()}}
|
|
||||||
|
|
||||||
{{arch()}} {{os()}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
serious_business := ```
|
|
||||||
from __future__ import print_function
|
|
||||||
import os
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
print(f"home: {os.environ.get("HOME", "?")}")
|
|
||||||
print(f"now-ish: {datetime.datetime.now()}")
|
|
||||||
```
|
|
||||||
|
|
||||||
dogs: aminal manimal _secret
|
|
||||||
|
|
||||||
# show the aminal by kind
|
|
||||||
aminal kind='dog':
|
|
||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
print('\n'.join(os.environ.keys()), file=sys.stderr)
|
|
||||||
|
|
||||||
_secret +WHATEVER='OK':
|
|
||||||
@print("don't tell anyone {{WHATEVER}}")
|
|
||||||
|
|
||||||
manimal:
|
|
||||||
@print('wow')
|
|
@@ -1 +0,0 @@
|
|||||||
autocmd BufReadPost justfile,*.justfile setfiletype just
|
|
@@ -1,80 +0,0 @@
|
|||||||
" Vim syntax file
|
|
||||||
" Language: Just Command Runner
|
|
||||||
" Maintainer: Dan Buch <dan@meatballhat.com>
|
|
||||||
" Latest Revision: 28 May 2021
|
|
||||||
|
|
||||||
if exists("b:current_syntax")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
let b:shell_syntax = "sh"
|
|
||||||
if exists("g:just_shell_syntax")
|
|
||||||
let b:shell_syntax = g:just_shell_syntax
|
|
||||||
endif
|
|
||||||
|
|
||||||
exe "syn include @setshellsyntax syntax/" . b:shell_syntax . ".vim"
|
|
||||||
|
|
||||||
syn keyword justKeyword alias export if else set
|
|
||||||
|
|
||||||
syn keyword justFunction arch os os_family
|
|
||||||
syn keyword justFunction env_var env_var_or_default invocation_directory
|
|
||||||
syn keyword justFunction justfile justfile_directory just_executable
|
|
||||||
|
|
||||||
syn keyword justSetting shell export dotenv-load positional-arguments
|
|
||||||
|
|
||||||
syn match justOperator "\v:"
|
|
||||||
syn match justOperator "\v\)"
|
|
||||||
syn match justOperator "\v\("
|
|
||||||
syn match justOperator "\v\+"
|
|
||||||
syn match justOperator "\v\@"
|
|
||||||
syn match justOperator "\v\="
|
|
||||||
syn match justOperator "\v:\="
|
|
||||||
|
|
||||||
syn match justQuote '\v"'
|
|
||||||
syn match justQuote "\v'"
|
|
||||||
syn match justQuote '\v"""'
|
|
||||||
syn match justQuote "\v'''"
|
|
||||||
|
|
||||||
syn match justBacktick "\v\`"
|
|
||||||
syn match justBacktick "\v\`\`\`"
|
|
||||||
|
|
||||||
syn match justCurlyBrace "\v\{\{"
|
|
||||||
syn match justCurlyBrace "\v\}\}"
|
|
||||||
|
|
||||||
syn match justComment "\v#.*$"
|
|
||||||
|
|
||||||
syn match justShebang "\v^ *#!.*$"
|
|
||||||
|
|
||||||
syn match justPublicName "\v[a-zA-Z][_a-zA-Z0-9-]*[a-zA-Z0-9_]"
|
|
||||||
syn match justPrivateName "\v_[a-zA-Z0-9][_a-zA-Z0-9-]*[a-zA-Z0-9_]"
|
|
||||||
|
|
||||||
syn region justCurlyBraced matchgroup=justCurlyBrace start="\v\{\{" skip="\v\\\{" end="\v\}\}" contains=justKeyword,justFunction,justOperator,justQuote,justString,justRawString
|
|
||||||
|
|
||||||
syn region justString matchgroup=justQuote start='\v"' skip='\v\\"' end='\v"' contains=justCurlyBraced
|
|
||||||
syn region justString matchgroup=justQuote start='\v"""' skip='\v\\"' end='\v"""' contains=justCurlyBraced
|
|
||||||
|
|
||||||
syn region justRawString matchgroup=justQuote start="\v'" end="\v'"
|
|
||||||
syn region justRawString matchgroup=justQuote start="\v'''" end="\v'''"
|
|
||||||
|
|
||||||
syn region justSubshell matchgroup=justBacktick start="\v\`" skip="\v\\\`" end="\v\`" contains=@setshellsyntax
|
|
||||||
syn region justSubshell matchgroup=justBacktick start="\v\`\`\`" skip="\v\\\`" end="\v\`\`\`" contains=@setshellsyntax
|
|
||||||
syn region justRecipeBody start="\v^ *" end="$" keepend contains=@setshellsyntax
|
|
||||||
syn region justRecipeNoechoBody start="\v^ *\@" end="$" keepend contains=justOperator,@setshellsyntax
|
|
||||||
|
|
||||||
hi def link justPublicName Constant
|
|
||||||
hi def link justPrivateName Constant
|
|
||||||
hi def link justKeyword Keyword
|
|
||||||
hi def link justSetting Keyword
|
|
||||||
hi def link justOperator Operator
|
|
||||||
hi def link justComment Comment
|
|
||||||
hi def link justShebang PreProc
|
|
||||||
hi def link justFunction Function
|
|
||||||
hi def link justQuote String
|
|
||||||
hi def link justString String
|
|
||||||
hi def link justRawString String
|
|
||||||
hi def link justCurlyBrace Keyword
|
|
||||||
hi def link justBacktick Special
|
|
||||||
|
|
||||||
let b:current_syntax = "just"
|
|
||||||
|
|
||||||
" vim:expandtab:ts=2:sts=2
|
|
Reference in New Issue
Block a user