@@ -0,0 +1,4 @@ | |||
result | |||
result-doc | |||
*.swp | |||
@@ -0,0 +1,2 @@ | |||
if u use dis repo u agree to not be a chud. | |||
military and corps get out. |
@@ -0,0 +1,63 @@ | |||
# cellularAutomata | |||
a small application for running a one-dimensional cellular automata from random inputs, using comonads | |||
## usage | |||
the program will default to the size of the window | |||
`-w` and `-g` inputs can be given to determine the width and height, respectively | |||
## requirements | |||
* `getOpt` | |||
* `ncurses` (for detecting term width/height) | |||
## building / running | |||
builds using nix | |||
from a local folder: | |||
``` | |||
nix build . | |||
./result/bin/cellularAutomata | |||
``` | |||
from the repo directly: | |||
``` | |||
nix run github:techieAgnostic/cellularAutomata -- -w 40 -g 25 | |||
``` | |||
it may also be included as a flake input, as one normally would, and added to the package list using the included overlay | |||
## example | |||
`./cellularAutomata -w 40 -g 25` | |||
``` | |||
██ ████ █ █████ █ ███ █ ███ ███ | |||
██ █ ███ █ ██ █ ███ ██ █ █ █ █ ██ | |||
█ ███ ███ █ ███ █ █ ██ ██ ███ | |||
███ █ █ █ █ ██ █ ███████ ██ ██ | |||
███ █ █ █ ██ █ ██ ██ ███ ██ | |||
█ █ █ ██████ █████ ██████ █ █ | |||
██ ██ █ █ █ ██ █ █ ██ ██ █ | |||
██ ██ ██ ███ █ █ ███ ███ █ █ █ | |||
█ ███████ ██ ███ █ █ █ █ █ ███ █ ██ | |||
█ █ █████ █ █ ██ █ ██ █ █ | |||
███ █ ██ █ █ ████ █ ██ ██ | |||
█ █ █ ████ █ █ █ ███ ██ █ █████████ | |||
██ █ █ ██ █ ███████ █ ██ | |||
█████ ██ █ █████ █ ███ █ ███ | |||
██ █ ██ ███ ███ █ ██ █ █ ██ | |||
████ █ █████ ██ ██ █ █ ███ ██ █ ████ | |||
█ ███ █ ██ ██ ██ █ █████ █ ██ | |||
█ █ ███ ██ █ ██ ████████ █ ██ █ ███ | |||
█ █ █ ██ ████ █ ███ █ ███ █ █ | |||
██ █ █████ █ █ ██ █ █ ██ █ | |||
██ █ ██ ███ ██ █ ███ █ █ ██ █ █ | |||
████ █████ ██ █ ██ ███ ███ ██████ ███ | |||
█ █ █ ██ █████ █ █ ██ ██ █ █ | |||
█ █ █ ███ ██ █ ██ ███ █ █ | |||
██ █ █ ███ █ ███ █ █ ███ █ ███ █ █ | |||
█ █ ██ █ █ █ █ ██ █ █ █ ██ | |||
``` |
@@ -0,0 +1,2 @@ | |||
import Distribution.Simple | |||
main = defaultMain |
@@ -0,0 +1,30 @@ | |||
cabal-version: >=1.10 | |||
-- Initial package description 'cellularAutomata.cabal' generated by 'cabal | |||
-- init'. For further documentation, see | |||
-- http://haskell.org/cabal/users-guide/ | |||
name: cellularAutomata | |||
version: 0.1.0.0 | |||
-- synopsis: | |||
-- description: | |||
-- bug-reports: | |||
-- license: | |||
license-file: LICENSE | |||
author: Thorn Avery | |||
maintainer: s@p7.co.nz | |||
-- copyright: | |||
-- category: | |||
build-type: Simple | |||
executable cellularAutomata | |||
main-is: Main.hs | |||
-- other-modules: | |||
-- other-extensions: | |||
build-depends: base >=4.13 && <4.14 | |||
, random | |||
, turtle | |||
, brick | |||
, process | |||
hs-source-dirs: src | |||
default-language: Haskell2010 | |||
extra-libraries: ncurses |
@@ -0,0 +1,43 @@ | |||
{ | |||
"nodes": { | |||
"flake-utils": { | |||
"locked": { | |||
"lastModified": 1601282935, | |||
"narHash": "sha256-WQAFV6sGGQxrRs3a+/Yj9xUYvhTpukQJIcMbIi7LCJ4=", | |||
"owner": "numtide", | |||
"repo": "flake-utils", | |||
"rev": "588973065fce51f4763287f0fda87a174d78bf48", | |||
"type": "github" | |||
}, | |||
"original": { | |||
"owner": "numtide", | |||
"repo": "flake-utils", | |||
"type": "github" | |||
} | |||
}, | |||
"nixpkgs": { | |||
"locked": { | |||
"lastModified": 1604368813, | |||
"narHash": "sha256-UOLaURSO448k+4bGJlaSMYeo2F5F6CuFo9VoYDkhmsk=", | |||
"owner": "NixOS", | |||
"repo": "nixpkgs", | |||
"rev": "d105075a1fd870b1d1617a6008cb38b443e65433", | |||
"type": "github" | |||
}, | |||
"original": { | |||
"owner": "NixOS", | |||
"ref": "nixos-20.09", | |||
"repo": "nixpkgs", | |||
"type": "github" | |||
} | |||
}, | |||
"root": { | |||
"inputs": { | |||
"flake-utils": "flake-utils", | |||
"nixpkgs": "nixpkgs" | |||
} | |||
} | |||
}, | |||
"root": "root", | |||
"version": 7 | |||
} |
@@ -0,0 +1,19 @@ | |||
{ | |||
description = "a basic cellular automata using comonads"; | |||
inputs = { | |||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-20.09"; | |||
flake-utils.url = "github:numtide/flake-utils"; | |||
}; | |||
outputs = { self, nixpkgs, flake-utils, ... }: | |||
flake-utils.lib.eachDefaultSystem (system: | |||
let | |||
pkgs = import nixpkgs { | |||
overlays = [ (import ./overlay.nix) ]; | |||
inherit system; | |||
}; | |||
in { | |||
defaultPackage = pkgs.cellularAutomata; | |||
}) // { | |||
overlay = import ./overlay.nix; | |||
}; | |||
} |
@@ -0,0 +1,13 @@ | |||
{ mkDerivation, base, brick, lib, ncurses, process, random, turtle | |||
}: | |||
mkDerivation { | |||
pname = "cellularAutomata"; | |||
version = "0.1.0.0"; | |||
src = ./..; | |||
isLibrary = false; | |||
isExecutable = true; | |||
executableHaskellDepends = [ base brick process random turtle ]; | |||
executableSystemDepends = [ ncurses ]; | |||
license = "unknown"; | |||
hydraPlatforms = lib.platforms.none; | |||
} |
@@ -0,0 +1,3 @@ | |||
final: prev: { | |||
cellularAutomata = (import ./release.nix) prev; | |||
} |
@@ -0,0 +1,47 @@ | |||
bspkgs: | |||
let | |||
dontCheckPackages = [ ]; | |||
doJailbreakPackages = [ ]; | |||
dontHaddockPackages = [ ]; | |||
config = { | |||
packageOverrides = pkgs: rec { | |||
haskellPackages = | |||
let | |||
generatedOverrides = haskellPackagesNew: haskellPackagesOld: | |||
let | |||
toPackage = file: _: { | |||
name = builtins.replaceStrings [ ".nix" ] [ "" ] file; | |||
value = haskellPackagesNew.callPackage | |||
( ./. + "/nix/${file}") { }; | |||
}; | |||
in | |||
pkgs.lib.mapAttrs' toPackage | |||
(builtins.readDir ./nix); | |||
makeOverrides = | |||
function: names: haskellPackagesNew: haskellPackagesOld: | |||
let | |||
toPackage = name: { | |||
inherit name; | |||
value = function haskellPackagesOld.${name}; | |||
}; | |||
in | |||
builtins.listToAttrs (map toPackage names); | |||
composeExtensionsList = | |||
pkgs.lib.fold pkgs.lib.composeExtensions (_: _: {}); | |||
manualOverrides = haskellPackagesNew: haskellPackagesOld: { | |||
}; | |||
in | |||
pkgs.haskellPackages.override { | |||
overrides = composeExtensionsList [ | |||
generatedOverrides | |||
(makeOverrides pkgs.haskell.lib.dontCheck dontCheckPackages) | |||
(makeOverrides pkgs.haskell.lib.doJailbreak doJailbreakPackages) | |||
(makeOverrides pkgs.haskell.lib.dontHaddock dontHaddockPackages) | |||
manualOverrides | |||
]; | |||
}; | |||
}; | |||
}; | |||
pkgs = import bspkgs.path { inherit config; system = bspkgs.system; }; | |||
in | |||
pkgs.haskellPackages.cellularAutomata |
@@ -0,0 +1,194 @@ | |||
module Main where | |||
--import System.Random | |||
import Control.Monad | |||
import System.Process | |||
import System.Random | |||
import System.Console.GetOpt | |||
import System.Environment(getArgs, getProgName) | |||
import Data.Maybe (fromMaybe) | |||
------------------- | |||
-- comonad class -- | |||
------------------- | |||
class Functor w => Comonad w | |||
where | |||
(=>>) :: w a -> (w a -> b) -> w b | |||
extract :: w a -> a | |||
duplicate :: w a -> w (w a) | |||
x =>> f = fmap f (duplicate x) | |||
------------ | |||
-- spaces -- | |||
------------ | |||
-- a locally focussed space | |||
data Space t = Space [t] t [t] | |||
-- spaces are also functors | |||
instance Functor Space where | |||
fmap f (Space l c r) = Space (map f l) (f c) (map f r) | |||
-- our space is a comonad | |||
instance Comonad Space where | |||
-- duplicate will create a new space where | |||
-- the focussed element is our original space | |||
-- and each side is increasingly shifted copies | |||
-- in that direction | |||
duplicate w = | |||
Space (tail $ iterate left w) | |||
w | |||
(tail $ iterate right w) | |||
-- extract simply returns the focussed element | |||
extract (Space _ c _) = c | |||
-- functions for moving the point | |||
-- of locality. | |||
-- todo: question the empty list cases | |||
-- most spaces should be infinite | |||
right :: Space t -> Space t | |||
right s@(Space l c []) = s | |||
right (Space l c (r:rs)) = Space (c:l) r rs | |||
left :: Space t -> Space t | |||
left s@(Space [] c r) = s | |||
left (Space (l:ls) c r) = Space ls l (c:r) | |||
-- bound will take an infinite space | |||
-- and bound it by i and j on each side | |||
-- (not including the focus) and | |||
-- turn it into a list for printing | |||
bound :: Int -> Int -> Space t -> [t] | |||
bound i j (Space l c r) = (reverse (take i l)) ++ (c:(take j r)) | |||
-- boundw works as above, but the | |||
-- entire list will be the size | |||
-- given | |||
boundw :: Int -> Space t -> [t] | |||
boundw n = bound (x-m) x | |||
where | |||
o = if odd n then 1 else 0 | |||
m = if even n then 1 else 0 | |||
x = (n - o) `div` 2 | |||
----------------------- | |||
-- cellular automata -- | |||
----------------------- | |||
-- the states our cells can be in | |||
-- may need to provide an ordering | |||
-- may need to generalise the number | |||
-- of states | |||
data CellState = Alive | Dead | |||
deriving Eq | |||
-- how the states are displayed on screen | |||
-- this should probably be input to a function | |||
-- rather than hardcoded | |||
instance Show CellState | |||
where | |||
show Alive = "█" | |||
show Dead = " " | |||
-- a rule stating how a cell is determined | |||
rule :: Space CellState -> CellState | |||
rule (Space (l:_) _ (r:_)) | |||
| l == r = Dead | |||
| otherwise = Alive | |||
-- take a space and a rule and | |||
-- return the next space | |||
step :: (Space t -> t) -> Space t -> Space t | |||
step f w = w =>> f | |||
--------------- | |||
-- rng stuff -- | |||
--------------- | |||
-- takes a generator and returns | |||
-- an infinite list of bools | |||
ilobs :: StdGen -> [Bool] | |||
ilobs rng = b : (ilobs r) | |||
where | |||
(b,r) = random rng | |||
----------------- | |||
-- gross io bs -- | |||
----------------- | |||
-- everything below this line deals with | |||
-- input/output, and is therefore gross | |||
-- i will clean this up one day, but it | |||
-- hurts my soul. | |||
------------------------ | |||
-- command line flags -- | |||
------------------------ | |||
-- structure containing the programs options | |||
data Options = Options | |||
{ optWidth :: Int | |||
, optGenerations :: Int | |||
} deriving Show | |||
-- the default options for the program | |||
-- the width and generations are injected | |||
-- and intended to be gotten at runtime | |||
-- to match the window dimensions | |||
defaultOptions :: Int -> Int -> Options | |||
defaultOptions w h = Options | |||
{ optWidth = w | |||
, optGenerations = h | |||
} | |||
-- the avaliable options | |||
options :: [OptDescr (Options -> Options)] | |||
options = | |||
[ Option ['w'] ["width"] | |||
(ReqArg (\w opts -> opts { optWidth = (read w) }) "WIDTH") | |||
"term width" | |||
, Option ['g'] ["generations"] | |||
(ReqArg (\t opts -> opts { optGenerations = (read t) }) "GENERATIONS") | |||
"time steps to simulate" | |||
] | |||
-- parse the options into the structure | |||
-- erroring if encountering a flag not known to us | |||
parseArgs :: IO Options | |||
parseArgs = do | |||
argv <- getArgs | |||
progName <- getProgName | |||
tw <- readProcess "tput" [ "cols" ] "" | |||
th <- readProcess "tput" [ "lines" ] "" | |||
case getOpt RequireOrder options argv of | |||
(opts, [], []) -> return (foldl (flip id) (defaultOptions (read tw) (read th)) opts) | |||
(_, _, errs) -> ioError (userError (concat errs ++ helpMessage)) | |||
where | |||
header = "Usage: " ++ progName ++ " [OPTION...]" | |||
helpMessage = usageInfo header options | |||
--------------- | |||
-- main loop -- | |||
--------------- | |||
-- simply print the current space, then recurse to the next | |||
runAutomata :: Space CellState -> Int -> Int -> IO () | |||
runAutomata s 0 w = putStrLn $ concat $ map show $ boundw w s | |||
runAutomata s n w = do | |||
putStrLn $ concat $ map show $ boundw w s | |||
runAutomata (step rule s) (n - 1) w | |||
main :: IO () | |||
main = do | |||
options <- parseArgs | |||
rng <- getStdGen | |||
let cs = map (\x -> if x then Alive else Dead) $ ilobs rng | |||
let w = (optWidth options) | |||
let h = (optGenerations options) | |||
let wh = (w + 1) `div` 2 | |||
let m = head cs | |||
let l = take wh $ drop 1 cs | |||
let r = take wh $ drop wh $ drop 1 cs | |||
let s = Space (l ++ (repeat Dead)) m (r ++ (repeat Dead)) | |||
runAutomata s h w |