Compare commits
189 Commits
7275f13738
...
main
Author | SHA1 | Date | |
---|---|---|---|
174c101fb1 | |||
4e7e47dd94 | |||
a5ce30aeeb | |||
11d4baf648 | |||
b1197d5c6a | |||
2be95276a5 | |||
3c8412d858 | |||
ac113056a8 | |||
7241713b3f | |||
0d19b4cb4f | |||
1a0ba35f7a | |||
869f273723 | |||
4c2c6ef429 | |||
e5166ee912 | |||
6ea704fe5b | |||
01348b1436 | |||
2dfa546e78 | |||
224300e1cf | |||
0ce1da0737 | |||
b979a794d6 | |||
d2d54c12a8 | |||
8ac30a239d | |||
d4b5c737e5 | |||
5381d5f0ce | |||
ea9cb1f846 | |||
58e734c60f | |||
df5d4f5a1f | |||
32e70e96ce | |||
8af4f619f4 | |||
52a5ea57e1 | |||
1a894440f9 | |||
353b77271d | |||
8f9842ddfb | |||
94cd12ac82 | |||
726566bb23 | |||
7e07054a70 | |||
af95125894 | |||
7da7228911 | |||
88133117ab | |||
c8ac7ac727 | |||
a135c338ea | |||
0fd7b5873d | |||
e28293e5bd | |||
58002830a8 | |||
8532dd4657 | |||
f6ac67a6a6 | |||
8424a6836a | |||
8e2f5c1ff0 | |||
9183869a50 | |||
1ed7fb7d33 | |||
a498ff949c | |||
ad65630b3f | |||
9109e3a3dc | |||
9d36627e87 | |||
db38eeada9 | |||
cb68a35b1b | |||
01a189c5ee | |||
3580198513 | |||
efb8f453ae | |||
5df7489f27 | |||
efc1c8f029 | |||
43a2e51712 | |||
88e4e319d3 | |||
c274f59f58 | |||
c8a4928ee8 | |||
6d523ad7c1 | |||
d9393152c5 | |||
2f3ce704b2 | |||
54a7a2340c | |||
8cc2e18965 | |||
ffb16efcdc | |||
10ec594031 | |||
cee338520e | |||
e92d117a41 | |||
dc9c3fd54d | |||
a8d82b180a | |||
337a795ad0 | |||
3f369e1e70 | |||
a3819c9b26 | |||
345464d0d4 | |||
5b55118373 | |||
accf0d0c2b | |||
b672131cfc | |||
d03225edce | |||
8434bf7025 | |||
087355502d | |||
0e6448df23 | |||
4da817996d | |||
7e0aa3b86b | |||
aa83db6f7a | |||
9d4f5b661b | |||
b94fb3b31e | |||
dacbddca6e | |||
6e50ec65f8 | |||
ca812add16 | |||
460741740b | |||
7204f08177 | |||
505e04613f | |||
7f4f627769 | |||
dc8045a20e | |||
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 | |||
20531d1154 | |||
cabb0a9eab | |||
93860403c2 | |||
c6d3ef3505 | |||
e34eb0d48f | |||
f574628afc | |||
60da2e20dc | |||
8f891c1e73 | |||
82ed5d328d | |||
49889a15b8 | |||
5568eb4409 | |||
8d0699fb7a | |||
7ae9903d95 | |||
8f3897e097 | |||
05043d778a | |||
ff7d915872 | |||
a9d1c31201 | |||
56321d17b6 | |||
5bfc637a7e | |||
6600e7b295 |
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@ -0,0 +1,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 90
|
||||
|
||||
[{*.c,*.go}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,9 @@
|
||||
*.hex
|
||||
*.log
|
||||
*.out
|
||||
*env
|
||||
.dep
|
||||
**/target/
|
||||
/hello_world/main
|
||||
/aoc*/**/input
|
||||
/arduino/build-*/
|
||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (C) 2020 Dan Buch
|
||||
Copyright (C) 2022 Dan Buch
|
||||
|
||||
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);
|
||||
}
|
19
cc4e/fc.c
Normal file
19
cc4e/fc.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
|
||||
/* print Fahrenheit-Celsius table
|
||||
for f = 0, 20, ..., 300 */
|
||||
|
||||
int main() {
|
||||
int lower, upper, step;
|
||||
float fahr, celsius;
|
||||
lower = 0; /* lower limit of temperature table */
|
||||
upper = 300; /* upper limit */
|
||||
step = 20; /* step size */
|
||||
fahr = lower;
|
||||
|
||||
while (fahr <= upper) {
|
||||
celsius = (5.0/9.0) * (fahr-32.0);
|
||||
printf("%4.0f %6.1f\n", fahr, celsius);
|
||||
fahr = fahr + step;
|
||||
}
|
||||
}
|
12
cc4e/fc_2.c
Normal file
12
cc4e/fc_2.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOWER 0 /* lower limit of table */
|
||||
#define UPPER 300 /* upper limit */
|
||||
#define STEP 20 /* step size */
|
||||
|
||||
int main() { /* Fahrenheit-Celsius table */
|
||||
int fahr;
|
||||
|
||||
for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
|
||||
printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
|
||||
}
|
5
cc4e/hello.c
Normal file
5
cc4e/hello.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
printf("hello, world\n");
|
||||
return 0;
|
||||
}
|
2
djoy/.gitignore
vendored
Normal file
2
djoy/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.venv/
|
||||
*.sqlite3
|
1
djoy/.python-version
Normal file
1
djoy/.python-version
Normal file
@ -0,0 +1 @@
|
||||
3.12.6
|
0
djoy/djoy/__init__.py
Normal file
0
djoy/djoy/__init__.py
Normal file
16
djoy/djoy/asgi.py
Normal file
16
djoy/djoy/asgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for djoy project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
|
||||
|
||||
application = get_asgi_application()
|
125
djoy/djoy/settings.py
Normal file
125
djoy/djoy/settings.py
Normal file
@ -0,0 +1,125 @@
|
||||
"""
|
||||
Django settings for djoy project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.1.1.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = os.environ.get("DJOY_SECRET_KEY", "django-insecure-0xohno")
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"polls.apps.PollsConfig",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "djoy.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "djoy.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = "static/"
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
24
djoy/djoy/urls.py
Normal file
24
djoy/djoy/urls.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""
|
||||
URL configuration for djoy project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path("polls/", include("polls.urls")),
|
||||
path("admin/", admin.site.urls),
|
||||
]
|
16
djoy/djoy/wsgi.py
Normal file
16
djoy/djoy/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for djoy project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
|
||||
|
||||
application = get_wsgi_application()
|
23
djoy/manage.py
Executable file
23
djoy/manage.py
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
0
djoy/polls/__init__.py
Normal file
0
djoy/polls/__init__.py
Normal file
5
djoy/polls/admin.py
Normal file
5
djoy/polls/admin.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Question
|
||||
|
||||
admin.site.register(Question)
|
6
djoy/polls/apps.py
Normal file
6
djoy/polls/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PollsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "polls"
|
51
djoy/polls/migrations/0001_initial.py
Normal file
51
djoy/polls/migrations/0001_initial.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Generated by Django 5.1.1 on 2024-09-16 01:23
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Question",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("question_text", models.CharField(max_length=200)),
|
||||
("pub_date", models.DateTimeField(verbose_name="date published")),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Choice",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("choice_text", models.CharField(max_length=200)),
|
||||
("votes", models.IntegerField(default=0)),
|
||||
(
|
||||
"question",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="polls.question"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
0
djoy/polls/migrations/__init__.py
Normal file
0
djoy/polls/migrations/__init__.py
Normal file
24
djoy/polls/models.py
Normal file
24
djoy/polls/models.py
Normal file
@ -0,0 +1,24 @@
|
||||
import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class Question(models.Model):
|
||||
question_text = models.CharField(max_length=200)
|
||||
pub_date = models.DateTimeField("date published")
|
||||
|
||||
def __str__(self):
|
||||
return self.question_text
|
||||
|
||||
def was_published_recently(self):
|
||||
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
|
||||
|
||||
|
||||
class Choice(models.Model):
|
||||
question = models.ForeignKey(Question, on_delete=models.CASCADE)
|
||||
choice_text = models.CharField(max_length=200)
|
||||
votes = models.IntegerField(default=0)
|
||||
|
||||
def __str__(self):
|
||||
return self.choice_text
|
3
djoy/polls/tests.py
Normal file
3
djoy/polls/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
11
djoy/polls/urls.py
Normal file
11
djoy/polls/urls.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("<int:question_id>/", views.detail, name="detail"),
|
||||
path("<int:question_id>/results/", views.results, name="results"),
|
||||
path("<int:question_id>/vote/", views.vote, name="vote"),
|
||||
]
|
26
djoy/polls/views.py
Normal file
26
djoy/polls/views.py
Normal file
@ -0,0 +1,26 @@
|
||||
from django.http import HttpResponse
|
||||
|
||||
from .models import Question
|
||||
|
||||
|
||||
def index(request) -> HttpResponse:
|
||||
latest_question_list = Question.objects.order_by("-pub_date")[:5]
|
||||
output = ", ".join([q.question_text for q in latest_question_list])
|
||||
return HttpResponse(output.encode())
|
||||
|
||||
|
||||
def detail(request, question_id) -> HttpResponse:
|
||||
_ = request
|
||||
return HttpResponse(f"You're looking at question {question_id}.".encode())
|
||||
|
||||
|
||||
def results(request, question_id) -> HttpResponse:
|
||||
_ = request
|
||||
return HttpResponse(
|
||||
f"You're looking at the results of question {question_id}".encode()
|
||||
)
|
||||
|
||||
|
||||
def vote(request, question_id) -> HttpResponse:
|
||||
_ = request
|
||||
return HttpResponse(f"You're voting on question {question_id}".encode())
|
3
djoy/requirements.txt
Normal file
3
djoy/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Django
|
||||
django-stubs
|
||||
ruff
|
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
|
62
intro-to-algorithms/insertion_sort.go
Normal file
62
intro-to-algorithms/insertion_sort.go
Normal file
@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
inBytes, err := os.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
inLines := strings.Split(strings.TrimSpace(string(inBytes)), "\n")
|
||||
inInts := make([]int64, len(inLines))
|
||||
|
||||
for i, line := range inLines {
|
||||
v, err := strconv.ParseInt(line, 0, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
inInts[i] = v
|
||||
}
|
||||
|
||||
if err := insertionSort(inInts); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, i := range inInts {
|
||||
fmt.Printf("%v\n", i)
|
||||
}
|
||||
}
|
||||
|
||||
func insertionSort(inInts []int64) error {
|
||||
if len(inInts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for j := 1; j < len(inInts); j += 1 {
|
||||
key := inInts[j]
|
||||
fmt.Fprintf(os.Stderr, "%+v (save %v from %v)\n", inInts, key, j)
|
||||
|
||||
i := j - 1
|
||||
|
||||
for ; i > -1 && inInts[i] > key; i -= 1 {
|
||||
inInts[i+1] = inInts[i]
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%+v (j=%v i=%v)\n", inInts, j, i)
|
||||
}
|
||||
|
||||
inInts[i+1] = key
|
||||
fmt.Fprintf(os.Stderr, "%+v (insert %v at %v)\n", inInts, key, i+1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
38
intro-to-algorithms/insertion_sort.py
Normal file
38
intro-to-algorithms/insertion_sort.py
Normal file
@ -0,0 +1,38 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> int:
|
||||
in_ints: list[int] = []
|
||||
with open(sys.argv[1]) as infile:
|
||||
for line in infile:
|
||||
in_ints.append(int(line.strip()))
|
||||
|
||||
insertion_sort(in_ints)
|
||||
|
||||
for i in in_ints:
|
||||
print(i)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def insertion_sort(in_ints: list[int]) -> None:
|
||||
for i in range(len(in_ints) - 1):
|
||||
j = i + 1
|
||||
key = in_ints[j]
|
||||
|
||||
print(f"{in_ints} (save {key} from {j})", file=sys.stderr)
|
||||
|
||||
while i > -1 and in_ints[i] > key:
|
||||
in_ints[i + 1] = in_ints[i]
|
||||
|
||||
print(f"{in_ints} (j={j} i={i})", file=sys.stderr)
|
||||
|
||||
i -= 1
|
||||
|
||||
in_ints[i + 1] = key
|
||||
print(f"{in_ints} (insert {key} at {i+1})", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
10
intro-to-algorithms/ints00
Normal file
10
intro-to-algorithms/ints00
Normal file
@ -0,0 +1,10 @@
|
||||
11
|
||||
2
|
||||
17
|
||||
-1
|
||||
34
|
||||
8
|
||||
999
|
||||
2
|
||||
-56
|
||||
3
|
1
lcthw/.envrc
Normal file
1
lcthw/.envrc
Normal file
@ -0,0 +1 @@
|
||||
export CFLAGS='-Werror -g'
|
3
lcthw/.ex10.argv
Normal file
3
lcthw/.ex10.argv
Normal file
@ -0,0 +1,3 @@
|
||||
carrot
|
||||
angelfood
|
||||
yellow
|
5
lcthw/.ex13.argv
Normal file
5
lcthw/.ex13.argv
Normal file
@ -0,0 +1,5 @@
|
||||
garble
|
||||
geckle
|
||||
gimple
|
||||
gorble
|
||||
gunkle
|
5
lcthw/.ex14.argv
Normal file
5
lcthw/.ex14.argv
Normal file
@ -0,0 +1,5 @@
|
||||
eggs
|
||||
legs
|
||||
pegs
|
||||
jeggings
|
||||
banana pants
|
2
lcthw/.ex17.argv
Normal file
2
lcthw/.ex17.argv
Normal file
@ -0,0 +1,2 @@
|
||||
ex17.db
|
||||
l
|
2
lcthw/.ex17ec.argv
Normal file
2
lcthw/.ex17ec.argv
Normal file
@ -0,0 +1,2 @@
|
||||
ex17ec.db
|
||||
l
|
1
lcthw/.ex17play.argv
Normal file
1
lcthw/.ex17play.argv
Normal file
@ -0,0 +1 @@
|
||||
ex17play.bin
|
1
lcthw/.gdbinit
Normal file
1
lcthw/.gdbinit
Normal file
@ -0,0 +1 @@
|
||||
set debuginfod enabled off
|
2
lcthw/.gitignore
vendored
Normal file
2
lcthw/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
ex*
|
||||
!ex*.c
|
35
lcthw/Makefile
Normal file
35
lcthw/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
CFLAGS ?= -Wall -g
|
||||
GDBRUN = gdb --batch --ex run --ex bt --ex q --args
|
||||
BUILD_TARGETS = $(foreach ex,$(wildcard ex*.c),$(subst .c,,$(ex)))
|
||||
|
||||
.PHONY: all
|
||||
all: build test
|
||||
|
||||
.PHONY: echo
|
||||
echo:
|
||||
@echo BUILD_TARGETS=$(BUILD_TARGETS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(BUILD_TARGETS)
|
||||
|
||||
.PHONY: build
|
||||
build: $(BUILD_TARGETS)
|
||||
|
||||
.PHONY: gtest
|
||||
gtest: $(BUILD_TARGETS)
|
||||
@$(foreach bt, $(BUILD_TARGETS), make .gtest.$(bt) &&) printf '\ngYAY\n'
|
||||
|
||||
.gtest.%: %
|
||||
@if test -f .$*.argv; then readarray -t test_argv <.$*.argv; fi && \
|
||||
printf '\n==> %s\n' "$*" && $(GDBRUN) ./$* "$${test_argv[@]}"
|
||||
|
||||
.PHONY: test
|
||||
test: $(BUILD_TARGETS)
|
||||
@$(foreach bt, $(BUILD_TARGETS), make .test.$(bt) &&) printf '\nYAY\n'
|
||||
|
||||
.test.%: %
|
||||
@if test -f .$*.argv; then readarray -t test_argv <.$*.argv; fi && \
|
||||
printf '\n==> %s\n' "$*" && ./$* "$${test_argv[@]}"
|
12
lcthw/ex1.c
Normal file
12
lcthw/ex1.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
/* This is a comment. */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int distance = 100;
|
||||
|
||||
// this is also a comment
|
||||
printf("You are %d miles away.\n", distance);
|
||||
|
||||
return 0;
|
||||
}
|
62
lcthw/ex10.c
Normal file
62
lcthw/ex10.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("ERROR: You need at least one argument.\n");
|
||||
// this is how you abort a program
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int arg = 1;
|
||||
for (arg = 1; arg < argc; arg++) {
|
||||
for (i = 0; argv[arg][i] != '\0'; i++) {
|
||||
printf("(%d) ", arg);
|
||||
|
||||
char letter = argv[arg][i];
|
||||
|
||||
// lowercase letters
|
||||
if (letter >= 'A' && letter <= 'Z') {
|
||||
letter += ('a' - 'A');
|
||||
}
|
||||
|
||||
switch (letter) {
|
||||
case 'a':
|
||||
printf("%d: 'a'\n", i);
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
printf("%d: 'e'\n", i);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
printf("%d: 'i'\n", i);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
printf("%d: 'o'\n", i);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
printf("%d: 'u'\n", i);
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
// why i > 2? is this a bug?
|
||||
if (i > 2) {
|
||||
// it's only sometimes Y
|
||||
printf("%d: 'y'\n", i);
|
||||
} else {
|
||||
printf("%d: %c is not a vowel\n", i, letter);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("%d: %c is not a vowel\n", i, letter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
48
lcthw/ex11.c
Normal file
48
lcthw/ex11.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int numbers[4] = { 0 };
|
||||
char name[4] = { 'a' };
|
||||
|
||||
// first, print them out raw
|
||||
printf("numbers: %d %d %d %d\n",
|
||||
numbers[0], numbers[1], numbers[2], numbers[3]);
|
||||
|
||||
printf("name each: %c %c %c %c\n",
|
||||
name[0], name[1], name[2], name[3]);
|
||||
|
||||
printf("name: %s\n", name);
|
||||
|
||||
// setup the numbers
|
||||
numbers[0] = 1;
|
||||
numbers[1] = 2;
|
||||
numbers[2] = 3;
|
||||
numbers[3] = 4;
|
||||
|
||||
// setup the name
|
||||
name[0] = 'Z';
|
||||
name[1] = 'e';
|
||||
name[2] = 'd';
|
||||
name[3] = '\0';
|
||||
|
||||
// then print them out initialized
|
||||
printf("numbers: %d %d %d %d\n",
|
||||
numbers[0], numbers[1], numbers[2], numbers[3]);
|
||||
|
||||
printf("name each: %c %c %c %c\n",
|
||||
name[0], name[1], name[2], name[3]);
|
||||
|
||||
// print the name like a string
|
||||
printf("name: %s\n", name);
|
||||
|
||||
// another way to use name
|
||||
char *another = "Zed";
|
||||
|
||||
printf("another: %s\n", another);
|
||||
|
||||
printf("another each: %c %c %c %c\n",
|
||||
another[0], another[1], another[2], another[3]);
|
||||
|
||||
return 0;
|
||||
}
|
32
lcthw/ex12.c
Normal file
32
lcthw/ex12.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int areas[] = { 10, 12, 13, 14, 20 };
|
||||
char name[] = "Zed";
|
||||
char full_name[] = {
|
||||
'Z', 'e', 'd',
|
||||
' ', 'A', '.', ' ',
|
||||
'S', 'h', 'a', 'w', '\0',
|
||||
};
|
||||
|
||||
// WARNING: On some systems you may have to change the
|
||||
// %ld in this code to a %u since it will use unsigned ints
|
||||
printf("The size of an int: %ld\n", sizeof(int));
|
||||
printf("The size of areas (int[]): %ld\n", sizeof(areas));
|
||||
printf("The number of ints in areas: %ld\n",
|
||||
sizeof(areas) / sizeof(int));
|
||||
printf("The first area is %d, the 2nd %d.\n", areas[0], areas[1]);
|
||||
|
||||
printf("The size of a char: %ld\n", sizeof(char));
|
||||
printf("The size of name (char[]): %ld\n", sizeof(name));
|
||||
printf("The number of chars: %ld\n", sizeof(name) / sizeof(char));
|
||||
|
||||
printf("The size of full_name (char[]): %ld\n", sizeof(full_name));
|
||||
printf("The number of chars: %ld\n",
|
||||
sizeof(full_name) / sizeof(char));
|
||||
|
||||
printf("name=\"%s\" and full_name=\"%s\"\n", name, full_name);
|
||||
|
||||
return 0;
|
||||
}
|
26
lcthw/ex13.c
Normal file
26
lcthw/ex13.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i = 0;
|
||||
// go through each string in argv
|
||||
// why am I skipping argv[0]?
|
||||
for (i = 1; i < argc; i++) {
|
||||
printf("arg %d: %s\n", i, argv[i]);
|
||||
}
|
||||
|
||||
// let's make our own array of strings
|
||||
char *states[] = {
|
||||
"California",
|
||||
"Oregon",
|
||||
"Washington",
|
||||
"Texas",
|
||||
NULL,
|
||||
};
|
||||
|
||||
for (i = 0; states[i] != NULL; i++) {
|
||||
printf("state %d: %s\n", i, states[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
36
lcthw/ex14.c
Normal file
36
lcthw/ex14.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
// forward declarations
|
||||
void print_letters(size_t len, char arg[]);
|
||||
|
||||
void print_arguments(int argc, char *argv[])
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
print_letters(strlen(argv[i]), argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void print_letters(size_t len, char arg[])
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
char ch = arg[i];
|
||||
|
||||
if (isprint((int)ch)) {
|
||||
printf("'%c' == %d ", ch, ch);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
print_arguments(argc, argv);
|
||||
return 0;
|
||||
}
|
56
lcthw/ex15.c
Normal file
56
lcthw/ex15.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// create two arrays we care about
|
||||
int ages[] = { 23, 43, 12, 89, 2 };
|
||||
char *names[] = {
|
||||
"Alan", "Frank",
|
||||
"Mary", "John", "Lisa"
|
||||
};
|
||||
|
||||
// safely get the size of ages
|
||||
int count = sizeof(ages) / sizeof(int);
|
||||
int i = 0;
|
||||
|
||||
char **first_name = &names[0];
|
||||
char **last_name = &names[count - 1];
|
||||
|
||||
printf("first_name=%p last_name=%p\n", first_name, last_name);
|
||||
printf("*first_name=%s *last_name=%s\n", *first_name, *last_name);
|
||||
|
||||
printf("---\n");
|
||||
|
||||
// first way using indexing
|
||||
for (i = count - 1; i > -1; i--) {
|
||||
printf("%s has %d years alive.\n", names[i], ages[i]);
|
||||
}
|
||||
|
||||
printf("---\n");
|
||||
|
||||
// setup the pointers to the start of the arrays
|
||||
int *cur_age = ages;
|
||||
char **cur_name = names;
|
||||
|
||||
// second way using pointers
|
||||
for (i = count - 1; i > -1; i--) {
|
||||
printf("%s is %d years old.\n", *(cur_name + i), *(cur_age + i));
|
||||
}
|
||||
|
||||
printf("---\n");
|
||||
|
||||
// third way, pointers are just arrays
|
||||
for (i = count - 1; i > -1; i--) {
|
||||
printf("%s is %d years old again.\n", cur_name[i], cur_age[i]);
|
||||
}
|
||||
|
||||
printf("---\n");
|
||||
|
||||
// fourth way with pointers in a stupid complex way
|
||||
for (cur_name = names, cur_age = ages;
|
||||
(cur_age - ages) < count; cur_name++, cur_age++) {
|
||||
printf("%s lived %d years so far.\n", *cur_name, *cur_age);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
73
lcthw/ex16.c
Normal file
73
lcthw/ex16.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Person {
|
||||
char *name;
|
||||
int age;
|
||||
int height;
|
||||
int weight;
|
||||
};
|
||||
|
||||
struct Person *Person_create(char *name, int age, int height, int weight)
|
||||
{
|
||||
struct Person *who = malloc(sizeof(struct Person));
|
||||
assert(who != NULL);
|
||||
|
||||
who->name = strdup(name);
|
||||
who->age = age;
|
||||
who->height = height;
|
||||
who->weight = weight;
|
||||
|
||||
return who;
|
||||
}
|
||||
|
||||
void Person_destroy(struct Person *who)
|
||||
{
|
||||
assert(who != NULL);
|
||||
|
||||
free(who->name);
|
||||
free(who);
|
||||
}
|
||||
|
||||
void Person_print(struct Person *who)
|
||||
{
|
||||
assert(who != NULL);
|
||||
|
||||
printf("Name: %s\n", who->name);
|
||||
printf("\tAge: %d\n", who->age);
|
||||
printf("\tHeight: %d\n", who->height);
|
||||
printf("\tWeight: %d\n", who->weight);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// make two people structures
|
||||
struct Person *joe = Person_create("Joe Alex", 32, 64, 100);
|
||||
|
||||
struct Person *frank = Person_create("Frank Blank", 20, 72, 180);
|
||||
|
||||
// print them out and where they are in memory
|
||||
printf("Joe is at memory location %p:\n", joe);
|
||||
Person_print(joe);
|
||||
|
||||
printf("Frank is at memory location %p:\n", frank);
|
||||
Person_print(frank);
|
||||
|
||||
// make everyone age 20 years and print them again
|
||||
joe->age += 20;
|
||||
joe->height -= 2;
|
||||
joe->weight += 40;
|
||||
Person_print(joe);
|
||||
|
||||
frank->age += 20;
|
||||
frank->weight += 20;
|
||||
Person_print(frank);
|
||||
|
||||
// destroy them both so we clean up
|
||||
Person_destroy(joe);
|
||||
Person_destroy(frank);
|
||||
|
||||
return 0;
|
||||
}
|
57
lcthw/ex16ec.c
Normal file
57
lcthw/ex16ec.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Person {
|
||||
char *name;
|
||||
int age;
|
||||
int height;
|
||||
int weight;
|
||||
};
|
||||
|
||||
struct Person Person_create(char *name, int age, int height, int weight)
|
||||
{
|
||||
struct Person who;
|
||||
|
||||
who.name = strdup(name);
|
||||
who.age = age;
|
||||
who.height = height;
|
||||
who.weight = weight;
|
||||
|
||||
return who;
|
||||
}
|
||||
|
||||
void Person_print(struct Person who)
|
||||
{
|
||||
printf("Name: %s\n", who.name);
|
||||
printf("\tAge: %d\n", who.age);
|
||||
printf("\tHeight: %d\n", who.height);
|
||||
printf("\tWeight: %d\n", who.weight);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// make two people structures
|
||||
struct Person joe = Person_create("Joe Alex", 32, 64, 100);
|
||||
|
||||
struct Person frank = Person_create("Frank Blank", 20, 72, 180);
|
||||
|
||||
// print them out and where they are in memory
|
||||
printf("Joe is at memory location %p:\n", &joe);
|
||||
Person_print(joe);
|
||||
|
||||
printf("Frank is at memory location %p:\n", &frank);
|
||||
Person_print(frank);
|
||||
|
||||
// make everyone age 20 years and print them again
|
||||
joe.age += 20;
|
||||
joe.height -= 2;
|
||||
joe.weight += 40;
|
||||
Person_print(joe);
|
||||
|
||||
frank.age += 20;
|
||||
frank.weight += 20;
|
||||
Person_print(frank);
|
||||
|
||||
return 0;
|
||||
}
|
237
lcthw/ex17.c
Normal file
237
lcthw/ex17.c
Normal file
@ -0,0 +1,237 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_DATA 512
|
||||
#define MAX_ROWS 100
|
||||
|
||||
struct Address {
|
||||
int id;
|
||||
int set;
|
||||
char name[MAX_DATA];
|
||||
char email[MAX_DATA];
|
||||
};
|
||||
|
||||
struct Database {
|
||||
struct Address rows[MAX_ROWS];
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
FILE *file;
|
||||
struct Database *db;
|
||||
};
|
||||
|
||||
void die(const char *message)
|
||||
{
|
||||
if (errno) {
|
||||
perror(message);
|
||||
} else {
|
||||
printf("ERROR: %s\n", message);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void Address_print(struct Address *addr)
|
||||
{
|
||||
printf("%d %s %s\n", addr->id, addr->name, addr->email);
|
||||
}
|
||||
|
||||
void Database_load(struct Connection *conn)
|
||||
{
|
||||
int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
|
||||
if (rc != 1) {
|
||||
die("Failed to load database.");
|
||||
}
|
||||
}
|
||||
|
||||
struct Connection *Database_open(const char *filename, char mode)
|
||||
{
|
||||
struct Connection *conn = malloc(sizeof(struct Connection));
|
||||
|
||||
if (!conn) {
|
||||
die("Memory error");
|
||||
}
|
||||
|
||||
conn->db = malloc(sizeof(struct Database));
|
||||
if (!conn->db) {
|
||||
die("Memory error");
|
||||
}
|
||||
|
||||
if (mode == 'c') {
|
||||
conn->file = fopen(filename, "w");
|
||||
} else {
|
||||
conn->file = fopen(filename, "r+");
|
||||
|
||||
if (conn->file) {
|
||||
Database_load(conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!conn->file) {
|
||||
die("Failed to open file");
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void Database_close(struct Connection *conn)
|
||||
{
|
||||
if (conn) {
|
||||
if (conn->file) {
|
||||
fclose(conn->file);
|
||||
}
|
||||
|
||||
if (conn->db) {
|
||||
free(conn->db);
|
||||
}
|
||||
|
||||
free(conn);
|
||||
}
|
||||
}
|
||||
|
||||
void Database_write(struct Connection *conn)
|
||||
{
|
||||
rewind(conn->file);
|
||||
|
||||
int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
|
||||
if (rc != 1) {
|
||||
die("Failed to write database.");
|
||||
}
|
||||
|
||||
rc = fflush(conn->file);
|
||||
if (rc == -1) {
|
||||
die("Cannot flush database");
|
||||
}
|
||||
}
|
||||
|
||||
void Database_create(struct Connection *conn)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < MAX_ROWS; i++) {
|
||||
struct Address addr = {.id = i, .set = 0};
|
||||
conn->db->rows[i] = addr;
|
||||
}
|
||||
}
|
||||
|
||||
void Database_set(struct Connection *conn, int id, const char *name, const char *email)
|
||||
{
|
||||
struct Address *addr = &conn->db->rows[id];
|
||||
if (addr->set) {
|
||||
die("Already set, delete it first");
|
||||
}
|
||||
|
||||
addr->set = 1;
|
||||
|
||||
char *res = strncpy(addr->name, name, MAX_DATA);
|
||||
if (!res) {
|
||||
die("Name copy failed");
|
||||
}
|
||||
|
||||
addr->name[MAX_DATA - 1] = '\0';
|
||||
|
||||
res = strncpy(addr->email, email, MAX_DATA);
|
||||
if (!res) {
|
||||
die("Email copy failed");
|
||||
}
|
||||
|
||||
addr->email[MAX_DATA - 1] = '\0';
|
||||
}
|
||||
|
||||
void Database_get(struct Connection *conn, int id)
|
||||
{
|
||||
struct Address *addr = &conn->db->rows[id];
|
||||
|
||||
if (addr->set) {
|
||||
Address_print(addr);
|
||||
} else {
|
||||
die("ID is not set");
|
||||
}
|
||||
}
|
||||
|
||||
void Database_delete(struct Connection *conn, int id)
|
||||
{
|
||||
struct Address addr = {.id = id, .set = 0};
|
||||
conn->db->rows[id] = addr;
|
||||
}
|
||||
|
||||
void Database_list(struct Connection *conn)
|
||||
{
|
||||
int i = 0;
|
||||
struct Database *db = conn->db;
|
||||
|
||||
for (i = 0; i < MAX_ROWS; i++) {
|
||||
struct Address *cur = &db->rows[i];
|
||||
|
||||
if (cur->set) {
|
||||
Address_print(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3) {
|
||||
die("USAGE: ex17 <dbfile> <action> [action params]");
|
||||
}
|
||||
|
||||
char *filename = argv[1];
|
||||
char action = argv[2][0];
|
||||
struct Connection *conn = Database_open(filename, action);
|
||||
int id = 0;
|
||||
|
||||
if (argc > 3) {
|
||||
id = atoi(argv[3]);
|
||||
}
|
||||
|
||||
if (id >= MAX_ROWS) {
|
||||
die("There's not that many records.");
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case 'c':
|
||||
Database_create(conn);
|
||||
Database_write(conn);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
if (argc != 4) {
|
||||
die("Need an id to get");
|
||||
}
|
||||
|
||||
Database_get(conn, id);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (argc != 6) {
|
||||
die("Need an id, name, email to set");
|
||||
}
|
||||
|
||||
Database_set(conn, id, argv[4], argv[5]);
|
||||
Database_write(conn);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
if (argc != 4) {
|
||||
die("Need an id to delete");
|
||||
}
|
||||
|
||||
Database_delete(conn, id);
|
||||
Database_write(conn);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
Database_list(conn);
|
||||
break;
|
||||
|
||||
default:
|
||||
die("Invalid action: c=create, g=get, s=set, d=del, l=list");
|
||||
}
|
||||
|
||||
Database_close(conn);
|
||||
|
||||
return 0;
|
||||
}
|
305
lcthw/ex17ec.c
Normal file
305
lcthw/ex17ec.c
Normal file
@ -0,0 +1,305 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEFAULT_MAX_DATA 512
|
||||
#define DEFAULT_MAX_ROWS 100
|
||||
|
||||
struct Address {
|
||||
char *name;
|
||||
char *email;
|
||||
};
|
||||
|
||||
struct TableOfContents {
|
||||
size_t n_rows;
|
||||
size_t string_field_size;
|
||||
};
|
||||
|
||||
struct Database {
|
||||
struct Address *rows;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
FILE *file;
|
||||
struct TableOfContents *toc;
|
||||
struct Database *db;
|
||||
};
|
||||
|
||||
void Database_close(struct Connection *conn);
|
||||
struct Address *Database_get(struct Connection *conn, const char *email);
|
||||
|
||||
void die(struct Connection *conn, const char *message)
|
||||
{
|
||||
Database_close(conn);
|
||||
|
||||
if (errno) {
|
||||
perror(message);
|
||||
} else {
|
||||
printf("ERROR: %s\n", message);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void Address_print(struct Address *addr)
|
||||
{
|
||||
if (!addr) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s %s\n", addr->name, addr->email);
|
||||
}
|
||||
|
||||
size_t Database_addr_size(struct Connection *conn)
|
||||
{
|
||||
return sizeof(struct Address) +
|
||||
/* name */ conn->toc->string_field_size +
|
||||
/* email */ conn->toc->string_field_size;
|
||||
}
|
||||
|
||||
size_t Database_size(struct Connection *conn)
|
||||
{
|
||||
return sizeof(struct Database) +
|
||||
(conn->toc->n_rows * Database_addr_size(conn));
|
||||
}
|
||||
|
||||
size_t Database_n_rows(struct Connection *conn)
|
||||
{
|
||||
return sizeof(conn->db->rows) / Database_addr_size(conn);
|
||||
}
|
||||
|
||||
void Database_load(struct Connection *conn)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = fread(conn->toc, sizeof(struct TableOfContents), 1, conn->file);
|
||||
if (rc != 1) {
|
||||
die(conn, "Failed to load TableOfContents.");
|
||||
}
|
||||
|
||||
rc = fread(conn->db, Database_size(conn), 1, conn->file);
|
||||
if (rc != 1) {
|
||||
die(conn, "Failed to load Database.");
|
||||
}
|
||||
}
|
||||
|
||||
struct Connection *Database_open(const char *filename, char mode, size_t string_field_size)
|
||||
{
|
||||
struct Connection *conn = malloc(sizeof(struct Connection));
|
||||
|
||||
if (!conn) {
|
||||
die(conn, "Memory error allocating Connection");
|
||||
}
|
||||
|
||||
conn->toc = malloc(sizeof(struct TableOfContents));
|
||||
if (!conn->toc) {
|
||||
die(conn, "Memory error allocating TableOfContents");
|
||||
}
|
||||
|
||||
conn->toc->string_field_size = string_field_size;
|
||||
|
||||
conn->db = malloc(sizeof(struct Database));
|
||||
if (!conn->db) {
|
||||
die(conn, "Memory error allocating Database");
|
||||
}
|
||||
|
||||
if (mode == 'c') {
|
||||
conn->file = fopen(filename, "w");
|
||||
|
||||
conn->toc->n_rows = 1;
|
||||
|
||||
conn->db->rows = malloc(Database_addr_size(conn));
|
||||
if (!conn->db->rows) {
|
||||
die(conn, "Memory error allocating Database rows");
|
||||
}
|
||||
|
||||
} else {
|
||||
conn->file = fopen(filename, "r+");
|
||||
|
||||
if (conn->file) {
|
||||
Database_load(conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!conn->file) {
|
||||
die(conn, "Failed to open file");
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void Database_close(struct Connection *conn)
|
||||
{
|
||||
if (conn) {
|
||||
if (conn->file) {
|
||||
fclose(conn->file);
|
||||
}
|
||||
|
||||
if (conn->toc) {
|
||||
free(conn->toc);
|
||||
}
|
||||
|
||||
if (conn->db) {
|
||||
free(conn->db);
|
||||
}
|
||||
|
||||
free(conn);
|
||||
}
|
||||
}
|
||||
|
||||
void Database_write(struct Connection *conn)
|
||||
{
|
||||
rewind(conn->file);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
rc = fwrite(conn->toc, sizeof(struct TableOfContents), 1, conn->file);
|
||||
if (rc != 1) {
|
||||
die(conn, "Failed to write TableOfContents.");
|
||||
}
|
||||
|
||||
rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
|
||||
if (rc != 1) {
|
||||
die(conn, "Failed to write Database.");
|
||||
}
|
||||
|
||||
rc = fflush(conn->file);
|
||||
if (rc == -1) {
|
||||
die(conn, "Cannot flush");
|
||||
}
|
||||
}
|
||||
|
||||
void Database_set(struct Connection *conn, const char *name, const char *email)
|
||||
{
|
||||
struct Address *addr = Database_get(conn, email);
|
||||
if (!addr) {
|
||||
addr = malloc(Database_addr_size(conn));
|
||||
conn->db->rows[Database_n_rows(conn)+1] = *addr;
|
||||
}
|
||||
|
||||
char *res = strncpy(addr->name, name, conn->toc->string_field_size);
|
||||
if (!res) {
|
||||
die(conn, "Name copy failed");
|
||||
}
|
||||
|
||||
addr->name[conn->toc->string_field_size - 1] = '\0';
|
||||
|
||||
res = strncpy(addr->email, email, conn->toc->string_field_size);
|
||||
if (!res) {
|
||||
die(conn, "Email copy failed");
|
||||
}
|
||||
|
||||
addr->email[conn->toc->string_field_size - 1] = '\0';
|
||||
}
|
||||
|
||||
struct Address *Database_get(struct Connection *conn, const char *email)
|
||||
{
|
||||
int i = 0;
|
||||
struct Address *addr;
|
||||
|
||||
for (i = 0; i < Database_n_rows(conn); i++) {
|
||||
addr = &conn->db->rows[i];
|
||||
|
||||
if (strcmp(addr->email, email) == 0) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Database_delete(struct Connection *conn, const char *email)
|
||||
{
|
||||
struct Address *addr = Database_get(conn, email);
|
||||
if (!addr) {
|
||||
// already deleted
|
||||
return;
|
||||
}
|
||||
|
||||
addr->email = NULL;
|
||||
addr->name = NULL;
|
||||
}
|
||||
|
||||
void Database_list(struct Connection *conn)
|
||||
{
|
||||
int i = 0;
|
||||
struct Database *db = conn->db;
|
||||
|
||||
for (i = 0; i < Database_n_rows(conn); i++) {
|
||||
struct Address *cur = &db->rows[i];
|
||||
|
||||
Address_print(cur);
|
||||
}
|
||||
}
|
||||
|
||||
size_t get_string_field_size()
|
||||
{
|
||||
size_t string_field_size = DEFAULT_MAX_DATA;
|
||||
|
||||
const char *string_field_size_string = getenv("EX17_MAX_DATA");
|
||||
if (string_field_size_string) {
|
||||
int string_field_size_int = atoi(string_field_size_string);
|
||||
string_field_size = string_field_size_int;
|
||||
}
|
||||
|
||||
return string_field_size;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3) {
|
||||
die(NULL, "USAGE: ex17 <dbfile> <action> [action params]");
|
||||
}
|
||||
|
||||
char *filename = argv[1];
|
||||
char action = argv[2][0];
|
||||
|
||||
size_t string_field_size = get_string_field_size();
|
||||
|
||||
struct Connection *conn = Database_open(filename, action, string_field_size);
|
||||
|
||||
switch (action) {
|
||||
case 'c':
|
||||
Database_write(conn);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
if (argc != 4) {
|
||||
die(conn, "Need an `email` to get");
|
||||
}
|
||||
|
||||
Address_print(Database_get(conn, argv[3]));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (argc != 5) {
|
||||
die(conn, "Need a `name` and `email` to set");
|
||||
}
|
||||
|
||||
Database_set(conn, argv[3], argv[4]);
|
||||
Database_write(conn);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
if (argc != 4) {
|
||||
die(conn, "Need an `email` to delete");
|
||||
}
|
||||
|
||||
Database_delete(conn, argv[3]);
|
||||
Database_write(conn);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
Database_list(conn);
|
||||
break;
|
||||
|
||||
default:
|
||||
die(conn, "Invalid action: c=create, g=get, s=set, d=del, l=list");
|
||||
}
|
||||
|
||||
Database_close(conn);
|
||||
|
||||
return 0;
|
||||
}
|
109
lcthw/ex17play.c
Normal file
109
lcthw/ex17play.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define NAME_SIZE 64
|
||||
|
||||
struct State {
|
||||
int counter;
|
||||
time_t updated_at;
|
||||
char name[NAME_SIZE];
|
||||
size_t history_entries;
|
||||
};
|
||||
|
||||
static char *names[] = {
|
||||
"boingo",
|
||||
"barko",
|
||||
"flip",
|
||||
"dingles",
|
||||
"zebra",
|
||||
"horus",
|
||||
"blendy",
|
||||
"kitems",
|
||||
"horple",
|
||||
NULL,
|
||||
};
|
||||
|
||||
char *random_name()
|
||||
{
|
||||
int i = 0;
|
||||
int mod = 0;
|
||||
for (i = 0; names[i] != NULL; i++) {
|
||||
mod = i;
|
||||
}
|
||||
|
||||
return names[random() % mod];
|
||||
}
|
||||
|
||||
void tick(FILE *fp, int init)
|
||||
{
|
||||
struct State *state = malloc(sizeof(struct State));
|
||||
if (!state) {
|
||||
err(EXIT_FAILURE, "Memory error making State");
|
||||
}
|
||||
|
||||
if (init == 0) {
|
||||
size_t read = fread(state, sizeof(struct State), 1, fp);
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"Read %d \"%s\" (%lu byte(s)) from %lu\n",
|
||||
state->counter, state->name, sizeof(struct State) * read, state->updated_at
|
||||
);
|
||||
}
|
||||
|
||||
time_t history[state->history_entries];
|
||||
size_t histories_read = fread(&history, sizeof(time_t), state->history_entries, fp);
|
||||
|
||||
state->counter++;
|
||||
state->updated_at = time(0);
|
||||
strncpy(state->name, random_name(), NAME_SIZE);
|
||||
state->history_entries++;
|
||||
|
||||
rewind(fp);
|
||||
|
||||
size_t wrote = fwrite(state, sizeof(struct State), 1, fp);
|
||||
fwrite(history, sizeof(time_t), sizeof(history) / sizeof(time_t), fp);
|
||||
fwrite(&state->updated_at, sizeof(time_t), 1, fp);
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"Wrote %d \"%s\" (%lu byte(s)) at %lu (history length=%lu)\n",
|
||||
state->counter, state->name, sizeof(struct State) * wrote, state->updated_at,
|
||||
state->history_entries
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
errx(EXIT_FAILURE, "Missing filename argument");
|
||||
}
|
||||
|
||||
int random_seed = time(0);
|
||||
const char *random_seed_str = getenv("RANDOM_SEED");
|
||||
if (random_seed_str) {
|
||||
random_seed = atoi(random_seed_str);
|
||||
}
|
||||
|
||||
srandom(random_seed);
|
||||
|
||||
int init = 0;
|
||||
FILE *fp = fopen(argv[1], "r+");
|
||||
|
||||
if (!fp) {
|
||||
fp = fopen(argv[1], "w+");
|
||||
init = 1;
|
||||
}
|
||||
|
||||
if (!fp) {
|
||||
err(EXIT_FAILURE, "Failed to open file");
|
||||
}
|
||||
|
||||
tick(fp, init);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
12
lcthw/ex3.c
Normal file
12
lcthw/ex3.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int age = 10;
|
||||
int height = 72;
|
||||
|
||||
printf("I am %d years old.\n", age);
|
||||
printf("I am %d inches tall.\n", height);
|
||||
|
||||
return 0;
|
||||
}
|
43
lcthw/ex7.c
Normal file
43
lcthw/ex7.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int distance = 100;
|
||||
float power = 2.345f;
|
||||
double super_power = 56789.4532;
|
||||
char initial = 'A';
|
||||
char first_name[] = "Zed";
|
||||
char last_name[] = "Shaw";
|
||||
|
||||
printf("You are %d miles away.\n", distance);
|
||||
printf("You have %f levels of power.\n", power);
|
||||
printf("You have %f awesome super powers.\n", super_power);
|
||||
printf("I have an initial %c.\n", initial);
|
||||
printf("I have a first name %s.\n", first_name);
|
||||
printf("I have a last name %s.\n", last_name);
|
||||
printf("My whole name is %s %c. %s.\n",
|
||||
first_name, initial, last_name);
|
||||
|
||||
int bugs = 100;
|
||||
double bug_rate = 1.2;
|
||||
|
||||
printf("You have %d bugs at the imaginary rate of %f.\n",
|
||||
bugs, bug_rate);
|
||||
|
||||
long universe_of_defects = 1L * 1024L * 1024L * 1024L;
|
||||
printf("The entire universe has %ld bugs.\n", universe_of_defects);
|
||||
|
||||
double expected_bugs = bugs * bug_rate;
|
||||
printf("You are expected to have %f bugs.\n", expected_bugs);
|
||||
|
||||
double part_of_universe = expected_bugs / universe_of_defects;
|
||||
printf("That is only a %e portion of the universe.\n",
|
||||
part_of_universe);
|
||||
|
||||
// this makes no sense, just a demo of something weird
|
||||
char nul_byte = '\0';
|
||||
int care_percentage = bugs * nul_byte;
|
||||
printf("Which means you should care %d%%.\n", care_percentage);
|
||||
|
||||
return 0;
|
||||
}
|
21
lcthw/ex8.c
Normal file
21
lcthw/ex8.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (argc == 1) {
|
||||
printf("You have no argument.\n");
|
||||
} else if (argc > 1 && argc < 4) {
|
||||
printf("Here's your arguments:\n");
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
printf("%s ", argv[i]);
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("You have too many arguments.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
15
lcthw/ex9.c
Normal file
15
lcthw/ex9.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i = 25;
|
||||
while (i > 0) {
|
||||
printf("%d", i);
|
||||
i--;
|
||||
}
|
||||
|
||||
// need this to add a final newline
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
1
leetcode/.gitignore
vendored
Normal file
1
leetcode/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.envrc
|
1
leetcode/.python-version
Normal file
1
leetcode/.python-version
Normal file
@ -0,0 +1 @@
|
||||
3.12.0
|
1
leetcode/.testdata/max_sub_array0.json
Normal file
1
leetcode/.testdata/max_sub_array0.json
Normal file
File diff suppressed because one or more lines are too long
147
leetcode/pyproject.toml
Normal file
147
leetcode/pyproject.toml
Normal file
@ -0,0 +1,147 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "leetcode"
|
||||
description = 'What A Mess'
|
||||
readme = "README.md"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.7"
|
||||
license = "MIT"
|
||||
keywords = []
|
||||
authors = [
|
||||
{ name = "Dan Buch", email = "dan@meatballhat.com" },
|
||||
]
|
||||
classifiers = []
|
||||
dependencies = [
|
||||
"ipython",
|
||||
"ipdb",
|
||||
"matplotlib",
|
||||
"numpy"
|
||||
]
|
||||
|
||||
[tool.hatch.envs.default]
|
||||
dependencies = [
|
||||
"coverage[toml]>=6.5",
|
||||
"pytest",
|
||||
]
|
||||
[tool.hatch.envs.default.scripts]
|
||||
test = "pytest {args:tests}"
|
||||
test-cov = "coverage run -m pytest {args:tests}"
|
||||
cov-report = [
|
||||
"- coverage combine",
|
||||
"coverage report",
|
||||
]
|
||||
cov = [
|
||||
"test-cov",
|
||||
"cov-report",
|
||||
]
|
||||
|
||||
[[tool.hatch.envs.all.matrix]]
|
||||
python = ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
|
||||
[tool.hatch.envs.lint]
|
||||
detached = true
|
||||
dependencies = [
|
||||
"black>=23.1.0",
|
||||
"mypy>=1.0.0",
|
||||
"ruff>=0.0.243",
|
||||
]
|
||||
[tool.hatch.envs.lint.scripts]
|
||||
typing = "mypy --install-types --non-interactive {args:.}"
|
||||
style = [
|
||||
"ruff {args:.}",
|
||||
"black --check --diff {args:.}",
|
||||
]
|
||||
fmt = [
|
||||
"black {args:.}",
|
||||
"ruff --fix {args:.}",
|
||||
"style",
|
||||
]
|
||||
all = [
|
||||
"style",
|
||||
"typing",
|
||||
]
|
||||
|
||||
[tool.black]
|
||||
target-version = ["py37"]
|
||||
line-length = 90
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py37"
|
||||
line-length = 90
|
||||
select = [
|
||||
"A",
|
||||
"ARG",
|
||||
"B",
|
||||
"C",
|
||||
"DTZ",
|
||||
"E",
|
||||
"EM",
|
||||
"F",
|
||||
"FBT",
|
||||
"I",
|
||||
"ICN",
|
||||
"ISC",
|
||||
"N",
|
||||
"PLC",
|
||||
"PLE",
|
||||
"PLR",
|
||||
"PLW",
|
||||
"Q",
|
||||
"RUF",
|
||||
"S",
|
||||
"T",
|
||||
"TID",
|
||||
"UP",
|
||||
"W",
|
||||
"YTT",
|
||||
]
|
||||
ignore = [
|
||||
# Allow non-abstract empty methods in abstract base classes
|
||||
"B027",
|
||||
# Allow boolean positional values in function calls, like `dict.get(... True)`
|
||||
"FBT003",
|
||||
# Ignore checks for possible passwords
|
||||
"S105", "S106", "S107",
|
||||
# Ignore complexity
|
||||
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
|
||||
# Allow print func
|
||||
"T201",
|
||||
# Allow assert statements
|
||||
"S101",
|
||||
]
|
||||
unfixable = [
|
||||
# Don't touch unused imports
|
||||
"F401",
|
||||
]
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["leetcode"]
|
||||
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
ban-relative-imports = "all"
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
# Tests can use magic values, assertions, and relative imports
|
||||
"tests/**/*" = ["PLR2004", "S101", "TID252"]
|
||||
|
||||
[tool.coverage.run]
|
||||
source_pkgs = ["leetcode", "tests"]
|
||||
branch = true
|
||||
parallel = true
|
||||
omit = [
|
||||
"src/leetcode/__about__.py",
|
||||
]
|
||||
|
||||
[tool.coverage.paths]
|
||||
leetcode = ["src/leetcode", "*/leetcode/src/leetcode"]
|
||||
tests = ["tests", "*/leetcode/tests"]
|
||||
|
||||
[tool.coverage.report]
|
||||
exclude_lines = [
|
||||
"no cov",
|
||||
"if __name__ == .__main__.:",
|
||||
"if TYPE_CHECKING:",
|
||||
]
|
57
leetcode/roman.py
Normal file
57
leetcode/roman.py
Normal file
@ -0,0 +1,57 @@
|
||||
SIMPLE = {
|
||||
"I": 1,
|
||||
"V": 5,
|
||||
"X": 10,
|
||||
"L": 50,
|
||||
"C": 100,
|
||||
"D": 500,
|
||||
"M": 1000,
|
||||
}
|
||||
SIMPLE_REVERSE = {v: k for k, v in SIMPLE.items()}
|
||||
COMPOUND = {
|
||||
"IV": 4,
|
||||
"IX": 9,
|
||||
"XL": 40,
|
||||
"XC": 90,
|
||||
"CD": 400,
|
||||
"CM": 900,
|
||||
}
|
||||
PREFIXES = {k[0] for k in COMPOUND.keys()}
|
||||
COMPOUND_REVERSE = {v: k for k, v in COMPOUND.items()}
|
||||
ALL = SIMPLE | COMPOUND
|
||||
ALL_REVERSE = {v: k for k, v in ALL.items()}
|
||||
|
||||
|
||||
def i2r(i: int) -> str:
|
||||
if i > 100_000:
|
||||
raise ValueError(f"{i} is too silly")
|
||||
|
||||
r: list[str] = []
|
||||
|
||||
for int_val, roman_val in sorted(ALL_REVERSE.items(), reverse=True):
|
||||
remainder = i % int_val
|
||||
|
||||
r += [roman_val] * int((i - remainder) / int_val)
|
||||
|
||||
i = remainder
|
||||
|
||||
return "".join(r)
|
||||
|
||||
|
||||
def r2i(r: str) -> int:
|
||||
total = 0
|
||||
offset = 0
|
||||
|
||||
for i in range(len(r)):
|
||||
if i + offset > len(r) - 1:
|
||||
break
|
||||
|
||||
c = r[i + offset]
|
||||
if c in PREFIXES and (i + offset + 1) < len(r) and c + r[i + offset + 1] in ALL:
|
||||
total += ALL[c + r[i + offset + 1]]
|
||||
offset += 1
|
||||
continue
|
||||
|
||||
total += ALL[c]
|
||||
|
||||
return total
|
57
leetcode/spiral_matrix.py
Normal file
57
leetcode/spiral_matrix.py
Normal file
@ -0,0 +1,57 @@
|
||||
import itertools
|
||||
import typing
|
||||
|
||||
|
||||
def matrix_spiral(matrix: list[list[typing.Any]]) -> list[typing.Any]:
|
||||
return [matrix[y][x] for x, y in matrix_spiral_path(matrix)]
|
||||
|
||||
|
||||
def matrix_spiral_path(matrix: list[list[int]]) -> list[tuple[int, int]]:
|
||||
snek = SpinSnek(matrix)
|
||||
|
||||
while snek.step():
|
||||
...
|
||||
|
||||
return snek.path
|
||||
|
||||
|
||||
COMPASS = (
|
||||
(1, 0), # east
|
||||
(0, 1), # south
|
||||
(-1, 0), # west
|
||||
(0, -1), # north
|
||||
)
|
||||
|
||||
|
||||
class SpinSnek:
|
||||
def __init__(self, board: list[list[int]], loc: tuple[int, int] = (0, 0)):
|
||||
self.max_loc: tuple[int, int] = (len(board[0]) - 1, len(board) - 1)
|
||||
self.spinner: itertools.cycle[tuple[int, int]] = itertools.cycle(COMPASS)
|
||||
self.direction = next(self.spinner)
|
||||
self.path: list[tuple[int, int]] = [loc]
|
||||
self.missteps: int = 0
|
||||
|
||||
def step(self) -> bool:
|
||||
loc = self.path[-1]
|
||||
next_loc: tuple[int, int] = (
|
||||
loc[0] + self.direction[0],
|
||||
loc[1] + self.direction[1],
|
||||
)
|
||||
|
||||
if (
|
||||
next_loc[0] > self.max_loc[0]
|
||||
or next_loc[1] > self.max_loc[1]
|
||||
or next_loc[0] < 0
|
||||
or next_loc[1] < 0
|
||||
or next_loc in self.path
|
||||
):
|
||||
self.direction = next(self.spinner)
|
||||
if self.missteps > len(COMPASS) - 1:
|
||||
return False
|
||||
|
||||
self.missteps += 1
|
||||
return self.step()
|
||||
|
||||
self.missteps: int = 0
|
||||
self.path.append(next_loc)
|
||||
return True
|
169
leetcode/stdlib.py
Normal file
169
leetcode/stdlib.py
Normal file
@ -0,0 +1,169 @@
|
||||
import dataclasses
|
||||
import typing
|
||||
|
||||
|
||||
class LinkedListNode(typing.Protocol):
|
||||
val: int
|
||||
next: typing.Optional["LinkedListNode"]
|
||||
|
||||
|
||||
class ListNode:
|
||||
"""ListNode is the leetcode "standard library" type used in linked lists"""
|
||||
|
||||
def __init__(self, val: int = 0, next: typing.Optional["ListNode"] = None): # no qa
|
||||
self.val = val
|
||||
self.next = next
|
||||
|
||||
|
||||
class ListNodeRandom:
|
||||
"""ListNodeRandom is another weirdo linked list thing from
|
||||
leetcode that is obviously very different than a binary tree
|
||||
node :upside_down_face:
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
x: int,
|
||||
next: typing.Optional["ListNodeRandom"] = None,
|
||||
random: typing.Optional["ListNodeRandom"] = None,
|
||||
):
|
||||
self.val = x
|
||||
self.next = next
|
||||
self.random = random
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ListNodeRandomNicely:
|
||||
val: int
|
||||
next: typing.Optional["ListNodeRandomNicely"] = None
|
||||
random: typing.Optional["ListNodeRandomNicely"] = None
|
||||
|
||||
|
||||
class BinaryTreeNode(typing.Protocol):
|
||||
val: int
|
||||
left: typing.Optional["BinaryTreeNode"]
|
||||
right: typing.Optional["BinaryTreeNode"]
|
||||
|
||||
|
||||
class TreeNode:
|
||||
"""TreeNode is the leetcode "standard library" type used in binary trees"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
val: int = 0,
|
||||
left: typing.Optional["TreeNode"] = None,
|
||||
right: typing.Optional["TreeNode"] = None,
|
||||
):
|
||||
self.val = val
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
@classmethod
|
||||
def from_int(cls, val: int | None) -> typing.Optional["TreeNode"]:
|
||||
if val is None:
|
||||
return None
|
||||
|
||||
return TreeNode(val)
|
||||
|
||||
# __repr__ was added by me
|
||||
def __repr__(self) -> str:
|
||||
filtered_parts = []
|
||||
|
||||
for key, value in [
|
||||
("val", self.val),
|
||||
("left", self.left),
|
||||
("right", self.right),
|
||||
]:
|
||||
if value is not None:
|
||||
filtered_parts.append((key, value))
|
||||
|
||||
middle = ", ".join([f"{k}={v!r}" for k, v in filtered_parts])
|
||||
|
||||
return f"TreeNode({middle})"
|
||||
|
||||
# __eq__ was added by me
|
||||
def __eq__(self, other: typing.Optional["TreeNode"]) -> bool:
|
||||
return (
|
||||
other is not None
|
||||
and self.val == other.val
|
||||
and self.left == other.left
|
||||
and self.right == other.right
|
||||
)
|
||||
|
||||
|
||||
class ConnectableBinaryTreeNode(typing.Protocol):
|
||||
val: int
|
||||
left: typing.Optional["BinaryTreeNode"]
|
||||
right: typing.Optional["BinaryTreeNode"]
|
||||
next: typing.Optional["BinaryTreeNode"]
|
||||
|
||||
|
||||
class Node:
|
||||
"""Node is the *other* leetcode "standard library" type used in binary trees"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
val: int = 0,
|
||||
left: typing.Optional["Node"] = None,
|
||||
right: typing.Optional["Node"] = None,
|
||||
next: typing.Optional["Node"] = None, # no qa
|
||||
):
|
||||
self.val = val
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.next = next
|
||||
|
||||
# __repr__ was added by me
|
||||
def __repr__(self) -> str:
|
||||
filtered_parts = []
|
||||
|
||||
for key, value in [
|
||||
("val", self.val),
|
||||
("right", self.right),
|
||||
("left", self.left),
|
||||
("next", self.next),
|
||||
]:
|
||||
if value is not None:
|
||||
filtered_parts.append((key, value))
|
||||
|
||||
middle = ", ".join([f"{k}={v!r}" for k, v in filtered_parts])
|
||||
|
||||
return f"Node({middle})"
|
||||
|
||||
# __eq__ was added by me
|
||||
def __eq__(self, other: "Node") -> bool:
|
||||
return (
|
||||
other is not None
|
||||
and self.val == other.val
|
||||
and self.left == other.left
|
||||
and self.right == other.right
|
||||
and self.next == other.next
|
||||
)
|
||||
|
||||
# __list__ was added by me
|
||||
def __list__(self) -> list[int | None]:
|
||||
ret = [self.val]
|
||||
ret += self.right.__list__() if self.right is not None else [None]
|
||||
ret += self.left.__list__() if self.left is not None else [None]
|
||||
ret += self.next.__list__() if self.next is not None else [None]
|
||||
return ret
|
||||
|
||||
|
||||
class NeighborlyNode:
|
||||
"""NeighborlyNode is a "Node" type used in leetcode graph puzzles"""
|
||||
|
||||
def __init__(self, val=0, neighbors=None):
|
||||
self.val = val
|
||||
self.neighbors = neighbors if neighbors is not None else []
|
||||
|
||||
|
||||
class NeighborlyNodeNicely(typing.NamedTuple):
|
||||
val: int
|
||||
neighbors: list["NeighborlyNodeNicely"]
|
||||
|
||||
def __eq__(self, other: typing.Optional["NeighborlyNodeNicely"]) -> bool:
|
||||
return (
|
||||
other is not None
|
||||
and self.val == other.val
|
||||
and [n.val for n in self.neighbors] == [n.val for n in other.neighbors]
|
||||
)
|
640
leetcode/stuff.py
Normal file
640
leetcode/stuff.py
Normal file
@ -0,0 +1,640 @@
|
||||
import collections.abc
|
||||
import copy
|
||||
import random
|
||||
import typing
|
||||
|
||||
import stdlib
|
||||
|
||||
|
||||
def yep(s: str) -> bool:
|
||||
return s.strip().lower().startswith("y")
|
||||
|
||||
|
||||
def guess_bisect_repl(lower: int, upper: int) -> int:
|
||||
mid = lower + ((upper - lower) // 2)
|
||||
|
||||
if yep(input(f"is it {mid}? ")):
|
||||
return mid
|
||||
|
||||
if yep(input(f"higher than {mid}? ")):
|
||||
return guess_bisect_repl(mid, upper)
|
||||
|
||||
return guess_bisect_repl(lower, mid)
|
||||
|
||||
|
||||
def find_sqrt_ish(n: int) -> int:
|
||||
return int(find_bisect(0, n, gen_sqrt_check(n)))
|
||||
|
||||
|
||||
def gen_sqrt_check(n: int) -> typing.Callable[[float], int]:
|
||||
def check(mid: float) -> int:
|
||||
mid_sq: float = mid * mid
|
||||
|
||||
if mid_sq == n:
|
||||
return 0
|
||||
|
||||
if mid_sq < n:
|
||||
return 1
|
||||
|
||||
return -1
|
||||
|
||||
return check
|
||||
|
||||
|
||||
def find_bisect(
|
||||
lower: float, upper: float, check: typing.Callable[[float], int]
|
||||
) -> float:
|
||||
mid: float = lower + ((upper - lower) / 2)
|
||||
|
||||
print(f"lower={lower} mid={mid} upper={upper}")
|
||||
|
||||
if mid == lower or mid == upper or check(mid) == 0:
|
||||
return mid
|
||||
|
||||
if check(mid) == 1:
|
||||
return find_bisect(mid, upper, check)
|
||||
|
||||
return find_bisect(lower, mid, check)
|
||||
|
||||
|
||||
def cartesian_path(p0: tuple[int, int], p1: tuple[int, int]) -> list[tuple[int, int]]:
|
||||
path: list[tuple[int, int]] = []
|
||||
|
||||
if p0 < p1:
|
||||
for i in range(p0[1], p1[1]):
|
||||
path.append((i, p0[0]))
|
||||
|
||||
for i in range(p0[0], p1[0]):
|
||||
path.append((p1[1], i))
|
||||
|
||||
else:
|
||||
for i in range(p0[1], p1[1] - 1, -1):
|
||||
path.append((i, p0[0]))
|
||||
|
||||
for i in range(p0[0] - 1, p1[0], -1):
|
||||
path.append((p1[1], i))
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def gen_matrix(width: int, height: int) -> list[list[int]]:
|
||||
return [list(range(width)) for _ in range(height)]
|
||||
|
||||
|
||||
class MinStack:
|
||||
def __init__(self):
|
||||
self._v: list[int] = []
|
||||
self._min: list[int] = []
|
||||
|
||||
def push(self, val: int) -> None:
|
||||
self._v.append(val)
|
||||
self._min.append(min(val, self._min[-1] if self._min else val))
|
||||
|
||||
def pop(self) -> None:
|
||||
self._v.pop(-1)
|
||||
self._min.pop(-1)
|
||||
|
||||
def top(self) -> int:
|
||||
return self._v[-1]
|
||||
|
||||
def getMin(self) -> int: # no qa
|
||||
return self._min[-1]
|
||||
|
||||
|
||||
def linked_list_to_list(head: stdlib.LinkedListNode | None) -> list[int]:
|
||||
seen: set[int] = set()
|
||||
ret: list[int] = []
|
||||
|
||||
while head is not None:
|
||||
if hash(head) in seen:
|
||||
return ret
|
||||
|
||||
seen.add(hash(head))
|
||||
ret.append(head.val)
|
||||
head = head.next
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def sort_linked_list(head: stdlib.LinkedListNode | None) -> stdlib.LinkedListNode | None:
|
||||
by_val: list[tuple[int, stdlib.LinkedListNode]] = []
|
||||
ret: stdlib.LinkedListNode | None = None
|
||||
|
||||
while head is not None:
|
||||
by_val.append((head.val, head))
|
||||
head = head.next
|
||||
|
||||
cur = ret
|
||||
|
||||
for _, node in sorted(by_val, key=lambda v: v[0]):
|
||||
if cur is None:
|
||||
cur = ret = node
|
||||
continue
|
||||
|
||||
cur.next = node
|
||||
cur = cur.next
|
||||
|
||||
if cur is not None:
|
||||
cur.next = None
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def connect_binary_tree_right(
|
||||
root: stdlib.ConnectableBinaryTreeNode | None,
|
||||
) -> tuple[stdlib.ConnectableBinaryTreeNode | None, list[int | None]]:
|
||||
if root is None:
|
||||
return None, []
|
||||
|
||||
by_level = binary_tree_by_level(copy.deepcopy(root))
|
||||
by_level = typing.cast(dict[int, list[stdlib.ConnectableBinaryTreeNode]], by_level)
|
||||
serialized: list[int | None] = []
|
||||
|
||||
print("")
|
||||
|
||||
if 0 not in by_level or len(by_level[0]) == 0:
|
||||
return None, []
|
||||
|
||||
connected_root = by_level[0][0]
|
||||
|
||||
for level, nodes in sorted(by_level.items(), key=lambda p: p[0]):
|
||||
for i in range(len(nodes)):
|
||||
serialized.append(nodes[i].val)
|
||||
|
||||
if len(nodes) > i + 1:
|
||||
print(f"{'-' * level}> connecting {nodes[i].val} -> {nodes[i + 1].val}")
|
||||
nodes[i].next = nodes[i + 1]
|
||||
|
||||
serialized.append(None)
|
||||
|
||||
return connected_root, serialized
|
||||
|
||||
|
||||
def binary_tree_by_level(
|
||||
root: stdlib.BinaryTreeNode,
|
||||
) -> dict[int, list[stdlib.BinaryTreeNode]]:
|
||||
combined: dict[int, list[stdlib.BinaryTreeNode]] = {}
|
||||
|
||||
for path in collect_binary_tree_levels(0, root):
|
||||
level, node = path
|
||||
combined.setdefault(level, [])
|
||||
combined[level].insert(0, node)
|
||||
|
||||
return combined
|
||||
|
||||
|
||||
def collect_binary_tree_levels(
|
||||
level: int, node: stdlib.BinaryTreeNode | None
|
||||
) -> typing.Iterator[tuple[int, stdlib.BinaryTreeNode]]:
|
||||
if node is None:
|
||||
return
|
||||
|
||||
yield (level, node)
|
||||
yield from collect_binary_tree_levels(level + 1, node.right)
|
||||
yield from collect_binary_tree_levels(level + 1, node.left)
|
||||
|
||||
|
||||
def sum_binary_tree_path_ints(root: stdlib.BinaryTreeNode | None) -> int:
|
||||
path_ints: list[int] = []
|
||||
|
||||
for path in collect_binary_tree_paths(root):
|
||||
path_ints.append(int("".join([str(node.val) for node in path])))
|
||||
|
||||
return sum(path_ints)
|
||||
|
||||
|
||||
def binary_tree_paths_as_lists(
|
||||
paths: list[list[stdlib.BinaryTreeNode]],
|
||||
) -> list[list[int]]:
|
||||
paths_vals: list[list[int]] = []
|
||||
|
||||
for path in paths:
|
||||
paths_vals.append([node.val for node in path])
|
||||
|
||||
return paths_vals
|
||||
|
||||
|
||||
def collect_binary_tree_paths(
|
||||
node: stdlib.BinaryTreeNode | None,
|
||||
) -> typing.Iterator[list[stdlib.BinaryTreeNode]]:
|
||||
if node is None:
|
||||
return
|
||||
|
||||
if node.right is None and node.left is None:
|
||||
yield [node]
|
||||
return
|
||||
|
||||
if node.right is not None:
|
||||
for path in collect_binary_tree_paths(node.right):
|
||||
yield [node] + path
|
||||
|
||||
if node.left is not None:
|
||||
for path in collect_binary_tree_paths(node.left):
|
||||
yield [node] + path
|
||||
|
||||
|
||||
def binary_tree_from_list(inlist: list[int | None]) -> stdlib.BinaryTreeNode | None:
|
||||
if len(inlist) == 0:
|
||||
return None
|
||||
|
||||
nodes: list[stdlib.BinaryTreeNode | None] = [
|
||||
typing.cast(stdlib.BinaryTreeNode | None, stdlib.TreeNode.from_int(i))
|
||||
for i in inlist
|
||||
]
|
||||
nodes_copy = nodes[::-1]
|
||||
root = nodes_copy.pop()
|
||||
|
||||
for node in nodes:
|
||||
if node is None:
|
||||
continue
|
||||
|
||||
if len(nodes_copy) == 0:
|
||||
break
|
||||
|
||||
node.left = nodes_copy.pop()
|
||||
|
||||
if len(nodes_copy) > 0:
|
||||
node.right = nodes_copy.pop()
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def binary_tree_from_preorder_inorder(
|
||||
preorder: list[int], inorder: list[int]
|
||||
) -> stdlib.BinaryTreeNode | None:
|
||||
preorder_reversed = preorder[::-1]
|
||||
|
||||
def subtree(left: list[int], right: list[int]) -> stdlib.BinaryTreeNode:
|
||||
root: stdlib.BinaryTreeNode = typing.cast(
|
||||
stdlib.BinaryTreeNode, stdlib.TreeNode(preorder_reversed.pop())
|
||||
)
|
||||
|
||||
if len(left) > 1:
|
||||
split_pos = left.index(preorder_reversed[-1])
|
||||
root.left = subtree(left[:split_pos], left[split_pos + 1 :])
|
||||
elif len(left) == 1:
|
||||
preorder_reversed.remove(left[0])
|
||||
root.left = typing.cast(stdlib.BinaryTreeNode, stdlib.TreeNode(left[0]))
|
||||
|
||||
if len(right) > 1:
|
||||
split_pos = right.index(preorder_reversed[-1])
|
||||
root.right = subtree(right[:split_pos], right[split_pos + 1 :])
|
||||
elif len(right) == 1:
|
||||
preorder_reversed.remove(right[0])
|
||||
root.right = typing.cast(stdlib.BinaryTreeNode, stdlib.TreeNode(right[0]))
|
||||
|
||||
return root
|
||||
|
||||
split_pos = inorder.index(preorder[0])
|
||||
|
||||
return subtree(inorder[:split_pos], inorder[split_pos + 1 :])
|
||||
|
||||
|
||||
class JumpSpace(typing.NamedTuple):
|
||||
pos: int
|
||||
val: int
|
||||
moves: list["JumpSpace"]
|
||||
|
||||
@classmethod
|
||||
def from_board(
|
||||
cls, pos: int = 0, board: typing.Iterable[int] = ()
|
||||
) -> typing.Optional["JumpSpace"]:
|
||||
board = list(board)
|
||||
|
||||
if len(board) == 0:
|
||||
return None
|
||||
|
||||
space = cls(pos, board[pos], [])
|
||||
space.collect(board)
|
||||
return space
|
||||
|
||||
def collect(self, board: list[int]) -> None:
|
||||
del self.moves[:]
|
||||
|
||||
if self.pos > len(board) or len(board) == 0:
|
||||
return
|
||||
|
||||
for n in range(self.pos + 1, self.pos + self.val + 1):
|
||||
if n >= len(board):
|
||||
break
|
||||
|
||||
self.moves.append(typing.cast(JumpSpace, JumpSpace.from_board(n, board)))
|
||||
|
||||
def jump_paths(self) -> list[list[int]]:
|
||||
ret: list[list[int]] = [[self.pos]]
|
||||
|
||||
for next_space in self.moves:
|
||||
for path in next_space.jump_paths():
|
||||
ret.append([self.pos] + path)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def collect_complete_jump_paths_from_board(board: list[int]) -> list[list[int]]:
|
||||
return [
|
||||
p
|
||||
for p in collect_jump_paths_from_board(board)
|
||||
if len(p) > 0 and p[-1] >= len(board) - 1
|
||||
]
|
||||
|
||||
|
||||
def collect_jump_paths_from_board(board: list[int]) -> list[list[int]]:
|
||||
space = JumpSpace.from_board(0, board)
|
||||
if space is None:
|
||||
return []
|
||||
|
||||
return space.jump_paths()
|
||||
|
||||
|
||||
# NOTE: the expensive way goes like this
|
||||
# complete_paths = collect_complete_jump_paths_from_board(board)
|
||||
|
||||
# if len(complete_paths) == 0:
|
||||
# return -1
|
||||
|
||||
# return min([len(p) - 1 for p in complete_paths])
|
||||
|
||||
|
||||
def count_min_jumps_from_board(board: list[int]) -> int:
|
||||
return len(collect_min_jumps_from_board(board))
|
||||
|
||||
|
||||
def collect_min_jumps_from_board(board: list[int]) -> list[int]:
|
||||
if len(board) < 3:
|
||||
return list(range(1, len(board)))
|
||||
|
||||
jumps: list[int] = []
|
||||
range_begin: int = 0
|
||||
val = board[range_begin]
|
||||
range_end: int = range_begin + val + 1
|
||||
|
||||
while range_end < len(board):
|
||||
potential_jumps = board[range_begin:range_end]
|
||||
|
||||
scored_jumps = [
|
||||
(val + range_begin + i, val, range_begin + i)
|
||||
for i, val in enumerate(potential_jumps)
|
||||
]
|
||||
_, val, space = max(scored_jumps)
|
||||
|
||||
jumps.append(space)
|
||||
|
||||
range_begin = space
|
||||
range_end = range_begin + val + 1
|
||||
|
||||
return jumps + [len(board) - 1]
|
||||
|
||||
|
||||
def h_index(citations: list[int]) -> int:
|
||||
last_qualified = None
|
||||
|
||||
for i, citation_count in enumerate(list(sorted(citations, reverse=True))):
|
||||
if citation_count >= i + 1:
|
||||
last_qualified = i + 1
|
||||
else:
|
||||
break
|
||||
|
||||
return last_qualified or 0
|
||||
|
||||
|
||||
class SlowRandomizedSet:
|
||||
def __init__(self):
|
||||
self._i: set[int] = set()
|
||||
|
||||
def insert(self, val: int) -> bool:
|
||||
ok = val not in self._i
|
||||
self._i.add(val)
|
||||
return ok
|
||||
|
||||
def remove(self, val: int) -> bool:
|
||||
if val in self._i:
|
||||
self._i.remove(val)
|
||||
return True
|
||||
return False
|
||||
|
||||
def getRandom(self) -> int:
|
||||
return random.choice(list(self._i))
|
||||
|
||||
|
||||
class RandomizedSet:
|
||||
def __init__(self):
|
||||
self._l: list[int] = []
|
||||
self._m: dict[int, int] = {}
|
||||
|
||||
def insert(self, val: int) -> bool:
|
||||
if val in self._m:
|
||||
return False
|
||||
|
||||
self._m[val] = len(self._l)
|
||||
self._l.append(val)
|
||||
|
||||
return True
|
||||
|
||||
def remove(self, val: int) -> bool:
|
||||
if val not in self._m:
|
||||
return False
|
||||
|
||||
val_loc = self._m[val]
|
||||
last_val = self._l[-1]
|
||||
self._l[val_loc] = last_val
|
||||
self._m[last_val] = val_loc
|
||||
|
||||
self._l.pop()
|
||||
self._m.pop(val)
|
||||
|
||||
return True
|
||||
|
||||
def getRandom(self) -> int:
|
||||
return random.choice(self._l)
|
||||
|
||||
|
||||
class TrieNode(typing.NamedTuple):
|
||||
value: str
|
||||
kids: dict[str, "TrieNode"]
|
||||
|
||||
@property
|
||||
def is_leaf(self) -> bool:
|
||||
return "__self__" in self.kids
|
||||
|
||||
@classmethod
|
||||
def leaf(cls) -> "TrieNode":
|
||||
return cls("__self__", {})
|
||||
|
||||
|
||||
class Trie:
|
||||
def __init__(self):
|
||||
self._root_node = TrieNode("", {})
|
||||
|
||||
def insert(self, word: str) -> None:
|
||||
if len(word) == 0:
|
||||
return
|
||||
|
||||
current_node = self._root_node
|
||||
|
||||
for prefix in [word[: i + 1] for i in range(len(word))]:
|
||||
current_node.kids.setdefault(prefix, TrieNode(prefix, {}))
|
||||
current_node = current_node.kids[prefix]
|
||||
|
||||
leaf = TrieNode.leaf()
|
||||
current_node.kids[leaf.value] = leaf
|
||||
|
||||
def search(self, word: str) -> bool:
|
||||
return self._has(word, prefix_ok=False)
|
||||
|
||||
def startsWith(self, prefix: str) -> bool:
|
||||
return self._has(prefix, prefix_ok=True)
|
||||
|
||||
def _has(self, word: str, prefix_ok: bool) -> bool:
|
||||
if len(word) == 0:
|
||||
return True
|
||||
|
||||
reverse_path = [word[: i + 1] for i in range(len(word))][::-1]
|
||||
current_node = self._root_node
|
||||
|
||||
while reverse_path and current_node is not None:
|
||||
current_node = current_node.kids.get(reverse_path.pop())
|
||||
|
||||
return (
|
||||
current_node is not None
|
||||
and (current_node.is_leaf or prefix_ok)
|
||||
and current_node.value == word
|
||||
)
|
||||
|
||||
|
||||
def count_factorial_trailing_zeroes(number: int) -> int:
|
||||
divisor: int = 5
|
||||
zeroes_count: int = 0
|
||||
|
||||
while divisor <= number:
|
||||
zeroes_count += number // divisor
|
||||
divisor *= 5
|
||||
|
||||
return zeroes_count
|
||||
|
||||
|
||||
def copy_random_list(
|
||||
head: stdlib.ListNodeRandom | None,
|
||||
) -> stdlib.ListNodeRandom | None:
|
||||
if head is None:
|
||||
return None
|
||||
|
||||
ordered = []
|
||||
cur = head
|
||||
while cur is not None:
|
||||
ordered.append(cur)
|
||||
cur = cur.next
|
||||
|
||||
ordered_copy = [stdlib.ListNodeRandom(entry.val) for entry in ordered]
|
||||
|
||||
hash_idx = {hash(n): i for i, n in enumerate(ordered)}
|
||||
|
||||
for i, entry in enumerate(ordered):
|
||||
if i + 1 < len(ordered_copy):
|
||||
ordered_copy[i].next = ordered_copy[i + 1]
|
||||
|
||||
if entry.random is not None:
|
||||
ordered_copy[i].random = ordered_copy[hash_idx[hash(entry.random)]]
|
||||
|
||||
return ordered_copy[0]
|
||||
|
||||
|
||||
def sum_max_sub_array(nums: list[int]) -> int:
|
||||
mmax = last = prev = nums[0]
|
||||
|
||||
for i in range(1, len(nums)):
|
||||
prev = nums[i] + last
|
||||
last = max(nums[i], prev)
|
||||
mmax = max(mmax, last)
|
||||
|
||||
return mmax
|
||||
|
||||
|
||||
def sum_max_sub_array_i(nums: list[int]) -> tuple[int, int]:
|
||||
mmax_i: int = 0
|
||||
mmax = last = prev = nums[0]
|
||||
|
||||
for i in range(1, len(nums)):
|
||||
prev = nums[i] + last
|
||||
last = max(nums[i], prev)
|
||||
mmax_i = i if last > mmax else mmax_i
|
||||
mmax = max(mmax, last)
|
||||
|
||||
return mmax_i, mmax
|
||||
|
||||
|
||||
def sum_max_sub_array_accum(nums: list[int]) -> int:
|
||||
accum: list[int] = [nums[0]]
|
||||
|
||||
for i in range(1, len(nums)):
|
||||
prev: int = nums[i] + accum[-1]
|
||||
accum.append(max(nums[i], prev))
|
||||
|
||||
return max(accum)
|
||||
|
||||
|
||||
def accum_sub_array_maxes(nums: list[int]) -> list[int]:
|
||||
accum: list[int] = [nums[0]]
|
||||
|
||||
for i in range(1, len(nums)):
|
||||
prev: int = nums[i] + accum[-1]
|
||||
accum.append(max(nums[i], prev))
|
||||
|
||||
return accum
|
||||
|
||||
|
||||
def neighborly_node_from_list(inlist: list[list[int]]):
|
||||
# Alias "Node" type for leetcode compat
|
||||
Node = stdlib.NeighborlyNodeNicely
|
||||
|
||||
if len(inlist) == 0:
|
||||
return None
|
||||
|
||||
outlist = [Node(i + 1, []) for i in range(len(inlist))]
|
||||
|
||||
for i in range(len(inlist)):
|
||||
outlist[i].neighbors[:] = []
|
||||
|
||||
for neighbor_val in inlist[i]:
|
||||
outlist[i].neighbors.append(outlist[neighbor_val - 1])
|
||||
|
||||
return outlist[0]
|
||||
|
||||
|
||||
def neighborly_node_to_list(node) -> list[list[int]]:
|
||||
serialized: dict[int, list[int]] = {}
|
||||
|
||||
for cur in traverse_neighborly_node(node, serialized):
|
||||
if cur is None:
|
||||
break
|
||||
|
||||
serialized[cur.val] = [n.val for n in cur.neighbors]
|
||||
|
||||
return [v for _, v in sorted(serialized.items())]
|
||||
|
||||
|
||||
def traverse_neighborly_node(
|
||||
node: stdlib.NeighborlyNodeNicely, memo: collections.abc.Container[int]
|
||||
) -> typing.Iterator[stdlib.NeighborlyNodeNicely | None]:
|
||||
yield node
|
||||
|
||||
if node is None:
|
||||
return
|
||||
|
||||
for neighbor in node.neighbors:
|
||||
if neighbor.val in memo:
|
||||
continue
|
||||
|
||||
yield from traverse_neighborly_node(neighbor, memo)
|
||||
|
||||
|
||||
def find_min_in_rotated_array(nums: list[int]) -> int:
|
||||
if nums[0] <= nums[-1]:
|
||||
return nums[0]
|
||||
|
||||
if len(nums) <= 3:
|
||||
return min(nums)
|
||||
|
||||
if nums[len(nums) // 2] > nums[-1]:
|
||||
return find_min_in_rotated_array(nums[len(nums) // 2 :])
|
||||
|
||||
return find_min_in_rotated_array(nums[: (len(nums) // 2) + 1])
|
15
leetcode/test_roman.py
Normal file
15
leetcode/test_roman.py
Normal file
@ -0,0 +1,15 @@
|
||||
import pytest
|
||||
|
||||
import roman
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("n", "expected"),
|
||||
[
|
||||
(3, "III"),
|
||||
(58, "LVIII"),
|
||||
(1994, "MCMXCIV"),
|
||||
],
|
||||
)
|
||||
def test_int_to_roman(n: int, expected: str):
|
||||
assert roman.i2r(n) == expected
|
46
leetcode/test_spiral_matrix.py
Normal file
46
leetcode/test_spiral_matrix.py
Normal file
@ -0,0 +1,46 @@
|
||||
import pytest
|
||||
|
||||
import spiral_matrix
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("matrix", "expected"),
|
||||
[
|
||||
(
|
||||
[
|
||||
["a", "b", "c"],
|
||||
["d", "e", "f"],
|
||||
["g", "h", "i"],
|
||||
],
|
||||
["a", "b", "c", "f", "i", "h", "g", "d", "e"],
|
||||
),
|
||||
(
|
||||
[
|
||||
[1, 2, 3, 4],
|
||||
[5, 6, 7, 8],
|
||||
[9, 10, 11, 12],
|
||||
],
|
||||
[1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7],
|
||||
),
|
||||
(
|
||||
[
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
],
|
||||
[] # noqa
|
||||
+ [1, 2, 3, 4, 5, 6, 7, 8, 9] # right
|
||||
+ [9, 9, 9] # down
|
||||
+ [9, 8, 7, 6, 5, 4, 3, 2, 1] # left
|
||||
+ [1, 1] # up
|
||||
+ [1, 2, 3, 4, 5, 6, 7, 8] # right
|
||||
+ [8] # down
|
||||
+ [8, 7, 6, 5, 4, 3, 2] # left
|
||||
+ [2, 3, 4, 5, 6, 7], # right
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_matrix_spiral(matrix, expected):
|
||||
assert spiral_matrix.matrix_spiral(matrix) == expected
|
505
leetcode/test_stuff.py
Normal file
505
leetcode/test_stuff.py
Normal file
@ -0,0 +1,505 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
import stuff
|
||||
import stdlib
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("n", "expected"),
|
||||
[
|
||||
(0, 0),
|
||||
(1, 1),
|
||||
(5, 2),
|
||||
(4, 2),
|
||||
(8, 2),
|
||||
(9, 3),
|
||||
],
|
||||
)
|
||||
def test_find_sqrt_ish(n: int, expected: int):
|
||||
assert stuff.find_sqrt_ish(n) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("ops", "expected"),
|
||||
[
|
||||
(
|
||||
(
|
||||
[
|
||||
("new",),
|
||||
("push", -2),
|
||||
("push", 0),
|
||||
("push", -3),
|
||||
("getMin",),
|
||||
("pop",),
|
||||
("top",),
|
||||
("getMin",),
|
||||
]
|
||||
),
|
||||
[
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
-3,
|
||||
None,
|
||||
0,
|
||||
-2,
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
def test_min_stack(ops: list[tuple[str] | tuple[str, int]], expected: list[int | None]):
|
||||
returned: list[int | None] = []
|
||||
inst: stuff.MinStack | None = None
|
||||
|
||||
for op in ops:
|
||||
if len(op) == 1:
|
||||
if op[0] == "new":
|
||||
inst = stuff.MinStack()
|
||||
returned.append(None)
|
||||
continue
|
||||
|
||||
returned.append(getattr(inst, op[0])())
|
||||
continue
|
||||
|
||||
method, arg = op
|
||||
returned.append(getattr(inst, method)(arg))
|
||||
|
||||
assert returned == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("head", "expected"),
|
||||
[
|
||||
(None, None),
|
||||
(
|
||||
stdlib.ListNode(
|
||||
4, stdlib.ListNode(2, stdlib.ListNode(1, stdlib.ListNode(3)))
|
||||
),
|
||||
stdlib.ListNode(
|
||||
1, stdlib.ListNode(2, stdlib.ListNode(3, stdlib.ListNode(4)))
|
||||
),
|
||||
),
|
||||
(
|
||||
stdlib.ListNode(
|
||||
4,
|
||||
stdlib.ListNode(
|
||||
19,
|
||||
stdlib.ListNode(
|
||||
14,
|
||||
stdlib.ListNode(
|
||||
5,
|
||||
stdlib.ListNode(
|
||||
-3,
|
||||
stdlib.ListNode(
|
||||
1,
|
||||
stdlib.ListNode(
|
||||
8,
|
||||
stdlib.ListNode(
|
||||
5, stdlib.ListNode(11, stdlib.ListNode(15))
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
stdlib.ListNode(
|
||||
-3,
|
||||
stdlib.ListNode(
|
||||
1,
|
||||
stdlib.ListNode(
|
||||
4,
|
||||
stdlib.ListNode(
|
||||
5,
|
||||
stdlib.ListNode(
|
||||
5,
|
||||
stdlib.ListNode(
|
||||
8,
|
||||
stdlib.ListNode(
|
||||
11,
|
||||
stdlib.ListNode(
|
||||
14, stdlib.ListNode(15, stdlib.ListNode(19))
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_sort_linked_list(
|
||||
head: stdlib.LinkedListNode | None, expected: stdlib.LinkedListNode | None
|
||||
):
|
||||
if head is None:
|
||||
assert stuff.sort_linked_list(head) == expected
|
||||
return
|
||||
|
||||
assert stuff.linked_list_to_list(
|
||||
stuff.sort_linked_list(head)
|
||||
) == stuff.linked_list_to_list(expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("root", "expected"),
|
||||
[
|
||||
(
|
||||
stdlib.Node(
|
||||
1,
|
||||
left=stdlib.Node(2, left=stdlib.Node(4), right=stdlib.Node(5)),
|
||||
right=stdlib.Node(3, right=stdlib.Node(7)),
|
||||
),
|
||||
[1, None, 2, 3, None, 4, 5, 7, None],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_connect_binary_tree_right(
|
||||
root: stdlib.ConnectableBinaryTreeNode | None, expected: list[int | None] | None
|
||||
):
|
||||
if expected is None:
|
||||
assert root is None
|
||||
return
|
||||
|
||||
connected, serialized = stuff.connect_binary_tree_right(root)
|
||||
assert connected is not None
|
||||
assert serialized == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("root", "expected"),
|
||||
[
|
||||
(
|
||||
stdlib.Node(
|
||||
4,
|
||||
right=stdlib.Node(0),
|
||||
left=stdlib.Node(9, right=stdlib.Node(1), left=stdlib.Node(5)),
|
||||
),
|
||||
1026,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_connect_binary_tree_sum_numbers(
|
||||
root: stdlib.BinaryTreeNode | None, expected: int
|
||||
):
|
||||
assert stuff.sum_binary_tree_path_ints(root) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("inlist", "expected"),
|
||||
[
|
||||
(
|
||||
[3, 5, 1, 6, 2, 0, 8, None, None, 7, 4],
|
||||
stdlib.TreeNode(
|
||||
3,
|
||||
left=stdlib.TreeNode(
|
||||
5,
|
||||
left=stdlib.TreeNode(6),
|
||||
right=stdlib.TreeNode(
|
||||
2,
|
||||
left=stdlib.TreeNode(7),
|
||||
right=stdlib.TreeNode(4),
|
||||
),
|
||||
),
|
||||
right=stdlib.TreeNode(
|
||||
1,
|
||||
left=stdlib.TreeNode(0),
|
||||
right=stdlib.TreeNode(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_binary_tree_from_list(
|
||||
inlist: list[int | None], expected: stdlib.BinaryTreeNode | None
|
||||
):
|
||||
assert stuff.binary_tree_from_list(inlist) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("preorder", "inorder", "expected"),
|
||||
[
|
||||
(
|
||||
[3, 9, 20, 15, 7],
|
||||
[9, 3, 15, 20, 7],
|
||||
stdlib.TreeNode(
|
||||
3,
|
||||
left=stdlib.TreeNode(9),
|
||||
right=stdlib.TreeNode(
|
||||
20, left=stdlib.TreeNode(15), right=stdlib.TreeNode(7)
|
||||
),
|
||||
),
|
||||
),
|
||||
(
|
||||
[-1],
|
||||
[-1],
|
||||
stdlib.TreeNode(-1),
|
||||
),
|
||||
(
|
||||
[1, 2],
|
||||
[1, 2],
|
||||
stdlib.TreeNode(1, right=stdlib.TreeNode(2)),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_binary_tree_from_preorder_inorder(
|
||||
preorder: list[int], inorder: list[int], expected: stdlib.BinaryTreeNode
|
||||
):
|
||||
assert stuff.binary_tree_from_preorder_inorder(preorder, inorder) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("board", "expected"),
|
||||
[
|
||||
(
|
||||
[2, 3, 1, 1, 4],
|
||||
[
|
||||
[0, 1, 2, 3, 4],
|
||||
[0, 1, 3, 4],
|
||||
[0, 1, 4],
|
||||
[0, 2, 3, 4],
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_collect_complete_jump_paths_from_board(
|
||||
board: list[int], expected: list[list[int]]
|
||||
):
|
||||
assert list(sorted(stuff.collect_complete_jump_paths_from_board(board))) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("board", "expected"),
|
||||
[
|
||||
(
|
||||
[2, 3, 1, 1, 4],
|
||||
2,
|
||||
),
|
||||
(
|
||||
[2, 3, 0, 1, 4],
|
||||
2,
|
||||
),
|
||||
(
|
||||
[1],
|
||||
0,
|
||||
),
|
||||
(
|
||||
[1, 2],
|
||||
1,
|
||||
),
|
||||
(
|
||||
[6, 2, 6, 1, 7, 9, 3, 5, 3, 7, 2, 8, 9, 4, 7, 7, 2, 2, 8, 4, 6, 6, 1, 3],
|
||||
4,
|
||||
),
|
||||
(
|
||||
[3, 4, 3, 2, 5, 4, 3],
|
||||
3,
|
||||
),
|
||||
(
|
||||
[3, 2, 1],
|
||||
1,
|
||||
),
|
||||
(
|
||||
[1, 2, 3],
|
||||
2,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_count_min_jumps_from_board(board: list[int], expected: int):
|
||||
assert stuff.count_min_jumps_from_board(board) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("citations", "expected"),
|
||||
[
|
||||
(
|
||||
[3, 0, 6, 1, 5],
|
||||
3,
|
||||
),
|
||||
(
|
||||
[1, 3, 1],
|
||||
1,
|
||||
),
|
||||
(
|
||||
[100],
|
||||
1,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_h_index(citations: list[int], expected: int):
|
||||
assert stuff.h_index(citations) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("cls",),
|
||||
[
|
||||
(stuff.SlowRandomizedSet,),
|
||||
(stuff.RandomizedSet,),
|
||||
],
|
||||
)
|
||||
def test_randomized_set(cls: type[stuff.RandomizedSet] | type[stuff.SlowRandomizedSet]):
|
||||
inst = cls()
|
||||
|
||||
assert inst.insert(1) is True
|
||||
assert inst.remove(2) is False
|
||||
assert inst.insert(2) is True
|
||||
assert inst.getRandom() in (1, 2)
|
||||
assert inst.remove(1) is True
|
||||
assert inst.insert(2) is False
|
||||
assert inst.getRandom() == 2
|
||||
|
||||
inst = cls()
|
||||
|
||||
assert inst.insert(1) is True
|
||||
assert inst.insert(10) is True
|
||||
assert inst.insert(20) is True
|
||||
assert inst.insert(30) is True
|
||||
|
||||
seen: set[int] = set()
|
||||
|
||||
for _ in range(10_000):
|
||||
seen.add(inst.getRandom())
|
||||
|
||||
assert seen == {1, 10, 20, 30}
|
||||
|
||||
# ["remove","remove","insert","getRandom","remove","insert"]
|
||||
# [[0],[0],[0],[],[0],[0]]
|
||||
|
||||
inst = cls()
|
||||
|
||||
assert inst.remove(0) is False
|
||||
assert inst.remove(0) is False
|
||||
assert inst.insert(0) is True
|
||||
assert inst.getRandom() == 0
|
||||
assert inst.remove(0) is True
|
||||
assert inst.insert(0) is True
|
||||
|
||||
# ["RandomizedSet","insert","insert","remove","insert","remove","getRandom"]
|
||||
# [[],[0],[1],[0],[2],[1],[]]
|
||||
|
||||
inst = cls()
|
||||
|
||||
assert inst.insert(0) is True
|
||||
assert inst.insert(1) is True
|
||||
assert inst.remove(0) is True
|
||||
assert inst.insert(2) is True
|
||||
assert inst.remove(1) is True
|
||||
assert inst.getRandom() == 2
|
||||
|
||||
|
||||
def test_trie_single_letter():
|
||||
trie = stuff.Trie()
|
||||
|
||||
assert trie.insert("a") is None
|
||||
assert trie.search("a") is True
|
||||
assert trie.startsWith("a") is True
|
||||
|
||||
|
||||
def test_trie_prefix_leaf():
|
||||
trie = stuff.Trie()
|
||||
|
||||
assert trie.insert("apple") is None
|
||||
assert trie.search("apple") is True
|
||||
assert trie.search("app") is False
|
||||
assert trie.startsWith("app") is True
|
||||
assert trie.insert("app") is None
|
||||
assert trie.search("app") is True
|
||||
|
||||
|
||||
def test_trie_two_letter():
|
||||
trie = stuff.Trie()
|
||||
|
||||
assert trie.insert("ab") is None
|
||||
assert trie.search("a") is False
|
||||
assert trie.startsWith("a") is True
|
||||
|
||||
|
||||
def test_trie_busy():
|
||||
trie = stuff.Trie()
|
||||
|
||||
assert trie.insert("app") is None
|
||||
assert trie.insert("apple") is None
|
||||
assert trie.insert("beer") is None
|
||||
assert trie.insert("add") is None
|
||||
assert trie.insert("jam") is None
|
||||
assert trie.insert("rental") is None
|
||||
assert trie.search("apps") is False
|
||||
assert trie.search("app") is True
|
||||
assert trie.search("ad") is False
|
||||
assert trie.search("applepie") is False
|
||||
assert trie.search("rest") is False
|
||||
assert trie.search("jan") is False
|
||||
assert trie.search("rent") is False
|
||||
assert trie.search("beer") is True
|
||||
assert trie.search("jam") is True
|
||||
assert trie.startsWith("apps") is False
|
||||
assert trie.startsWith("app") is True
|
||||
assert trie.startsWith("ad") is True
|
||||
assert trie.startsWith("applepie") is False
|
||||
assert trie.startsWith("rest") is False
|
||||
assert trie.startsWith("jan") is False
|
||||
assert trie.startsWith("rent") is True
|
||||
assert trie.startsWith("beer") is True
|
||||
assert trie.startsWith("jam") is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("nums", "expected"),
|
||||
[
|
||||
(
|
||||
[-2, 1, -3, 4, -1, 2, 1, -5, 4],
|
||||
6,
|
||||
),
|
||||
(
|
||||
[1],
|
||||
1,
|
||||
),
|
||||
(
|
||||
[5, 4, -1, 7, 8],
|
||||
23,
|
||||
),
|
||||
(
|
||||
[-2, 1],
|
||||
1,
|
||||
),
|
||||
(
|
||||
json.load(open(".testdata/max_sub_array0.json")),
|
||||
11081,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_max_sub_array(nums: list[int], expected: int):
|
||||
assert stuff.sum_max_sub_array(nums) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("inlist",),
|
||||
[
|
||||
([[2, 3, 4], [1, 7], [1], [1, 5, 6, 8], [4], [4], [2], [4]],),
|
||||
([[2, 4], [1, 3], [2, 4], [1, 3]],),
|
||||
],
|
||||
)
|
||||
def test_copy_neighborly_node(inlist):
|
||||
orig = stuff.neighborly_node_from_list(inlist)
|
||||
copied = stuff.neighborly_node_from_list(stuff.neighborly_node_to_list(orig))
|
||||
|
||||
assert id(orig) != id(copied)
|
||||
assert orig == copied
|
||||
assert stuff.neighborly_node_to_list(orig) == stuff.neighborly_node_to_list(copied)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("nums", "expected"),
|
||||
[
|
||||
([3, 4, 5, 1, 2], 1),
|
||||
([4, 5, 6, 7, 0, 1, 2], 0),
|
||||
([11, 13, 15, 17], 11),
|
||||
],
|
||||
)
|
||||
def test_find_min_in_rotated_array(nums: list[int], expected: int):
|
||||
assert stuff.find_min_in_rotated_array(nums) == expected
|
1
modernc/01/.gitignore
vendored
Normal file
1
modernc/01/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
getting-started
|
23
modernc/01/bad.c
Normal file
23
modernc/01/bad.c
Normal file
@ -0,0 +1,23 @@
|
||||
/* This may look like nonsense, but really is -*- mode: C -*- */
|
||||
|
||||
/* The main thing that this program does. */
|
||||
void main() {
|
||||
// Decralations
|
||||
int i;
|
||||
double A[5] = {
|
||||
9.0,
|
||||
2.9,
|
||||
3.E+25,
|
||||
.00007,
|
||||
};
|
||||
|
||||
// Doing some work
|
||||
for (i = 0; i < 5; ++i) {
|
||||
printf("element %d is %g, \tits square is %g\n",
|
||||
i,
|
||||
A[i],
|
||||
A[i]*A[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
24
modernc/01/getting-started.c
Normal file
24
modernc/01/getting-started.c
Normal file
@ -0,0 +1,24 @@
|
||||
/* This may look like nonsense, but really is -*- mode: C -*- */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* The main thing that this program does. */
|
||||
int main(void) {
|
||||
// Decralations
|
||||
double A[5] = {
|
||||
[0] = 9.0,
|
||||
[1] = 2.9,
|
||||
[4] = 3.E+25,
|
||||
[3] = .00007,
|
||||
};
|
||||
|
||||
// Doing some work
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
printf("element %zu is %g, \tits square is %g\n",
|
||||
i,
|
||||
A[i],
|
||||
A[i]*A[i]);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
2
modernc/03/.gitignore
vendored
Normal file
2
modernc/03/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
challenge-1
|
||||
heron
|
90
modernc/03/array.c
Normal file
90
modernc/03/array.c
Normal file
@ -0,0 +1,90 @@
|
||||
#include <stdlib.h>
|
||||
#include "array.h"
|
||||
|
||||
void IntArray_new(IntArray* a, size_t size) {
|
||||
a->arr = malloc(size * sizeof(int));
|
||||
a->used = 0;
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
int IntArray_append(IntArray* a, int el) {
|
||||
if (a->used == a->size) {
|
||||
a->size *= 2;
|
||||
a->arr = realloc(a->arr, a->size * sizeof(int));
|
||||
}
|
||||
|
||||
a->used++;
|
||||
|
||||
a->arr[a->used] = el;
|
||||
return a->used;
|
||||
}
|
||||
|
||||
int IntArray_get(IntArray* a, int idx, int dflt) {
|
||||
if (idx > a->used) {
|
||||
return dflt;
|
||||
}
|
||||
|
||||
return a->arr[idx];
|
||||
}
|
||||
|
||||
int IntArray_length(IntArray* a) {
|
||||
return a->used;
|
||||
}
|
||||
|
||||
void IntArray_mergesort(IntArray* a) {
|
||||
IntArray left_side;
|
||||
IntArray right_side;
|
||||
|
||||
size_t split = a->used / 2;
|
||||
|
||||
IntArray_new(&left_side, split);
|
||||
IntArray_new(&right_side, split);
|
||||
|
||||
for (int i = 0; i < split; ++i) {
|
||||
IntArray_append(&left_side, IntArray_get(a, i, 0));
|
||||
}
|
||||
|
||||
for (int i = split; i < a->used; ++i) {
|
||||
IntArray_append(&right_side, IntArray_get(a, i, 0));
|
||||
}
|
||||
|
||||
IntArray_mergesort(&left_side);
|
||||
IntArray_mergesort(&right_side);
|
||||
|
||||
IntArray_mergesort_merge(a, &left_side, &right_side);
|
||||
}
|
||||
|
||||
void IntArray_mergesort_merge(IntArray* a, IntArray* left_side, IntArray* right_side) {
|
||||
IntArray_free(a);
|
||||
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
|
||||
while (i < left_side->used && j < right_side->used) {
|
||||
if (left_side->arr[i] <= right_side->arr[j]) {
|
||||
IntArray_append(a, left_side->arr[i]);
|
||||
i++;
|
||||
} else {
|
||||
IntArray_append(a, right_side->arr[j]);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t li = 0; li < left_side->used; ++li) {
|
||||
IntArray_append(a, left_side->arr[li]);
|
||||
}
|
||||
|
||||
for (size_t ri = 0; ri < right_side->used; ++ri) {
|
||||
IntArray_append(a, right_side->arr[ri]);
|
||||
}
|
||||
}
|
||||
|
||||
void IntArray_quicksort(IntArray* a) {
|
||||
return;
|
||||
}
|
||||
|
||||
void IntArray_free(IntArray* a) {
|
||||
free(a->arr);
|
||||
a->arr = NULL;
|
||||
a->used = a->size = 0;
|
||||
}
|
22
modernc/03/array.h
Normal file
22
modernc/03/array.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef INCLUDED_ARRAY_H
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
int *arr;
|
||||
size_t used;
|
||||
size_t size;
|
||||
} IntArray;
|
||||
|
||||
void IntArray_new(IntArray*, size_t);
|
||||
|
||||
int IntArray_append(IntArray*, int);
|
||||
int IntArray_get(IntArray*, int, int);
|
||||
int IntArray_length(IntArray*);
|
||||
void IntArray_mergesort(IntArray*);
|
||||
void IntArray_mergesort_merge(IntArray*, IntArray*, IntArray*);
|
||||
void IntArray_quicksort(IntArray*);
|
||||
|
||||
void IntArray_free(IntArray*);
|
||||
|
||||
#define INCLUDED_ARRAY_H 1
|
||||
#endif
|
114
modernc/03/challenge-1.c
Normal file
114
modernc/03/challenge-1.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "array.h"
|
||||
|
||||
#define IODASH "-"
|
||||
|
||||
void usage(char* prog) {
|
||||
fprintf(stderr, "Usage: %s [-f|--input-file <input-file>] [-o|--output-file <output-file>]\n", prog);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char* input_file = IODASH;
|
||||
char* output_file = IODASH;
|
||||
|
||||
int c = 0;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"input-file", required_argument, 0, 'f'},
|
||||
{"output-file", required_argument, 0, 'o'},
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
|
||||
c = getopt_long(argc, argv, "f:o:", long_options, &option_index);
|
||||
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'f':
|
||||
input_file = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
output_file = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(input_file, IODASH) == 0) {
|
||||
fprintf(stderr, "(reading from stdin)\n");
|
||||
}
|
||||
|
||||
if (strcmp(output_file, IODASH) == 0) {
|
||||
fprintf(stderr, "(writing to stdout)\n");
|
||||
}
|
||||
|
||||
printf("input_file='%s' output_file='%s'\n", input_file, output_file);
|
||||
|
||||
FILE* instream;
|
||||
|
||||
instream = stdin;
|
||||
if (strcmp(input_file, IODASH) != 0) {
|
||||
instream = fopen(input_file, "r");
|
||||
}
|
||||
|
||||
if (instream == NULL) {
|
||||
perror("opening input file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
FILE* outstream;
|
||||
|
||||
outstream = stdout;
|
||||
if (strcmp(output_file, IODASH) != 0) {
|
||||
outstream = fopen(output_file, "r");
|
||||
}
|
||||
|
||||
if (outstream == NULL) {
|
||||
perror("opening output file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int line_int;
|
||||
ssize_t nread;
|
||||
|
||||
char* endptr = NULL;
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
IntArray accum;
|
||||
IntArray_new(&accum, 100);
|
||||
|
||||
while ((nread = getline(&line, &len, instream)) != -1) {
|
||||
IntArray_append(&accum, atoi(line));
|
||||
}
|
||||
|
||||
printf("Accumulated %i ints\n", IntArray_length(&accum));
|
||||
|
||||
IntArray_mergesort(&accum);
|
||||
|
||||
printf("Merge sorted:\n");
|
||||
|
||||
for (int i = 0; i < accum.used; ++i) {
|
||||
printf("%i: %i\n", i, IntArray_get(&accum, i, 0));
|
||||
}
|
||||
|
||||
if (strcmp(input_file, IODASH) != 0) {
|
||||
fclose(instream);
|
||||
}
|
||||
|
||||
if (strcmp(output_file, IODASH) != 0) {
|
||||
fclose(outstream);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
40
modernc/03/heron.c
Normal file
40
modernc/03/heron.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* lower and upper iteration limits centered around 1.0 */
|
||||
static double const eps1m01 = 1.0 - 0x1P-01;
|
||||
static double const eps1p01 = 1.0 + 0x1P-01;
|
||||
static double const eps1m24 = 1.0 - 0x1P-24;
|
||||
static double const eps1p24 = 1.0 + 0x1P-24;
|
||||
|
||||
int main(int argc, char* argv[argc+1]) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
double const a = strtod(argv[i], 0);
|
||||
double x = 1.0;
|
||||
|
||||
for (;;) {
|
||||
double prod = a*x;
|
||||
if (prod < eps1m01) {
|
||||
x *= 2.0;
|
||||
} else if (eps1p01 < prod) {
|
||||
x *= 0.5;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
double prod = a*x;
|
||||
if ((prod < eps1m24) || (eps1p24 < prod)) {
|
||||
x *= (2.0 - prod);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("heron: a=%.5e,\tx=%.5e,\ta*x=%.12f\n",
|
||||
a, x, a*x);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
1
modernc/05/.gitignore
vendored
Normal file
1
modernc/05/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
basic
|
9
modernc/05/basic.c
Normal file
9
modernc/05/basic.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
double x = 5.0;
|
||||
double y = 3.0;
|
||||
|
||||
x = (x * 1.5) - y;
|
||||
printf("x is \%g\n", x);
|
||||
}
|
1
personal-home-page/.gitignore
vendored
Normal file
1
personal-home-page/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/vendor/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user