next #1
					 12 changed files with 853 additions and 576 deletions
				
			
		
							
								
								
									
										2
									
								
								.envrc
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								.envrc
									
										
									
									
									
								
							|  | @ -1 +1 @@ | ||||||
| use nix | use flake | ||||||
|  |  | ||||||
							
								
								
									
										493
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										493
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -2,20 +2,11 @@ | ||||||
| # It is not intended for manual editing. | # It is not intended for manual editing. | ||||||
| version = 3 | version = 3 | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "aho-corasick" |  | ||||||
| version = "1.1.3" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" |  | ||||||
| dependencies = [ |  | ||||||
|  "memchr", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "anstream" | name = "anstream" | ||||||
| version = "0.6.14" | version = "0.6.18" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anstyle", |  "anstyle", | ||||||
|  "anstyle-parse", |  "anstyle-parse", | ||||||
|  | @ -28,49 +19,62 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "anstyle" | name = "anstyle" | ||||||
| version = "1.0.7" | version = "1.0.10" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "anstyle-parse" | name = "anstyle-parse" | ||||||
| version = "0.2.4" | version = "0.2.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "utf8parse", |  "utf8parse", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "anstyle-query" | name = "anstyle-query" | ||||||
| version = "1.0.3" | version = "1.1.2" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-sys 0.52.0", |  "windows-sys 0.59.0", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "anstyle-wincon" | name = "anstyle-wincon" | ||||||
| version = "3.0.3" | version = "3.0.7" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anstyle", |  "anstyle", | ||||||
|  "windows-sys 0.52.0", |  "once_cell", | ||||||
|  |  "windows-sys 0.59.0", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "autocfg" | name = "autocfg" | ||||||
| version = "1.3.0" | version = "1.4.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "bitflags" | name = "bitflags" | ||||||
| version = "2.5.0" | version = "2.9.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "bitvec" | ||||||
|  | version = "1.0.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" | ||||||
|  | dependencies = [ | ||||||
|  |  "funty", | ||||||
|  |  "radium", | ||||||
|  |  "tap", | ||||||
|  |  "wyz", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "cfg-if" | name = "cfg-if" | ||||||
|  | @ -80,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap" | name = "clap" | ||||||
| version = "4.5.4" | version = "4.5.37" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" | checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clap_builder", |  "clap_builder", | ||||||
|  "clap_derive", |  "clap_derive", | ||||||
|  | @ -90,9 +94,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap_builder" | name = "clap_builder" | ||||||
| version = "4.5.2" | version = "4.5.37" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" | checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anstream", |  "anstream", | ||||||
|  "anstyle", |  "anstyle", | ||||||
|  | @ -102,9 +106,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap_derive" | name = "clap_derive" | ||||||
| version = "4.5.4" | version = "4.5.32" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "heck", |  "heck", | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  | @ -114,27 +118,38 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap_lex" | name = "clap_lex" | ||||||
| version = "0.7.0" | version = "0.7.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "colorchoice" | name = "colorchoice" | ||||||
| version = "1.0.1" | version = "1.0.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "convert_case" | ||||||
|  | version = "0.7.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" | ||||||
|  | dependencies = [ | ||||||
|  |  "unicode-segmentation", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "crossterm" | name = "crossterm" | ||||||
| version = "0.27.0" | version = "0.29.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" | checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
|  "crossterm_winapi", |  "crossterm_winapi", | ||||||
|  "libc", |  "derive_more", | ||||||
|  |  "document-features", | ||||||
|  "mio", |  "mio", | ||||||
|  "parking_lot", |  "parking_lot", | ||||||
|  |  "rustix", | ||||||
|  "signal-hook", |  "signal-hook", | ||||||
|  "signal-hook-mio", |  "signal-hook-mio", | ||||||
|  "winapi", |  "winapi", | ||||||
|  | @ -150,33 +165,56 @@ dependencies = [ | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "env_filter" | name = "derive_more" | ||||||
| version = "0.1.0" | version = "2.0.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" | checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "log", |  "derive_more-impl", | ||||||
|  "regex", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "env_logger" | name = "derive_more-impl" | ||||||
| version = "0.11.3" | version = "2.0.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" | checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anstream", |  "convert_case", | ||||||
|  "anstyle", |  "proc-macro2", | ||||||
|  "env_filter", |  "quote", | ||||||
|  "humantime", |  "syn", | ||||||
|  "log", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "document-features" | ||||||
|  | version = "0.2.11" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" | ||||||
|  | dependencies = [ | ||||||
|  |  "litrs", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "errno" | ||||||
|  | version = "0.3.11" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  |  "windows-sys 0.59.0", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "funty" | ||||||
|  | version = "2.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "getrandom" | name = "getrandom" | ||||||
| version = "0.2.15" | version = "0.2.16" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "libc", |  "libc", | ||||||
|  | @ -189,23 +227,29 @@ version = "0.5.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "humantime" |  | ||||||
| version = "2.1.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "is_terminal_polyfill" | name = "is_terminal_polyfill" | ||||||
| version = "1.70.0" | version = "1.70.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "libc" | name = "libc" | ||||||
| version = "0.2.155" | version = "0.2.172" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "linux-raw-sys" | ||||||
|  | version = "0.9.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "litrs" | ||||||
|  | version = "0.4.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "lock_api" | name = "lock_api" | ||||||
|  | @ -219,33 +263,33 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "log" | name = "log" | ||||||
| version = "0.4.21" | version = "0.4.27" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "memchr" |  | ||||||
| version = "2.7.2" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "mio" | name = "mio" | ||||||
| version = "0.8.11" | version = "1.0.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "log", |  "log", | ||||||
|  "wasi", |  "wasi", | ||||||
|  "windows-sys 0.48.0", |  "windows-sys 0.52.0", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "parking_lot" | name = "once_cell" | ||||||
| version = "0.12.2" | version = "1.21.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "parking_lot" | ||||||
|  | version = "0.12.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "lock_api", |  "lock_api", | ||||||
|  "parking_lot_core", |  "parking_lot_core", | ||||||
|  | @ -261,49 +305,58 @@ dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
|  "smallvec", |  "smallvec", | ||||||
|  "windows-targets 0.52.5", |  "windows-targets", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ppv-lite86" | name = "ppv-lite86" | ||||||
| version = "0.2.17" | version = "0.2.21" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" | ||||||
|  | dependencies = [ | ||||||
|  |  "zerocopy", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro2" | name = "proc-macro2" | ||||||
| version = "1.0.82" | version = "1.0.95" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "unicode-ident", |  "unicode-ident", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "quote" | name = "quote" | ||||||
| version = "1.0.36" | version = "1.0.40" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "rand" | name = "radium" | ||||||
| version = "0.9.0-alpha.1" | version = "0.7.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8d31e63ea85be51c423e52ba8f2e68a3efd53eed30203ee029dd09947333693e" | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand" | ||||||
|  | version = "0.8.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  "rand_chacha", |  "rand_chacha", | ||||||
|  "rand_core", |  "rand_core", | ||||||
|  "zerocopy", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "rand_chacha" | name = "rand_chacha" | ||||||
| version = "0.9.0-alpha.1" | version = "0.3.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "78674ef918c19451dbd250f8201f8619b494f64c9aa6f3adb28fd8a0f1f6da46" | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ppv-lite86", |  "ppv-lite86", | ||||||
|  "rand_core", |  "rand_core", | ||||||
|  | @ -311,52 +364,35 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "rand_core" | name = "rand_core" | ||||||
| version = "0.9.0-alpha.1" | version = "0.6.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "cc89dffba8377c5ec847d12bb41492bda235dba31a25e8b695cd0fe6589eb8c9" | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "getrandom", |  "getrandom", | ||||||
|  "zerocopy", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "redox_syscall" | name = "redox_syscall" | ||||||
| version = "0.5.1" | version = "0.5.11" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" | checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "regex" | name = "rustix" | ||||||
| version = "1.10.4" | version = "1.0.7" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" | checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "aho-corasick", |  "bitflags", | ||||||
|  "memchr", |  "errno", | ||||||
|  "regex-automata", |  "libc", | ||||||
|  "regex-syntax", |  "linux-raw-sys", | ||||||
|  |  "windows-sys 0.59.0", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "regex-automata" |  | ||||||
| version = "0.4.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" |  | ||||||
| dependencies = [ |  | ||||||
|  "aho-corasick", |  | ||||||
|  "memchr", |  | ||||||
|  "regex-syntax", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "regex-syntax" |  | ||||||
| version = "0.8.3" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "scopeguard" | name = "scopeguard" | ||||||
| version = "1.2.0" | version = "1.2.0" | ||||||
|  | @ -364,24 +400,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "servicepoint-life" | name = "servicepoint" | ||||||
| version = "0.1.0" | version = "0.14.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0ce70bae3641ccafdeb9832ac367efd51243e0708ef35151ad8c2c4ee578aa4a" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clap", |  "bitvec", | ||||||
|  "crossterm", |  | ||||||
|  "env_logger", |  | ||||||
|  "log", |  "log", | ||||||
|  "rand", |  "rand", | ||||||
|  "servicepoint2", |  "thiserror", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "servicepoint2" | name = "servicepoint-life" | ||||||
| version = "0.4.2" | version = "0.2.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "94d9aecc4d31a71578481de6c6d64383d374126c38469c9689067579c1d910fd" |  | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "log", |  "clap", | ||||||
|  |  "crossterm", | ||||||
|  |  "rand", | ||||||
|  |  "servicepoint", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -396,9 +433,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "signal-hook-mio" | name = "signal-hook-mio" | ||||||
| version = "0.2.3" | version = "0.2.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "mio", |  "mio", | ||||||
|  | @ -407,18 +444,18 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "signal-hook-registry" | name = "signal-hook-registry" | ||||||
| version = "1.4.2" | version = "1.4.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" | checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "smallvec" | name = "smallvec" | ||||||
| version = "1.13.2" | version = "1.15.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "strsim" | name = "strsim" | ||||||
|  | @ -428,9 +465,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "syn" | name = "syn" | ||||||
| version = "2.0.64" | version = "2.0.101" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
|  | @ -438,16 +475,48 @@ dependencies = [ | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-ident" | name = "tap" | ||||||
| version = "1.0.12" | version = "1.0.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "thiserror" | ||||||
|  | version = "2.0.12" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" | ||||||
|  | dependencies = [ | ||||||
|  |  "thiserror-impl", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "thiserror-impl" | ||||||
|  | version = "2.0.12" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-ident" | ||||||
|  | version = "1.0.18" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-segmentation" | ||||||
|  | version = "1.12.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "utf8parse" | name = "utf8parse" | ||||||
| version = "0.2.1" | version = "0.2.2" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "wasi" | name = "wasi" | ||||||
|  | @ -477,159 +546,111 @@ version = "0.4.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows-sys" |  | ||||||
| version = "0.48.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" |  | ||||||
| dependencies = [ |  | ||||||
|  "windows-targets 0.48.5", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-sys" | name = "windows-sys" | ||||||
| version = "0.52.0" | version = "0.52.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-targets 0.52.5", |  "windows-targets", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "windows-sys" | ||||||
|  | version = "0.59.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" | ||||||
|  | dependencies = [ | ||||||
|  |  "windows-targets", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-targets" | name = "windows-targets" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows_aarch64_gnullvm 0.48.5", |  "windows_aarch64_gnullvm", | ||||||
|  "windows_aarch64_msvc 0.48.5", |  "windows_aarch64_msvc", | ||||||
|  "windows_i686_gnu 0.48.5", |  "windows_i686_gnu", | ||||||
|  "windows_i686_msvc 0.48.5", |  | ||||||
|  "windows_x86_64_gnu 0.48.5", |  | ||||||
|  "windows_x86_64_gnullvm 0.48.5", |  | ||||||
|  "windows_x86_64_msvc 0.48.5", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows-targets" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" |  | ||||||
| dependencies = [ |  | ||||||
|  "windows_aarch64_gnullvm 0.52.5", |  | ||||||
|  "windows_aarch64_msvc 0.52.5", |  | ||||||
|  "windows_i686_gnu 0.52.5", |  | ||||||
|  "windows_i686_gnullvm", |  "windows_i686_gnullvm", | ||||||
|  "windows_i686_msvc 0.52.5", |  "windows_i686_msvc", | ||||||
|  "windows_x86_64_gnu 0.52.5", |  "windows_x86_64_gnu", | ||||||
|  "windows_x86_64_gnullvm 0.52.5", |  "windows_x86_64_gnullvm", | ||||||
|  "windows_x86_64_msvc 0.52.5", |  "windows_x86_64_msvc", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_aarch64_gnullvm" | name = "windows_aarch64_gnullvm" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_aarch64_gnullvm" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_aarch64_msvc" | name = "windows_aarch64_msvc" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_aarch64_msvc" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_i686_gnu" | name = "windows_i686_gnu" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_i686_gnu" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_i686_gnullvm" | name = "windows_i686_gnullvm" | ||||||
| version = "0.52.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_i686_msvc" | name = "windows_i686_msvc" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_i686_msvc" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_gnu" | name = "windows_x86_64_gnu" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_x86_64_gnu" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_gnullvm" | name = "windows_x86_64_gnullvm" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_x86_64_gnullvm" |  | ||||||
| version = "0.52.5" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_msvc" | name = "windows_x86_64_msvc" | ||||||
| version = "0.48.5" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_msvc" | name = "wyz" | ||||||
| version = "0.52.5" | version = "0.5.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" | ||||||
|  | dependencies = [ | ||||||
|  |  "tap", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "zerocopy" | name = "zerocopy" | ||||||
| version = "0.8.0-alpha.6" | version = "0.8.25" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "db678a6ee512bd06adf35c35be471cae2f9c82a5aed2b5d15e03628c98bddd57" | checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "zerocopy-derive", |  "zerocopy-derive", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "zerocopy-derive" | name = "zerocopy-derive" | ||||||
| version = "0.8.0-alpha.6" | version = "0.8.25" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "201585ea96d37ee69f2ac769925ca57160cef31acb137c16f38b02b76f4c1e62" | checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								Cargo.toml
									
										
									
									
									
								
							
							
						
						
									
										36
									
								
								Cargo.toml
									
										
									
									
									
								
							|  | @ -1,12 +1,34 @@ | ||||||
| [package] | [package] | ||||||
| name = "servicepoint-life" | name = "servicepoint-life" | ||||||
| version = "0.1.0" | version = "0.2.0" | ||||||
| edition = "2021" | edition = "2021" | ||||||
|  | license = "GPL-3.0-or-later" | ||||||
|  | description = "Small terminal app that generates conways game of life rules and simulates them on the servicepoint display." | ||||||
|  | repository = "https://git.berlin.ccc.de/vinzenz/servicepoint-life" | ||||||
|  | readme = "README.md" | ||||||
|  | keywords = ["cccb", "cccb-servicepoint", "conway", "game-of-life", "simulation"] | ||||||
|  | rust-version = "1.70.0" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| clap = { version = "4.5.4", features = ["derive"] } | clap = { version = "4.5", features = ["derive"] } | ||||||
| rand = "0.9.0-alpha.1" | rand = "0.8" | ||||||
| env_logger = "0.11.3" | crossterm = "0.29" | ||||||
| servicepoint2 = { version = "0.4.2", default-features = false } | 
 | ||||||
| crossterm = "0.27.0" | [dependencies.servicepoint] | ||||||
| log = "0.4.21" | package = "servicepoint" | ||||||
|  | version = "0.14.0" | ||||||
|  | features = ["rand"] | ||||||
|  | default-features = false | ||||||
|  | 
 | ||||||
|  | [lints.clippy] | ||||||
|  | incompatible_msrv = "forbid" | ||||||
|  | 
 | ||||||
|  | [profile.release] | ||||||
|  | lto = true          # Enable link-time optimization | ||||||
|  | codegen-units = 1   # Reduce number of codegen units to increase optimizations | ||||||
|  | 
 | ||||||
|  | [profile.size-optimized] | ||||||
|  | inherits = "release" | ||||||
|  | opt-level = 'z'     # Optimize for size | ||||||
|  | panic = 'abort'     # Abort on panic | ||||||
|  | strip = true        # Strip symbols from binary | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,5 +1,13 @@ | ||||||
| # servicepoint-life | # servicepoint-life | ||||||
| 
 | 
 | ||||||
| More fully featured game of life for the servicepoint display based on the example in the repo. | This is a small terminal app that generates conways game of life rules and simulates them on the servicepoint display. | ||||||
| 
 | 
 | ||||||
|  | It uses the [servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint/) library for the display commands. | ||||||
| 
 | 
 | ||||||
|  | ## Running | ||||||
|  | 
 | ||||||
|  | With Nix flakes, you can just `nix run git+https://git.berlin.ccc.de/vinzenz/servicepoint-life`. | ||||||
|  | 
 | ||||||
|  | Otherwise, you can `cargo install --git https://git.berlin.ccc.de/vinzenz/servicepoint-life` and then run `servicepoint-life`. | ||||||
|  | 
 | ||||||
|  | If you want to poke at the code you can always check out the repo and `cargo run` it. | ||||||
|  |  | ||||||
							
								
								
									
										64
									
								
								flake.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								flake.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | { | ||||||
|  |   "nodes": { | ||||||
|  |     "naersk": { | ||||||
|  |       "inputs": { | ||||||
|  |         "nixpkgs": [ | ||||||
|  |           "nixpkgs" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1745925850, | ||||||
|  |         "narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=", | ||||||
|  |         "owner": "nix-community", | ||||||
|  |         "repo": "naersk", | ||||||
|  |         "rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "nix-community", | ||||||
|  |         "repo": "naersk", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "nix-filter": { | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1731533336, | ||||||
|  |         "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=", | ||||||
|  |         "owner": "numtide", | ||||||
|  |         "repo": "nix-filter", | ||||||
|  |         "rev": "f7653272fd234696ae94229839a99b73c9ab7de0", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "numtide", | ||||||
|  |         "repo": "nix-filter", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "nixpkgs": { | ||||||
|  |       "locked": { | ||||||
|  |         "lastModified": 1746183838, | ||||||
|  |         "narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=", | ||||||
|  |         "owner": "nixos", | ||||||
|  |         "repo": "nixpkgs", | ||||||
|  |         "rev": "bf3287dac860542719fe7554e21e686108716879", | ||||||
|  |         "type": "github" | ||||||
|  |       }, | ||||||
|  |       "original": { | ||||||
|  |         "owner": "nixos", | ||||||
|  |         "ref": "nixos-24.11", | ||||||
|  |         "repo": "nixpkgs", | ||||||
|  |         "type": "github" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "root": { | ||||||
|  |       "inputs": { | ||||||
|  |         "naersk": "naersk", | ||||||
|  |         "nix-filter": "nix-filter", | ||||||
|  |         "nixpkgs": "nixpkgs" | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "root": "root", | ||||||
|  |   "version": 7 | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								flake.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								flake.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | ||||||
|  | { | ||||||
|  |   description = "Flake for servicepoint-life"; | ||||||
|  | 
 | ||||||
|  |   inputs = { | ||||||
|  |     nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; | ||||||
|  |     nix-filter.url = "github:numtide/nix-filter"; | ||||||
|  |     naersk = { | ||||||
|  |       url = "github:nix-community/naersk"; | ||||||
|  |       inputs.nixpkgs.follows = "nixpkgs"; | ||||||
|  |     }; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   outputs = | ||||||
|  |     inputs@{ | ||||||
|  |       self, | ||||||
|  |       nixpkgs, | ||||||
|  |       naersk, | ||||||
|  |       nix-filter, | ||||||
|  |     }: | ||||||
|  |     let | ||||||
|  |       lib = nixpkgs.lib; | ||||||
|  |       supported-systems = [ | ||||||
|  |         "x86_64-linux" | ||||||
|  |         "aarch64-linux" | ||||||
|  |         "aarch64-darwin" | ||||||
|  |         "x86_64-darwin" | ||||||
|  |       ]; | ||||||
|  |       forAllSystems = | ||||||
|  |         f: | ||||||
|  |         lib.genAttrs supported-systems ( | ||||||
|  |           system: | ||||||
|  |           f rec { | ||||||
|  |             pkgs = nixpkgs.legacyPackages.${system}; | ||||||
|  |             naersk' = pkgs.callPackage naersk { }; | ||||||
|  |             selfPkgs = self.packages."${system}"; | ||||||
|  |             inherit system; | ||||||
|  |           } | ||||||
|  |         ); | ||||||
|  |     in | ||||||
|  |     rec { | ||||||
|  |       packages = forAllSystems ( | ||||||
|  |         { pkgs, naersk', ... }: | ||||||
|  |         rec { | ||||||
|  |           servicepoint-life = naersk'.buildPackage rec { | ||||||
|  |             strictDeps = true; | ||||||
|  |             nativeBuildInputs = with pkgs; [ pkg-config ]; | ||||||
|  |             buildInputs = with pkgs; [ | ||||||
|  |               xe | ||||||
|  |               xz | ||||||
|  |             ]; | ||||||
|  |             src = nix-filter.lib.filter { | ||||||
|  |               root = ./.; | ||||||
|  |               include = [ | ||||||
|  |                 ./Cargo.toml | ||||||
|  |                 ./Cargo.lock | ||||||
|  |                 ./src | ||||||
|  |                 ./README.md | ||||||
|  |                 ./LICENSE | ||||||
|  |               ]; | ||||||
|  |             }; | ||||||
|  |           }; | ||||||
|  | 
 | ||||||
|  |           default = servicepoint-life; | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       legacyPackages = packages; | ||||||
|  | 
 | ||||||
|  |       apps = forAllSystems ( | ||||||
|  |         { selfPkgs, ... }: | ||||||
|  |         rec { | ||||||
|  |           servicepoint-life = { | ||||||
|  |             type = "app"; | ||||||
|  |             program = "${selfPkgs.servicepoint-life}/bin/servicepoint-life"; | ||||||
|  |           }; | ||||||
|  |           default = servicepoint-life; | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       devShells = forAllSystems ( | ||||||
|  |         { pkgs, selfPkgs, ... }: | ||||||
|  |         { | ||||||
|  |           default = pkgs.mkShell rec { | ||||||
|  |             inputsFrom = [ selfPkgs.default ]; | ||||||
|  |             packages = [ | ||||||
|  |               pkgs.gdb | ||||||
|  |               (pkgs.symlinkJoin { | ||||||
|  |                 name = "rust-toolchain"; | ||||||
|  |                 paths = with pkgs; [ | ||||||
|  |                   rustc | ||||||
|  |                   cargo | ||||||
|  |                   rustfmt | ||||||
|  |                   clippy | ||||||
|  |                 ]; | ||||||
|  |               }) | ||||||
|  |             ]; | ||||||
|  |           }; | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-rfc-style); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| { pkgs ? import <nixpkgs> {} }: |  | ||||||
|   pkgs.mkShell { |  | ||||||
|     nativeBuildInputs = with pkgs.buildPackages; [ rustup cargo pkg-config xe lzma ]; |  | ||||||
| } |  | ||||||
							
								
								
									
										153
									
								
								src/app.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/app.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | ||||||
|  | use crate::{ | ||||||
|  |     print::{println_debug, println_info, println_warning}, | ||||||
|  |     simulation::{Simulation, SimulationEvent}, | ||||||
|  |     Cli, | ||||||
|  | }; | ||||||
|  | use crossterm::{ | ||||||
|  |     event, | ||||||
|  |     event::{Event, KeyCode, KeyEvent, KeyEventKind}, | ||||||
|  |     execute, | ||||||
|  |     terminal::{ | ||||||
|  |         disable_raw_mode, enable_raw_mode, EnableLineWrap, EnterAlternateScreen, | ||||||
|  |         LeaveAlternateScreen, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | use servicepoint::{ | ||||||
|  |     Bitmap, BitmapCommand, BrightnessGrid, BrightnessGridCommand, UdpSocketExt, | ||||||
|  |     FRAME_PACING, TILE_HEIGHT, TILE_WIDTH, | ||||||
|  | }; | ||||||
|  | use std::{ | ||||||
|  |     io::stdout, | ||||||
|  |     net::UdpSocket, | ||||||
|  |     thread, | ||||||
|  |     time::{Duration, Instant}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub(crate) struct App { | ||||||
|  |     connection: UdpSocket, | ||||||
|  |     sim: Simulation, | ||||||
|  |     target_duration: Duration, | ||||||
|  |     pixels: Bitmap, | ||||||
|  |     luma: BrightnessGrid, | ||||||
|  |     terminated: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl App { | ||||||
|  |     pub fn new(cli: Cli) -> Self { | ||||||
|  |         let connection = UdpSocket::bind_connect(cli.destination) | ||||||
|  |             .expect("Could not connect. Did you forget `--destination`?"); | ||||||
|  | 
 | ||||||
|  |         execute!(stdout(), EnterAlternateScreen, EnableLineWrap) | ||||||
|  |             .expect("could not enter alternate screen"); | ||||||
|  |         enable_raw_mode().expect("could not enable raw terminal mode"); | ||||||
|  | 
 | ||||||
|  |         Self { | ||||||
|  |             connection, | ||||||
|  |             sim: Simulation::new(), | ||||||
|  |             terminated: false, | ||||||
|  |             target_duration: FRAME_PACING * 4, | ||||||
|  |             pixels: Bitmap::max_sized(), | ||||||
|  |             luma: BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn run_iteration(&mut self) { | ||||||
|  |         let start = Instant::now(); | ||||||
|  |         self.sim.run_iteration(); | ||||||
|  | 
 | ||||||
|  |         self.sim.draw_state(&mut self.pixels, &mut self.luma); | ||||||
|  |         let cmd: BitmapCommand = self.pixels.clone().into(); | ||||||
|  |         self.connection.send_command(cmd).unwrap(); | ||||||
|  |         let cmd: BrightnessGridCommand = self.luma.clone().into(); | ||||||
|  |         self.connection.send_command(cmd).unwrap(); | ||||||
|  | 
 | ||||||
|  |         self.poll_events(); | ||||||
|  | 
 | ||||||
|  |         let tick_time = start.elapsed(); | ||||||
|  |         if tick_time < self.target_duration { | ||||||
|  |             thread::sleep(self.target_duration - tick_time); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn terminated(&self) -> bool { | ||||||
|  |         self.terminated | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn poll_events(&mut self) -> bool { | ||||||
|  |         while event::poll(Duration::from_secs(0)).expect("could not poll") { | ||||||
|  |             let event = event::read().expect("could not read event"); | ||||||
|  | 
 | ||||||
|  |             if let Event::Key(KeyEvent { | ||||||
|  |                 kind: KeyEventKind::Press, | ||||||
|  |                 code, | ||||||
|  |                 .. | ||||||
|  |             }) = event | ||||||
|  |             { | ||||||
|  |                 if let Some(sim_event) = self.handle_key(code) { | ||||||
|  |                     self.sim.handle_event(sim_event); | ||||||
|  |                 }; | ||||||
|  |             } else { | ||||||
|  |                 println_debug(format!("unhandled event {event:?}")); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn handle_key(&mut self, code: KeyCode) -> Option<SimulationEvent> { | ||||||
|  |         match code { | ||||||
|  |             KeyCode::Char('d') => Some(SimulationEvent::RandomizeLeftPixels), | ||||||
|  |             KeyCode::Char('e') => Some(SimulationEvent::RandomizeLeftLuma), | ||||||
|  |             KeyCode::Char('f') => Some(SimulationEvent::RandomizeRightPixels), | ||||||
|  |             KeyCode::Char('r') => Some(SimulationEvent::RandomizeRightLuma), | ||||||
|  |             KeyCode::Right => Some(SimulationEvent::SeparatorAccelerate), | ||||||
|  |             KeyCode::Left => Some(SimulationEvent::SeparatorDecelerate), | ||||||
|  |             KeyCode::Char('h') => { | ||||||
|  |                 println_info("[h] help"); | ||||||
|  |                 println_info("[q] quit"); | ||||||
|  |                 println_info("[d] randomize left pixels"); | ||||||
|  |                 println_info("[e] randomize left luma"); | ||||||
|  |                 println_info("[r] randomize right pixels"); | ||||||
|  |                 println_info("[f] randomize right luma"); | ||||||
|  |                 println_info("[→] accelerate divider right"); | ||||||
|  |                 println_info("[←] accelerate divider left"); | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |             KeyCode::Char('q') => { | ||||||
|  |                 println_warning("terminating"); | ||||||
|  |                 self.terminated = true; | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |             KeyCode::Up => { | ||||||
|  |                 self.target_duration = self | ||||||
|  |                     .target_duration | ||||||
|  |                     .saturating_sub(Duration::from_millis(1)); | ||||||
|  |                 println_info(format!( | ||||||
|  |                     "increased simulation speed to {} ups", | ||||||
|  |                     1f64 / self.target_duration.as_secs_f64() | ||||||
|  |                 )); | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |             KeyCode::Down => { | ||||||
|  |                 self.target_duration = self | ||||||
|  |                     .target_duration | ||||||
|  |                     .saturating_add(Duration::from_millis(1)); | ||||||
|  |                 println_info(format!( | ||||||
|  |                     "decreased simulation speed to {} ups", | ||||||
|  |                     1f64 / self.target_duration.as_secs_f64() | ||||||
|  |                 )); | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |             key_code => { | ||||||
|  |                 println_debug(format!("unhandled KeyCode {key_code:?}")); | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for App { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         disable_raw_mode().expect("could not disable raw terminal mode"); | ||||||
|  |         execute!(stdout(), LeaveAlternateScreen).expect("could not leave alternate screen"); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/game.rs
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								src/game.rs
									
										
									
									
									
								
							|  | @ -1,29 +1,21 @@ | ||||||
| use servicepoint2::Grid; | use servicepoint::{Grid, Value, ValueGrid}; | ||||||
| 
 | 
 | ||||||
| use crate::rules::Rules; | use crate::rules::Rules; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct Game<TState, TGrid, TKernel, const KERNEL_SIZE: usize> | pub(crate) struct Game<T: Value> | ||||||
| where |  | ||||||
|     TGrid: Grid<TState>, |  | ||||||
|     TState: Copy + PartialEq, |  | ||||||
|     TKernel: Copy, |  | ||||||
| { | { | ||||||
|     pub field: TGrid, |     pub field: ValueGrid<T>, | ||||||
|     pub rules: Rules<TState, TKernel, KERNEL_SIZE>, |     pub rules: Rules<T>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<TState, TGrid, TKernel, const KERNEL_SIZE: usize> Game<TState, TGrid, TKernel, KERNEL_SIZE> | impl<T: Value> Game<T> | ||||||
| where |  | ||||||
|     TGrid: Grid<TState>, |  | ||||||
|     TState: Copy + PartialEq, |  | ||||||
|     TKernel: Copy, |  | ||||||
| { | { | ||||||
|     pub fn step(&mut self) { |     pub fn step(&mut self) { | ||||||
|         self.field = self.field_iteration(); |         self.field = self.field_iteration(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn field_iteration(&self) -> TGrid { |     fn field_iteration(&self) -> ValueGrid<T> { | ||||||
|         let mut next = TGrid::new(self.field.width(), self.field.height()); |         let mut next = ValueGrid::new(self.field.width(), self.field.height()); | ||||||
|         for x in 0..self.field.width() { |         for x in 0..self.field.width() { | ||||||
|             for y in 0..self.field.height() { |             for y in 0..self.field.height() { | ||||||
|                 let old_state = self.field.get(x, y); |                 let old_state = self.field.get(x, y); | ||||||
|  | @ -41,14 +33,12 @@ where | ||||||
|         let mut count = 0; |         let mut count = 0; | ||||||
| 
 | 
 | ||||||
|         let kernel = &self.rules.kernel; |         let kernel = &self.rules.kernel; | ||||||
|         assert_eq!(KERNEL_SIZE % 2, 1); |  | ||||||
|         let offset = KERNEL_SIZE as i32 / 2; |  | ||||||
| 
 | 
 | ||||||
|         for (kernel_y, kernel_row) in kernel.iter().enumerate() { |         for (kernel_y, kernel_row) in kernel.iter().enumerate() { | ||||||
|             let offset_y = kernel_y as i32 - offset; |             let offset_y = kernel_y as i32 - 1; | ||||||
| 
 | 
 | ||||||
|             for (kernel_x, kernel_value) in kernel_row.iter().enumerate() { |             for (kernel_x, kernel_value) in kernel_row.iter().enumerate() { | ||||||
|                 let offset_x = kernel_x as i32 - offset; |                 let offset_x = kernel_x as i32 - 1; | ||||||
|                 let neighbor_x = x + offset_x; |                 let neighbor_x = x + offset_x; | ||||||
|                 let neighbor_y = y + offset_y; |                 let neighbor_y = y + offset_y; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										281
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										281
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -1,30 +1,11 @@ | ||||||
| use std::io::stdout; | use crate::app::App; | ||||||
| use std::num::Wrapping; |  | ||||||
| use std::thread; |  | ||||||
| use std::time::{Duration, Instant}; |  | ||||||
| 
 |  | ||||||
| use clap::Parser; | use clap::Parser; | ||||||
| use crossterm::{event, execute}; |  | ||||||
| use crossterm::event::{Event, KeyCode, KeyEventKind}; |  | ||||||
| use crossterm::terminal::{ |  | ||||||
|     disable_raw_mode, enable_raw_mode, EnableLineWrap, EnterAlternateScreen, LeaveAlternateScreen, |  | ||||||
| }; |  | ||||||
| use log::LevelFilter; |  | ||||||
| use rand::distributions::{Distribution, Standard}; |  | ||||||
| use rand::Rng; |  | ||||||
| use servicepoint2::{ |  | ||||||
|     ByteGrid, CompressionCode, Connection, FRAME_PACING, Grid, Origin, PixelGrid, TILE_HEIGHT, |  | ||||||
|     TILE_WIDTH, |  | ||||||
| }; |  | ||||||
| use servicepoint2::Command::{BitmapLinearWin, CharBrightness}; |  | ||||||
| 
 |  | ||||||
| use crate::game::Game; |  | ||||||
| use crate::print::{println_debug, println_info, println_warning}; |  | ||||||
| use crate::rules::{generate_bb3, generate_u8b3}; |  | ||||||
| 
 | 
 | ||||||
|  | mod app; | ||||||
| mod game; | mod game; | ||||||
| mod print; | mod print; | ||||||
| mod rules; | mod rules; | ||||||
|  | mod simulation; | ||||||
| 
 | 
 | ||||||
| #[derive(Parser, Debug)] | #[derive(Parser, Debug)] | ||||||
| struct Cli { | struct Cli { | ||||||
|  | @ -33,258 +14,8 @@ struct Cli { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|     let connection = init(); |     let mut app = App::new(Cli::parse()); | ||||||
| 
 |     while !app.terminated() { | ||||||
|     let mut left_pixels = Game { |         app.run_iteration(); | ||||||
|         rules: generate_bb3(), |  | ||||||
|         field: PixelGrid::max_sized(), |  | ||||||
|     }; |  | ||||||
|     let mut right_pixels = Game { |  | ||||||
|         rules: generate_bb3(), |  | ||||||
|         field: PixelGrid::max_sized(), |  | ||||||
|     }; |  | ||||||
|     let mut left_luma = Game { |  | ||||||
|         rules: generate_u8b3(), |  | ||||||
|         field: ByteGrid::new(TILE_WIDTH, TILE_HEIGHT), |  | ||||||
|     }; |  | ||||||
|     let mut right_luma = Game { |  | ||||||
|         rules: generate_u8b3(), |  | ||||||
|         field: ByteGrid::new(TILE_WIDTH, TILE_HEIGHT), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     randomize(&mut left_luma.field); |  | ||||||
|     randomize(&mut left_pixels.field); |  | ||||||
|     randomize(&mut right_luma.field); |  | ||||||
|     randomize(&mut right_pixels.field); |  | ||||||
| 
 |  | ||||||
|     let mut pixels = PixelGrid::max_sized(); |  | ||||||
|     let mut luma = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT); |  | ||||||
| 
 |  | ||||||
|     let mut split_pixel = 0; |  | ||||||
|     let mut split_speed: i32 = 1; |  | ||||||
| 
 |  | ||||||
|     let mut iteration = Wrapping(0u8); |  | ||||||
| 
 |  | ||||||
|     let mut target_duration = FRAME_PACING; |  | ||||||
| 
 |  | ||||||
|     loop { |  | ||||||
|         let start = Instant::now(); |  | ||||||
| 
 |  | ||||||
|         left_pixels.step(); |  | ||||||
|         right_pixels.step(); |  | ||||||
| 
 |  | ||||||
|         if iteration % Wrapping(10) == Wrapping(0) { |  | ||||||
|             left_luma.step(); |  | ||||||
|             right_luma.step(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         iteration += Wrapping(1u8); |  | ||||||
| 
 |  | ||||||
|         if split_speed > 0 && split_pixel == pixels.width() { |  | ||||||
|             split_pixel = 0; |  | ||||||
| 
 |  | ||||||
|             (left_luma, right_luma) = (right_luma, left_luma); |  | ||||||
|             (left_pixels, right_pixels) = (right_pixels, left_pixels); |  | ||||||
| 
 |  | ||||||
|             randomize(&mut left_pixels.field); |  | ||||||
|             randomize(&mut left_luma.field); |  | ||||||
|             left_pixels.rules = generate_bb3(); |  | ||||||
|             left_luma.rules = generate_u8b3(); |  | ||||||
|         } else if split_speed < 0 && split_pixel == 0 { |  | ||||||
|             split_pixel = pixels.width(); |  | ||||||
| 
 |  | ||||||
|             (left_luma, right_luma) = (right_luma, left_luma); |  | ||||||
|             (left_pixels, right_pixels) = (right_pixels, left_pixels); |  | ||||||
| 
 |  | ||||||
|             randomize(&mut right_pixels.field); |  | ||||||
|             randomize(&mut right_luma.field); |  | ||||||
|             right_pixels.rules = generate_bb3(); |  | ||||||
|             right_luma.rules = generate_u8b3(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         split_pixel = |  | ||||||
|             i32::clamp(split_pixel as i32 + split_speed, 0, pixels.width() as i32) as usize; |  | ||||||
| 
 |  | ||||||
|         draw_pixels( |  | ||||||
|             &mut pixels, |  | ||||||
|             &left_pixels.field, |  | ||||||
|             &right_pixels.field, |  | ||||||
|             split_pixel, |  | ||||||
|         ); |  | ||||||
|         draw_luma( |  | ||||||
|             &mut luma, |  | ||||||
|             &left_luma.field, |  | ||||||
|             &right_luma.field, |  | ||||||
|             split_pixel / 8, |  | ||||||
|         ); |  | ||||||
|         send_to_screen(&connection, &pixels, &luma); |  | ||||||
| 
 |  | ||||||
|         while event::poll(Duration::from_secs(0)).expect("could not poll") { |  | ||||||
|             match event::read().expect("could not read event").try_into() { |  | ||||||
|                 Err(_) => {} |  | ||||||
|                 Ok(AppEvent::RandomizeLeftPixels) => { |  | ||||||
|                     randomize(&mut left_pixels.field); |  | ||||||
|                     println_debug("randomized left pixels"); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::RandomizeRightPixels) => { |  | ||||||
|                     randomize(&mut right_pixels.field); |  | ||||||
|                     println_info("randomized right pixels"); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::RandomizeLeftLuma) => { |  | ||||||
|                     randomize(&mut left_luma.field); |  | ||||||
|                     println_info("randomized left luma"); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::RandomizeRightLuma) => { |  | ||||||
|                     randomize(&mut right_luma.field); |  | ||||||
|                     println_info("randomized right luma"); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::SeparatorAccelerate) => { |  | ||||||
|                     split_speed += 1; |  | ||||||
|                     println_info(format!("increased separator speed to {split_speed}")); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::SeparatorDecelerate) => { |  | ||||||
|                     split_speed -= 1; |  | ||||||
|                     println_info(format!("decreased separator speed to {split_speed}")); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::Close) => { |  | ||||||
|                     println_warning("terminating"); |  | ||||||
|                     de_init(); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::SimulationSpeedUp) => { |  | ||||||
|                     target_duration = target_duration.saturating_sub(Duration::from_millis(1)); |  | ||||||
|                     println_info(format!("increased simulation speed to {} ups", 1f64 / target_duration.as_secs_f64())); |  | ||||||
|                 } |  | ||||||
|                 Ok(AppEvent::SimulationSpeedDown) => { |  | ||||||
|                     target_duration = target_duration.saturating_add(Duration::from_millis(1)); |  | ||||||
|                     println_info(format!("decreased simulation speed to {} ups", 1f64 / target_duration.as_secs_f64())); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let tick_time = start.elapsed(); |  | ||||||
|         if tick_time < target_duration { |  | ||||||
|             thread::sleep(target_duration - tick_time); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| enum AppEvent { |  | ||||||
|     Close, |  | ||||||
|     RandomizeLeftPixels, |  | ||||||
|     RandomizeRightPixels, |  | ||||||
|     RandomizeLeftLuma, |  | ||||||
|     RandomizeRightLuma, |  | ||||||
|     SeparatorAccelerate, |  | ||||||
|     SeparatorDecelerate, |  | ||||||
|     SimulationSpeedUp, |  | ||||||
|     SimulationSpeedDown, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl TryFrom<Event> for AppEvent { |  | ||||||
|     type Error = (); |  | ||||||
| 
 |  | ||||||
|     fn try_from(event: Event) -> Result<Self, Self::Error> { |  | ||||||
|         match event { |  | ||||||
|             Event::Key(key_event) if key_event.kind == KeyEventKind::Press => { |  | ||||||
|                 match key_event.code { |  | ||||||
|                     KeyCode::Char('h') => { |  | ||||||
|                         println_info("[h] help"); |  | ||||||
|                         println_info("[q] quit"); |  | ||||||
|                         println_info("[d] randomize left pixels"); |  | ||||||
|                         println_info("[e] randomize left luma"); |  | ||||||
|                         println_info("[r] randomize right pixels"); |  | ||||||
|                         println_info("[f] randomize right luma"); |  | ||||||
|                         println_info("[→] accelerate divider right"); |  | ||||||
|                         println_info("[←] accelerate divider left"); |  | ||||||
|                         Err(()) |  | ||||||
|                     } |  | ||||||
|                     KeyCode::Char('q') => Ok(AppEvent::Close), |  | ||||||
|                     KeyCode::Char('d') => Ok(AppEvent::RandomizeLeftPixels), |  | ||||||
|                     KeyCode::Char('e') => Ok(AppEvent::RandomizeLeftLuma), |  | ||||||
|                     KeyCode::Char('f') => Ok(AppEvent::RandomizeRightPixels), |  | ||||||
|                     KeyCode::Char('r') => Ok(AppEvent::RandomizeRightLuma), |  | ||||||
|                     KeyCode::Right => Ok(AppEvent::SeparatorAccelerate), |  | ||||||
|                     KeyCode::Left => Ok(AppEvent::SeparatorDecelerate), |  | ||||||
|                     KeyCode::Up => Ok(AppEvent::SimulationSpeedUp), |  | ||||||
|                     KeyCode::Down => Ok(AppEvent::SimulationSpeedDown), |  | ||||||
|                     key_code => { |  | ||||||
|                         println_debug(format!("unhandled KeyCode {key_code:?}")); |  | ||||||
|                         Err(()) |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             event => { |  | ||||||
|                 println_debug(format!("unhandled event {event:?}")); |  | ||||||
|                 Err(()) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn draw_pixels(pixels: &mut PixelGrid, left: &PixelGrid, right: &PixelGrid, split_index: usize) { |  | ||||||
|     for x in 0..pixels.width() { |  | ||||||
|         let left_or_right = if x < split_index { left } else { right }; |  | ||||||
|         for y in 0..pixels.height() { |  | ||||||
|             let set = x == split_index || left_or_right.get(x, y); |  | ||||||
|             pixels.set(x, y, set); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn draw_luma(luma: &mut ByteGrid, left: &ByteGrid, right: &ByteGrid, split_tile: usize) { |  | ||||||
|     for x in 0..luma.width() { |  | ||||||
|         let left_or_right = if x < split_tile { left } else { right }; |  | ||||||
|         for y in 0..luma.height() { |  | ||||||
|             let set = u8::max(48, left_or_right.get(x, y)); |  | ||||||
| 
 |  | ||||||
|             let set = set as f32 / u8::MAX as f32 * 12f32; |  | ||||||
| 
 |  | ||||||
|             luma.set(x, y, set as u8); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn send_to_screen(connection: &Connection, pixels: &PixelGrid, luma: &ByteGrid) { |  | ||||||
|     let pixel_cmd = BitmapLinearWin(Origin(0, 0), pixels.clone(), CompressionCode::Uncompressed); |  | ||||||
|     connection |  | ||||||
|         .send(pixel_cmd.into()) |  | ||||||
|         .expect("could not send pixels"); |  | ||||||
| 
 |  | ||||||
|     connection |  | ||||||
|         .send(CharBrightness(Origin(0, 0), luma.clone()).into()) |  | ||||||
|         .expect("could not send brightness"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn randomize<TGrid, TValue>(field: &mut TGrid) |  | ||||||
|     where |  | ||||||
|         TGrid: Grid<TValue>, |  | ||||||
|         Standard: Distribution<TValue>, |  | ||||||
| { |  | ||||||
|     let mut rng = rand::thread_rng(); |  | ||||||
| 
 |  | ||||||
|     for y in 0..field.height() { |  | ||||||
|         for x in 0..field.width() { |  | ||||||
|             field.set(x, y, rng.gen()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn init() -> Connection { |  | ||||||
|     env_logger::builder() |  | ||||||
|         .filter_level(LevelFilter::Info) |  | ||||||
|         .parse_default_env() |  | ||||||
|         .init(); |  | ||||||
| 
 |  | ||||||
|     execute!(stdout(), EnterAlternateScreen, EnableLineWrap) |  | ||||||
|         .expect("could not enter alternate screen"); |  | ||||||
|     enable_raw_mode().expect("could not enable raw terminal mode"); |  | ||||||
| 
 |  | ||||||
|     Connection::open(Cli::parse().destination) |  | ||||||
|         .expect("Could not connect. Did you forget `--destination`?") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn de_init() { |  | ||||||
|     disable_raw_mode().expect("could not disable raw terminal mode"); |  | ||||||
|     execute!(stdout(), LeaveAlternateScreen).expect("could not leave alternate screen"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										76
									
								
								src/rules.rs
									
										
									
									
									
								
							
							
						
						
									
										76
									
								
								src/rules.rs
									
										
									
									
									
								
							|  | @ -1,30 +1,30 @@ | ||||||
| use rand::rngs::ThreadRng; | use rand::rngs::ThreadRng; | ||||||
| use rand::{thread_rng, Rng}; | use rand::{thread_rng, Rng}; | ||||||
| 
 | use servicepoint::Value; | ||||||
| use crate::print::println_info; | use crate::print::println_info; | ||||||
| 
 | 
 | ||||||
| const MAX_BRIGHTNESS: u8 = 12; | pub struct Rules<T: Value> | ||||||
| 
 |  | ||||||
| pub struct Rules<TState, TKernel, const KERNEL_SIZE: usize> |  | ||||||
| where |  | ||||||
|     TState: Copy + PartialEq, |  | ||||||
|     TKernel: Copy, |  | ||||||
| { | { | ||||||
|     pub kernel: [[TKernel; KERNEL_SIZE]; KERNEL_SIZE], |     pub kernel: Kernel3x3, | ||||||
|     pub count_neighbor: Box<dyn Fn(TState, TKernel) -> i32>, |     pub count_neighbor: Box<dyn Fn(T, bool) -> i32>, | ||||||
|     pub next_state: Box<dyn Fn(TState, i32) -> TState>, |     pub next_state: Box<dyn Fn(T, i32) -> T>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub const MOORE_NEIGHBORHOOD: [[bool; 3]; 3] = | type Kernel3x3 = [[bool; 3]; 3]; | ||||||
|     [[true, true, true], [true, false, true], [true, true, true]]; |  | ||||||
| 
 | 
 | ||||||
| pub const NEUMANN_NEIGHBORHOOD: [[bool; 3]; 3] = [ | pub const MOORE_NEIGHBORHOOD: Kernel3x3 =    [ | ||||||
|  |     [true, true, true], | ||||||
|  |         [true, false, true], | ||||||
|  |         [true, true, true] | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | pub const NEUMANN_NEIGHBORHOOD: Kernel3x3 = [ | ||||||
|     [false, true, false], |     [false, true, false], | ||||||
|     [true, false, true], |     [true, false, true], | ||||||
|     [false, true, false], |     [false, true, false], | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| pub const DIAGONALS_NEIGHBORHOOD: [[bool; 3]; 3] = [ | pub const DIAGONALS_NEIGHBORHOOD: Kernel3x3 = [ | ||||||
|     [true, false, true], |     [true, false, true], | ||||||
|     [false, false, false], |     [false, false, false], | ||||||
|     [true, false, true], |     [true, false, true], | ||||||
|  | @ -39,16 +39,11 @@ pub fn count_true_neighbor(neighbor_state: bool, kernel_value: bool) -> i32 { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[must_use] | #[must_use] | ||||||
| pub fn generate_bb3() -> Rules<bool, bool, 3> { | pub fn generate_bb3() -> Rules<bool> { | ||||||
|     let mut rng = thread_rng(); |     let mut rng = thread_rng(); | ||||||
| 
 | 
 | ||||||
|     let is_moore = rng.gen_bool(1.0 / 2.0); |     let kernel = choose_neighborhood(&mut rng); | ||||||
|     let kernel = if is_moore { |     let max_neighbors = count_max_neighbors(kernel); | ||||||
|         MOORE_NEIGHBORHOOD |  | ||||||
|     } else { |  | ||||||
|         NEUMANN_NEIGHBORHOOD |  | ||||||
|     }; |  | ||||||
|     let max_neighbors = if is_moore { 8 } else { 4 }; |  | ||||||
| 
 | 
 | ||||||
|     let birth = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[0]); |     let birth = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[0]); | ||||||
|     let survive = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[]); |     let survive = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[]); | ||||||
|  | @ -78,17 +73,11 @@ fn generate_neighbor_counts(count: u8, rng: &mut ThreadRng, exclude: &[i32]) -> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[must_use] | #[must_use] | ||||||
| pub fn generate_u8b3() -> Rules<u8, bool, 3> { | pub fn generate_u8b3() -> Rules<u8> { | ||||||
|     let mut rng = thread_rng(); |     let mut rng = thread_rng(); | ||||||
| 
 | 
 | ||||||
|     let kernel = match rng.gen_range(0..3) { |     let kernel = choose_neighborhood(&mut rng); | ||||||
|         0 => MOORE_NEIGHBORHOOD, |     let alive_threshold = u8::max(1, rng.gen()); | ||||||
|         1 => NEUMANN_NEIGHBORHOOD, |  | ||||||
|         2 => DIAGONALS_NEIGHBORHOOD, |  | ||||||
|         _ => panic!(), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     let alive_threshold = rng.gen(); |  | ||||||
| 
 | 
 | ||||||
|     let birth = generate_neighbor_counts(rng.gen_range(1..=9), &mut rng, &[0]); |     let birth = generate_neighbor_counts(rng.gen_range(1..=9), &mut rng, &[0]); | ||||||
|     let survive = generate_neighbor_counts( |     let survive = generate_neighbor_counts( | ||||||
|  | @ -97,8 +86,8 @@ pub fn generate_u8b3() -> Rules<u8, bool, 3> { | ||||||
|         &[], |         &[], | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     let add = rng.gen_range(5..40); |     let add = rng.gen_range(1..15); | ||||||
|     let sub = rng.gen_range(5..40); |     let sub = rng.gen_range(1..15); | ||||||
| 
 | 
 | ||||||
|     println_info(format!("generated u8b3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}, alive_thresh: {alive_threshold}, delta: {add}/{sub}")); |     println_info(format!("generated u8b3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}, alive_thresh: {alive_threshold}, delta: {add}/{sub}")); | ||||||
| 
 | 
 | ||||||
|  | @ -115,3 +104,24 @@ pub fn generate_u8b3() -> Rules<u8, bool, 3> { | ||||||
|         }), |         }), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | fn choose_neighborhood(rng: &mut ThreadRng) -> Kernel3x3 { | ||||||
|  |     match rng.gen_range(0..3) { | ||||||
|  |         0 => MOORE_NEIGHBORHOOD, | ||||||
|  |         1 => NEUMANN_NEIGHBORHOOD, | ||||||
|  |         2 => DIAGONALS_NEIGHBORHOOD, | ||||||
|  |         _ => unreachable!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn count_max_neighbors<const SIZE: usize>(kernel: [[bool; SIZE]; SIZE]) -> u8 { | ||||||
|  |     let mut result = 0; | ||||||
|  |     for row in kernel { | ||||||
|  |         for cell in row { | ||||||
|  |             if cell { | ||||||
|  |                 result += 1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     result | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										179
									
								
								src/simulation.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/simulation.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,179 @@ | ||||||
|  | use crate::{ | ||||||
|  |     game::Game, | ||||||
|  |     print::{println_debug, println_info}, | ||||||
|  |     rules::{generate_bb3, generate_u8b3}, | ||||||
|  | }; | ||||||
|  | use rand::{distributions::Standard, prelude::Distribution, Rng}; | ||||||
|  | use servicepoint::{ | ||||||
|  |     Bitmap, Brightness, BrightnessGrid, Grid, Value, ValueGrid, PIXEL_HEIGHT, PIXEL_WIDTH, | ||||||
|  |     TILE_HEIGHT, TILE_SIZE, TILE_WIDTH, | ||||||
|  | }; | ||||||
|  | use std::num::Wrapping; | ||||||
|  | 
 | ||||||
|  | pub(crate) struct Simulation { | ||||||
|  |     pub(crate) left_pixels: Game<bool>, | ||||||
|  |     pub(crate) right_pixels: Game<bool>, | ||||||
|  |     pub(crate) left_luma: Game<u8>, | ||||||
|  |     pub(crate) right_luma: Game<u8>, | ||||||
|  |     split_pixel: usize, | ||||||
|  |     pub(crate) split_speed: i32, | ||||||
|  |     iteration: Wrapping<u8>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub enum SimulationEvent { | ||||||
|  |     RandomizeLeftPixels, | ||||||
|  |     RandomizeRightPixels, | ||||||
|  |     RandomizeLeftLuma, | ||||||
|  |     RandomizeRightLuma, | ||||||
|  |     SeparatorAccelerate, | ||||||
|  |     SeparatorDecelerate, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Simulation { | ||||||
|  |     pub fn new() -> Simulation { | ||||||
|  |         let left_pixels = Game { | ||||||
|  |             rules: generate_bb3(), | ||||||
|  |             field: make_randomized(PIXEL_WIDTH, PIXEL_HEIGHT), | ||||||
|  |         }; | ||||||
|  |         let right_pixels = Game { | ||||||
|  |             rules: generate_bb3(), | ||||||
|  |             field: make_randomized(PIXEL_WIDTH, PIXEL_HEIGHT), | ||||||
|  |         }; | ||||||
|  |         let left_luma = Game { | ||||||
|  |             rules: generate_u8b3(), | ||||||
|  |             field: make_randomized(TILE_WIDTH, TILE_HEIGHT), | ||||||
|  |         }; | ||||||
|  |         let right_luma = Game { | ||||||
|  |             rules: generate_u8b3(), | ||||||
|  |             field: make_randomized(TILE_WIDTH, TILE_HEIGHT), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         Self { | ||||||
|  |             left_pixels, | ||||||
|  |             right_pixels, | ||||||
|  |             left_luma, | ||||||
|  |             right_luma, | ||||||
|  |             split_pixel: 0, | ||||||
|  |             split_speed: 1, | ||||||
|  |             iteration: Wrapping(0u8), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn swap_left_right(&mut self) { | ||||||
|  |         std::mem::swap(&mut self.left_luma, &mut self.right_luma); | ||||||
|  |         std::mem::swap(&mut self.left_pixels, &mut self.right_pixels); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn regenerate(pixels: &mut Game<bool>, luma: &mut Game<u8>) { | ||||||
|  |         randomize(&mut pixels.field); | ||||||
|  |         randomize(&mut luma.field); | ||||||
|  |         pixels.rules = generate_bb3(); | ||||||
|  |         luma.rules = generate_u8b3(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn run_iteration(&mut self) { | ||||||
|  |         self.left_pixels.step(); | ||||||
|  |         self.right_pixels.step(); | ||||||
|  | 
 | ||||||
|  |         self.left_luma.step(); | ||||||
|  |         self.right_luma.step(); | ||||||
|  | 
 | ||||||
|  |         self.iteration += Wrapping(1u8); | ||||||
|  | 
 | ||||||
|  |         if self.split_speed > 0 && self.split_pixel == self.left_pixels.field.width() { | ||||||
|  |             self.split_pixel = 0; | ||||||
|  |             self.swap_left_right(); | ||||||
|  |             Self::regenerate(&mut self.left_pixels, &mut self.left_luma); | ||||||
|  |         } else if self.split_speed < 0 && self.split_pixel == 0 { | ||||||
|  |             self.split_pixel = self.left_pixels.field.width(); | ||||||
|  |             self.swap_left_right(); | ||||||
|  |             Self::regenerate(&mut self.right_pixels, &mut self.right_luma); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         self.split_pixel = i32::clamp( | ||||||
|  |             self.split_pixel as i32 + self.split_speed, | ||||||
|  |             0, | ||||||
|  |             self.left_pixels.field.width() as i32, | ||||||
|  |         ) as usize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn draw_state(&self, pixels: &mut Bitmap, luma: &mut BrightnessGrid) { | ||||||
|  |         for x in 0..pixels.width() { | ||||||
|  |             let left_or_right = if x < self.split_pixel { | ||||||
|  |                 &self.left_pixels.field | ||||||
|  |             } else { | ||||||
|  |                 &self.right_pixels.field | ||||||
|  |             }; | ||||||
|  |             for y in 0..pixels.height() { | ||||||
|  |                 let set = x == self.split_pixel || left_or_right.get(x, y); | ||||||
|  |                 pixels.set(x, y, set); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let split_tile = self.split_pixel / TILE_SIZE; | ||||||
|  |         for x in 0..luma.width() { | ||||||
|  |             let left_or_right = if x < split_tile { | ||||||
|  |                 &self.left_luma.field | ||||||
|  |             } else { | ||||||
|  |                 &self.right_luma.field | ||||||
|  |             }; | ||||||
|  |             for y in 0..luma.height() { | ||||||
|  |                 let set = left_or_right.get(x, y) as f32 / u8::MAX as f32 * u8::from(Brightness::MAX) as f32; | ||||||
|  |                 let set = (set as u8).max(1); | ||||||
|  |                 let set = Brightness::try_from(set).unwrap(); | ||||||
|  |                 luma.set(x, y, set); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn handle_event(&mut self, event: SimulationEvent) { | ||||||
|  |         match event { | ||||||
|  |             SimulationEvent::RandomizeLeftPixels => { | ||||||
|  |                 randomize(&mut self.left_pixels.field); | ||||||
|  |                 println_debug("randomized left pixels"); | ||||||
|  |             } | ||||||
|  |             SimulationEvent::RandomizeRightPixels => { | ||||||
|  |                 randomize(&mut self.right_pixels.field); | ||||||
|  |                 println_info("randomized right pixels"); | ||||||
|  |             } | ||||||
|  |             SimulationEvent::RandomizeLeftLuma => { | ||||||
|  |                 randomize(&mut self.left_luma.field); | ||||||
|  |                 println_info("randomized left luma"); | ||||||
|  |             } | ||||||
|  |             SimulationEvent::RandomizeRightLuma => { | ||||||
|  |                 randomize(&mut self.right_luma.field); | ||||||
|  |                 println_info("randomized right luma"); | ||||||
|  |             } | ||||||
|  |             SimulationEvent::SeparatorAccelerate => { | ||||||
|  |                 self.split_speed += 1; | ||||||
|  |                 println_info(format!("increased separator speed to {}", self.split_speed)); | ||||||
|  |             } | ||||||
|  |             SimulationEvent::SeparatorDecelerate => { | ||||||
|  |                 self.split_speed -= 1; | ||||||
|  |                 println_info(format!("decreased separator speed to {}", self.split_speed)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_randomized<T: Value>(width: usize, height: usize) -> ValueGrid<T> | ||||||
|  | where | ||||||
|  |     Standard: Distribution<T>, | ||||||
|  | { | ||||||
|  |     let mut grid = ValueGrid::new(width, height); | ||||||
|  |     randomize(&mut grid); | ||||||
|  |     grid | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn randomize<T: Value>(field: &mut ValueGrid<T>) | ||||||
|  | where | ||||||
|  |     Standard: Distribution<T>, | ||||||
|  | { | ||||||
|  |     let mut rng = rand::thread_rng(); | ||||||
|  | 
 | ||||||
|  |     for y in 0..field.height() { | ||||||
|  |         for x in 0..field.width() { | ||||||
|  |             field.set(x, y, rng.gen()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue