(* please add your name/handle to the credits section here:
	- velartrill
    - riking27
   also see grammar at line 79 for further editing opportunities
*)
let adjs = [| "gleeful"; "putrescent"; "whimsical"; "looming"; "endless"; "infinite"; "final"; "lustful"; 
	"twilight"; "black"; "lunar"; "solar"; "whispering"; "sundered"; "lawless"; "threefold"; "sevenfold";
	"tenfold"; "manifest"; "heavenly"; "somber"; "shadowed"; "wandering"; "distant"; "vengeful";
	"fearful"; "mighty"; "shrieking"; "lofty"; "loathesome"; "faceless"; "silent"; "pale"; "crimson";
	"fiery"; "wondrous"; "spiteful"; "weary"; "hateful"; "forsaken"; "forbidden"; "mellifluous";
    "abyssal"; "innocent"; "bronzed"; "fine"; "poor"; "masterful"; "forgotten"; "lost"; "infernal";
    "merciful"; "merciless"; "reckless"; "wonderful"; "beloved"; "ancient"; "decrepit"; "abstract";
    "honorable"; "animated"; "bloody"; "dead"; "old"; "expired"; "new"; "fresh"; |]
let nouns = [| "orb"; "law"; "moon"; "night"; "fist"; "question"; "deceit"; "perception"; "regret";
	"wonder"; "terror"; "hope"; "lust"; "inception"; "manifestation"; "whole"; "sea"; "stone"; "earth";
	"door"; "gate"; "portal"; "tree"; "fear"; "might"; "egret"; "circle"; "pit"; "crevice"; "hand"; "face";
	"inferno"; "spite"; "loom"; "malice"; "vial"; |]
let forces = [| "prince"; "king"; "queen"; "archon"; "god"; "demon"; "lord"; "lady"; "thing"; "wind";
	"beast"; "terror"; "spirit"; "spirits"; "muse"; "sylph"; "fool"; "child"; "creature"; "drake"; |]
let kinds = [| "rite"; "ritual"; "spell"; "invocation"; "evocation"; "conjuration"; "abjuration"; "binding"; 
	"incantation"; "appeasement"; "craft"; "alchemy"; "transmutation"; "transmogrification"; "incitement";
    "debasement"; "blessing"; "consecrecation"; "desecration"; |]

(* two of these are combined to form a surname *)
let people_name_elts = [| "wind"; "horn"; "fear"; "sea"; "sun"; "sky"; "star"; "hand"; "cold"; "warm"; "sand";
	"soul"; "sin"; "pale"; "dark"; "dread"; "hammer"; "lust"; "rage"; "blood"; "fire"; "flame"; "over"; "blight";
    "thunder"; "time"; "space"; "void"; "light"; "mind"; "life"; "air"; "breath"; "heir"; "doom"; "stein"; "chill";
    "cool"; "love"; "just"; "drake"; "hell"; "war"; "wright"; "bone"; |] 

(* these precede surnames *)
let forenames = [| "abigail"; "mordred"; "morgan"; "samantha"; "rachael"; "cassandra"; "jedediah"; "zebediah";
    "malachi"; "malachiah"; "charles"; "jezebel"; "simon"; "simone"; "salamandrael"; "jehosaphat"; "josiah";
    "mandrake"; "jessifer"; "sophia"; "morgause"; "winfred"; "winnifred"; "jennifer"; "carlissa"; "carlotta";
    "carissa"; "melissa"; "xylinda"; "samedra"; "damara"; "kurloz"; "hans"; "vladimir"; |] 
    
(* titles prefix a name, i.e. Sir Abigail Lusthammer *)
let titles = [| "sir"; "king"; "lord"; "queen"; "empress"; "dame"; "madam"; "mistress"; "master"; "lady";
    "emperor"; "archmage"; "magister"; "archmagister"; "magistress"; "magistrix"; "archmagistress";
    "archmagistrix"; "knight"; "agent"; |] 
	
type node = R of string array | B of node array | OL of node list | O of node | L of node list | S of string

let pick a = a.(Random.int(Array.length a))
let range min max = min+(Random.int(max-min))
let rec eval n : string = match n with
	| R(n) -> pick n
	| OL(n) -> eval (O(L n))
	| B(n) -> eval (pick n) 
	| O(n) -> if range 0 2 = 0 then eval n else ""
	| L(n) -> List.fold_left (fun a b -> a ^ (eval b)) "" n
	| S(s) -> s

let people = L [ R people_name_elts; R people_name_elts; ]
let force = L [R adjs; S " "; R forces]
let attrib = L[OL[R titles; S" "]; OL[R forenames; S" "]; people; OL[S" the "; R adjs;];S"'s ";]
let ritual_name = B [|
	L [O attrib; OL[R adjs; S " "]; R kinds; S " of "; O(S "the "); OL[force; S "'s "]; OL[R adjs; S " "]; R nouns; ];
	L [attrib; OL[R adjs; S " "]; R nouns; ]
|]

let agents = [| "a child"; "a bird"; "an eagle"; "a thief"; "a king"; "a conqueror"; "a fool"; "a madman";
			"an innocent"; "a murderer"; "a virgin"; "a queen"; "a pauper"; "a fox"; "a girl"; "a boy";
            "a dying man"; "a drake"; "a beast"; "a spirit"; "a sylph"; "a seer"; "the wind"; |]
