open Motus_lib (** Represent a decision rule *) let format_filter : Format.formatter -> Criteria.t -> unit = fun formatter f -> match f with | Lenght l -> Format.fprintf formatter "Doit etre de longueur %d" l | Contain (c, None) -> Format.fprintf formatter "Doit contenir un %c" c | Contain (c, Some i) -> Format.fprintf formatter "Doit contenir un %c a la position %d" c i | NotContain (c, None) -> Format.fprintf formatter "Ne doit pas contenir un %c" c | NotContain (c, Some i) -> Format.fprintf formatter "Ne doit pas contenir un %c a la position %d" c i (** Display the informations about the structure *) let show_structure : Format.formatter -> Wordlist.t -> Criteria.t list -> unit = fun format data filters -> Format.fprintf format "Filtres en cours : @[%a@]@\n" (Format.pp_print_list format_filter) filters; Format.fprintf format "Got %d elements @\n" (Wordlist.list_size data); if Wordlist.list_size data < 20 then Format.fprintf format "Remaining words @[@;%a@]@\n" (Format.pp_print_list ~pp_sep:Format.pp_force_newline (fun f w -> Format.fprintf f "%s" w ) ) (List.of_seq @@ Wordlist.words data); Format.close_box () (** Get the initial list *) let get_list : in_channel -> Criteria.t list -> Wordlist.t = fun channel filters -> let read channel = try Some (String.lowercase_ascii (Stdlib.input_line channel), channel) with | End_of_file -> None in let s = Seq.unfold read channel in Wordlist.add_words filters s (** Compare the proposed word and the result from the user in order to identify the future rules to apply *) let create_new_rules word result = let rules = ref [] and max_length = min (String.length word) (String.length result) in for i = 0 to max_length - 1 do match (String.get word i, String.get result i) with (* A space means that the letter is not present *) | c, ' ' -> rules := Criteria.(add (NotContain (c, None))) !rules (* The same letter means that the we found the right caracter *) | c, c' when Char.equal c c' -> rules := Criteria.(add (Contain (c, Some i)) !rules) (* Anything else, we got the letter, but at the wrong place *) | c, _ -> rules := Criteria.( add (Contain (c, None)) (add (NotContain (c, Some i)) !rules)) done; !rules type t = { catalog : Wordlist.t ; words : Wordlist.t } let rec run : int -> Criteria.t list -> t -> unit = fun len filters { catalog; words } -> let () = show_structure Format.std_formatter words filters in (* let next = Freq_analysis.analyse words |> Freq_analysis.pick_next_word words in *) let _, next = Entropy.analyse len ~catalog words in let () = Format.fprintf Format.std_formatter "Next word will be : %s@\n" next in let input = Stdlib.read_line () in (* if the input is empty, remove the word from the list and restart *) match String.equal String.empty input with | true -> let new_list = { catalog = Wordlist.remove_word catalog next ; words = Wordlist.remove_word words next } in run len filters new_list | false -> let new_rules = Criteria.merge_lists ~init:filters (create_new_rules next input) |> List.sort_uniq Stdlib.compare in let words = Wordlist.filter new_rules words in run len new_rules { catalog = Wordlist.remove_word catalog next; words } let init_rule rules word = String.iteri (fun p c -> match c with | ' ' -> () | _ -> rules := Criteria.Contain (c, Some p) :: !rules ) word let () = let length = ref 5 and dict = ref [] in let rules = ref [] in let anon_fun filename = dict := filename :: !dict in let speclist = [ ("-n", Arg.Set_int length, "Nombre de lettres") ; ("-p", Arg.String (init_rule rules), "Motif initial") ] in let () = Arg.parse speclist anon_fun "motus [-n ] dictionnaire" in let initial_filter = Criteria.Lenght !length :: !rules in let words_channel = open_in (List.hd !dict) in let words = get_list words_channel initial_filter in close_in words_channel; run !length initial_filter { catalog = words; words }