let liquids = B [| S "blood"; S "ale"; S "wine"; S "water"; S"sea-water"; S"river-water"; S"lake-water"; L[S "the blood of "; R agents]; |]
let liquid_containers = [| "chalice"; "ewer"; "goblet"; "bowl"; "waterskin"; "bottle"; "cask"; |]
let object_containers = [| "bowl"; "box"; "basket"; "chest"; |]
let surfaces = [| "plinth"; "platter"; "altar"; "plate"; |]
let objects = [| "stone"; "feather"; "orb"; "quill"; "knife"; "candle"; "bow"; "bullet";
	"necklace"; "charm"; "amulet"; "biscuit"; |]
let materials = [| "crystal"; "bone"; "iron"; "steel"; "copper"; "gold"; "silver"; "sapphire"; "opal";
	"jade"; "amethyst"; "quartz"; "diamond"; "emerald"; "tin"; "zinc"; "bismuth"; "sterling"; "bronze"; "blood"; |]
(* [optional[partorigin], bodyparts] *)
let bodyparts = [| "skull"; "femur"; "blood"; "tooth"; "toe"; "eye"; "eyeball"; "skin"; "hand"; "fingernail";
	"claw"; "toenail"; "tail"; "vertebra"; "backbone"; "heart"; "liver"; "spleen"; "womb"; "fetus"; "fin"; |]
let partorigin = [| "human"; "virgin"; "cow"; "dog"; "horse"; "beast"; "wolf"; "alligator"; "goat"; "bird";
	"crow"; "eagle"; "dove"; "pigeon"; "owl"; "elephant"; "bear"; "fox"; "buzzard";
    (* posessives *)
    "a beloved pet's"; "an innocent's"; "the target's"; "your"; "your"; "an intruder's"; |]

type thingkind = Liquid_container | Object_container | Surface | Object
type thing = {
	kind : thingkind;
	name : string;
	adj : string option;
	desc : string option;
}
let times = R[| "once"; "twice"; "thrice"; |]
let places = [| "in a dreadful ravine"; "in a forsaken village"; "at the bottom of the sea"; "on the moon";
			"in a grave"; "in a mausoleum"; "beneath a tree"; "where it should not have been"; "in the sea";
            "in a dwelling"; "in a forest"; |]
let desc =
	(* kinds *) B[|
		(* actions *) L[ B[|
			L[S"found";
				OL[S" "; R places];
				OL[S" by ";
					R(Array.concat [agents; [|"one who should not have found it"; |]])];
			];
			
			L[S"lost";
				OL[S" "; 
					R(Array.concat [places;[|"to the sea";|]])];
				OL[S" by "; 
					R(Array.concat [agents; [|"one who should not have lost it"; |]])];
			];
			L[S "stolen";
				OL[S" by "; 
					R(Array.concat [agents; [|"one who should not have stolen it"; |]])];
				OL[S" from "; 
					R(Array.concat [agents; [|"one who should not have lost it";"one who should not have stolen it";
					"one who should never have held it"; "its rightful owner"; "a vault"; "a castle"; "a beast"; |]])];
			];
			L[S "washed";
				OL[S" "; R[|
					"in the sea"; "in blood"; "beneath a waterfall"; "in a fountain"; "in a lake long-dry";
					"in tears"; "in tears of grief"; "in tears of joy";|]];
				OL[S" by "; 
					R(Array.concat [agents; [|"an elderly washerwoman"; |]])];
			];
		|];
		(* circumstances *) O(B[|
			S" at night";
			S" by chance";
			S" long ago";
			S" years ago";
			S" in the distant past";
			S" at noon";
			S" at dawn";
			S" in war";
            S" at war";
            S" for years";
            S" by accident";
		|]);
	];
    (* traits *) B[|
		L[times; S"-blessed"];
		L[times; S"-cursed"];
		S "black as night";
		S "red as blood";
        S "white as bone";
        S "blue as the sky";
        S "green as grass";
        S "bright as gold";
        S "old as time";
	|];
|]

let adj_material = L[ O(OL[R adjs; S" "]); OL[R materials; S" "]; ]
			
let newthing () : thing = let kind = pick [|Liquid_container; Object_container; Surface; Object;|] in {
	kind = kind;
	adj = pick[|None; Some(pick adjs)|];
	desc = pick[|None; Some(eval desc)|];
	name = match kind with
		| Liquid_container -> eval (L[adj_material; R liquid_containers])
		| Object_container -> eval (L[adj_material; R object_containers])
		| Surface -> eval (L[adj_material; R surfaces])
		| Object -> eval (B[|
			L[adj_material; R objects];
			L[OL[R partorigin; S" "]; R bodyparts;]
		|])}
let descthing t = (match t.adj with Some(s) -> s^" " | None -> "")^
	(*(String.uppercase_ascii()*) t.name^
	(match t.desc with Some(s) -> ", "^s | None -> "")


let ritual () =
	let name = eval ritual_name in
	let foci =
		let rec acc (times : int) : thing list = (newthing ()) :: (if times = 0 then [] else acc (times-1))
		in acc (range 0 10)
	in
	(String.uppercase_ascii(name))^"\n\n"^
	(pick [|"You will need:"; "Components:"; "Foci:"; "Ingredients:"; "Elements:"; "Materials:";
		"You must gather:"; "Necessary offerings:"|]) ^"\n"^
		(let rec enumerate_foci n f = match f with
			| x :: rs -> ((string_of_int n) ^ ") " ^ (descthing x) ^ "\n") ^ (enumerate_foci (n+1) rs)
			| [] -> "" in enumerate_foci 1 foci)
	
let () = Random.self_init (); print_string (ritual ());;

