From 324b8d2fcdb9d9777dc08017b3e42438c5323105 Mon Sep 17 00:00:00 2001 From: raj_mathe Date: Wed, 3 Nov 2021 16:05:46 +0100 Subject: [PATCH] master > master: code go - interactive mode eingerichtet --- .../golang/internal/endpoints/endpoints_it.go | 286 ++++++++++++++++-- .../internal/endpoints/endpoints_run.go | 6 +- code/golang/internal/menus/menus.go | 83 ++++- code/golang/internal/menus/menus_prompt.go | 14 - code/golang/internal/menus/menus_type.go | 33 +- code/golang/internal/setup/cli/cli.go | 24 +- code/golang/internal/types/types_cli.go | 22 +- code/golang/main.go | 8 +- 8 files changed, 409 insertions(+), 67 deletions(-) diff --git a/code/golang/internal/endpoints/endpoints_it.go b/code/golang/internal/endpoints/endpoints_it.go index 9c3f95a..5252126 100644 --- a/code/golang/internal/endpoints/endpoints_it.go +++ b/code/golang/internal/endpoints/endpoints_it.go @@ -8,15 +8,14 @@ import ( "ads/internal/core/logging" "ads/internal/menus" "ads/internal/setup" - "reflect" - // algorithm_search_binary "ads/internal/algorithms/search/binary" - // algorithm_search_interpol "ads/internal/algorithms/search/interpol" - // algorithm_search_ith_element "ads/internal/algorithms/search/ith_element" - // algorithm_search_jump "ads/internal/algorithms/search/jump" - // algorithm_search_poison "ads/internal/algorithms/search/poison" + algorithm_search_binary "ads/internal/algorithms/search/binary" + algorithm_search_interpol "ads/internal/algorithms/search/interpol" + algorithm_search_ith_element "ads/internal/algorithms/search/ith_element" + algorithm_search_jump "ads/internal/algorithms/search/jump" + algorithm_search_poison "ads/internal/algorithms/search/poison" algorithm_search_sequential "ads/internal/algorithms/search/sequential" - // algorithm_sum_maxsubsum "ads/internal/algorithms/sum/maxsubsum" + algorithm_sum_maxsubsum "ads/internal/algorithms/sum/maxsubsum" ) /* ---------------------------------------------------------------- * @@ -43,6 +42,7 @@ var menuMain = menus.Menu{ }, Options: []menus.MenuOption{ {Label: "Version des Programms anzeigen", Action: actionShowVersion}, + {Label: "Programm auf config Datei ausführen.", Action: actionRunOnConfig}, {Label: "Suchalgorithmen", Submenu: &menuSearchAlgorithms}, {Label: "Summenalgorithmen", Submenu: &menuSumAlgorithms}, }, @@ -56,14 +56,14 @@ var menuSearchAlgorithms = menus.Menu{ "Algorithmus wählen", }, Options: []menus.MenuOption{ - {Label: "Binärsuchalgorithmus"}, - {Label: "Interpolationsalgorithmus"}, - {Label: "Suche i. kleinstes Element"}, - {Label: "Suche i. kleinstes Element", SubLabel: "D & C"}, - {Label: "Sprungsuche", SubLabel: "linear"}, - {Label: "Sprungsuche", SubLabel: "exponentiell"}, - {Label: "Giftsuche"}, - {Label: "Giftsuche", SubLabel: "optimiert"}, + {Label: "Binärsuchalgorithmus", Action: actionAlgorithmSearchBinary}, + {Label: "Interpolationsalgorithmus", Action: actionAlgorithmSearchInterpolation}, + {Label: "Suche i. kleinstes Element", Action: actionAlgorithmSearchIthElement}, + {Label: "Suche i. kleinstes Element", SubLabel: "D & C", Action: actionAlgorithmSearchIthElementDc}, + {Label: "Sprungsuche", SubLabel: "linear", Action: actionAlgorithmSearchJump}, + {Label: "Sprungsuche", SubLabel: "exponentiell", Action: actionAlgorithmSearchJumpExp}, + {Label: "Giftsuche", Action: actionAlgorithmSearchPoison}, + {Label: "Giftsuche", SubLabel: "optimiert", Action: actionAlgorithmSearchPoisonFast}, {Label: "Sequentiellsuchalgorithmus", Action: actionAlgorithmSearchSequential}, }, DefaultOption: -1, @@ -76,8 +76,8 @@ var menuSumAlgorithms = menus.Menu{ "Algorithmus wählen", }, Options: []menus.MenuOption{ - {Label: "Maximale Teilsumme", SubLabel: "brute force"}, - {Label: "Maximale Teilsumme", SubLabel: "D & C"}, + {Label: "Maximale Teilsumme", SubLabel: "brute force", Action: actionAlgorithmSumMaxsub}, + {Label: "Maximale Teilsumme", SubLabel: "D & C", Action: actionAlgorithmSumMaxsubDc}, }, DefaultOption: -1, } @@ -86,23 +86,259 @@ var menuSumAlgorithms = menus.Menu{ * ACTIONS - basic * ---------------------------------------------------------------- */ -func actionShowVersion() error { +func actionShowVersion() (bool, error) { logging.LogInfo("Version des Programms") Version() - return nil + return false, nil } /* ---------------------------------------------------------------- * * ACTIONS - Algorithmen * ---------------------------------------------------------------- */ -// TODO -func actionAlgorithmSearchSequential() error { - input_L := []int{45, 67} - input_x := 6 - menus.PromptValue("L", "Liste von Integers", "z.B. \033[1m5 73 42\033[0m", reflect.TypeOf([]int{})) +func actionRunOnConfig() (bool, error) { + path, cancel, err := logging.Prompt("Pfad zur Configdatei bitte eingeben:") + if cancel { + err = nil + } else if err == nil { + err = RunNonInteractive(path) + } + return cancel, err +} + +func actionAlgorithmSearchBinary() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{ + "muss aufsteigend sortiert sein", + }) + if cancel || err != nil { + return cancel, err + } + input_x, cancel, err := PromptInputInteger("x", "Suchwert", []string{}) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_binary.FancyBinarySearch(input_L, input_x) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchInterpolation() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{ + "muss aufsteigend sortiert sein", + "sollte (idealerweise) nicht uniform verteilt sein", + }) + if cancel || err != nil { + return cancel, err + } + input_x, cancel, err := PromptInputInteger("x", "Suchwert", []string{}) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_interpol.FancyInterpolationSearch(input_L, input_x, 0, len(input_L)-1) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchIthElement() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{}) + if cancel || err != nil { + return cancel, err + } + input_i, cancel, err := PromptInputInteger("i", "Suchindex in sortierter Liste", []string{ + "sollte zw. 1 und len(L) liegen", + }) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_ith_element.FancyFindIthSmallest(input_L, input_i) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchIthElementDc() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{}) + if cancel || err != nil { + return cancel, err + } + input_i, cancel, err := PromptInputInteger("i", "Suchindex in sortierter Liste", []string{ + "sollte zw. 1 und len(L) liegen", + }) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_ith_element.FancyFindIthSmallestDC(input_L, input_i) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchJump() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{ + "muss aufsteigend sortiert sein", + "sollte (idealerweise) nicht uniform verteilt sein", + "sollte (idealerweise) keine Duplikate enthalten", + }) + if cancel || err != nil { + return cancel, err + } + input_x, cancel, err := PromptInputInteger("x", "Suchwert", []string{}) + if cancel || err != nil { + return cancel, err + } + input_m, cancel, err := PromptInputInteger("m", "Sprunggröße", []string{ + "sollte >= 2 sein", + }) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_jump.FancyJumpSearchLinear(input_L, input_x, input_m) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchJumpExp() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{ + "muss aufsteigend sortiert sein", + "sollte (idealerweise) nicht uniform verteilt sein", + "sollte (idealerweise) keine Duplikate enthalten", + }) + if cancel || err != nil { + return cancel, err + } + input_x, cancel, err := PromptInputInteger("x", "Suchwert", []string{}) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_jump.FancyJumpSearchExponentiell(input_L, input_x) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchPoison() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{ + "muss genau eine 1 enthalten", + }) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_poison.FancyFindPoison(input_L) + logging.Prompt("hallo") + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchPoisonFast() (bool, error) { + input_L, cancel, err := PromptInputListOfZeroOnes("L", "Liste von Getränken", []string{ + "muss genau eine 1 enthalten", + }) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_search_poison.FancyFindPoisonFast(input_L) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSearchSequential() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{}) + if cancel || err != nil { + return cancel, err + } + input_x, cancel, err := PromptInputInteger("x", "Suchwert", []string{}) + if cancel || err != nil { + return cancel, err + } setup.DisplayStartOfCaseBlank() algorithm_search_sequential.FancySequentialSearch(input_L, input_x) setup.DisplayEndOfCase() - return nil + return cancel, nil +} + +func actionAlgorithmSumMaxsub() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{}) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_sum_maxsubsum.FancyMaxSubSum(input_L) + setup.DisplayEndOfCase() + return cancel, nil +} + +func actionAlgorithmSumMaxsubDc() (bool, error) { + input_L, cancel, err := PromptInputListOfInt("L", "Liste von Werten", []string{}) + if cancel || err != nil { + return cancel, err + } + setup.DisplayStartOfCaseBlank() + algorithm_sum_maxsubsum.FancyMaxSubSumDC(input_L) + setup.DisplayEndOfCase() + return cancel, nil +} + +/* ---------------------------------------------------------------- * + * CUSTOM PROMPTS für besondere Inputs + * ---------------------------------------------------------------- */ + +func PromptInputInteger(name string, descr string, requirements []string) (int, bool, error) { + type responseType struct { + Response int `yaml:"response"` + } + var response = responseType{} + var query = menus.PromptValueQuery{ + Description: descr, + Name: name, + Type: "Integer", + Requirements: &requirements, + Response: &response, + } + cancel, err := query.Prompt() + return response.Response, cancel, err +} + +func PromptInputListOfInt(name string, descr string, requirements []string) ([]int, bool, error) { + type responseType struct { + Response []int `yaml:"response"` + } + var response = responseType{} + var query = menus.PromptValueQuery{ + Description: descr, + Name: name, + Type: "Integerliste", + ValidExamples: &[]string{ + "[1, 2, 7, 8, 5]", + "[1000, 0, 4]", + }, + Requirements: &requirements, + Response: &response, + } + cancel, err := query.Prompt() + return response.Response, cancel, err +} + +func PromptInputListOfZeroOnes(name string, descr string, requirements []string) ([]int, bool, error) { + type responseType struct { + Response []int `yaml:"response"` + } + var response = responseType{} + var query = menus.PromptValueQuery{ + Description: descr, + Name: name, + Type: "Liste von 0er und 1er", + ValidExamples: &[]string{ + "[0, 0, 0, 1, 0]", + "[1, 0, 1, 1]", + }, + Requirements: &requirements, + Response: &response, + } + cancel, err := query.Prompt() + return response.Response, cancel, err } diff --git a/code/golang/internal/endpoints/endpoints_run.go b/code/golang/internal/endpoints/endpoints_run.go index b121501..dd8fee4 100644 --- a/code/golang/internal/endpoints/endpoints_run.go +++ b/code/golang/internal/endpoints/endpoints_run.go @@ -24,12 +24,10 @@ import ( * ---------------------------------------------------------------- */ // Liest Config Datei ein und führt Algorithmen auf Fälle durch -func RunNoninteractive(path string) error { +func RunNonInteractive(path string) error { var err error var err_case error - logging.LogPlain(setup.Logo()) - // extrahiere user config config := setup.NewUserConfig() err = setup.GetUserConfig(path, &config) @@ -37,6 +35,8 @@ func RunNoninteractive(path string) error { return err } + logging.LogPlain(setup.Logo()) + // Fälle extrahieren cases := []types.UserConfigCase{} if config.Parts != nil && config.Parts.Cases != nil { diff --git a/code/golang/internal/menus/menus.go b/code/golang/internal/menus/menus.go index f94ed85..11b1438 100644 --- a/code/golang/internal/menus/menus.go +++ b/code/golang/internal/menus/menus.go @@ -6,10 +6,13 @@ package menus import ( "fmt" + "reflect" "strconv" "strings" "ads/internal/core/logging" + + "gopkg.in/yaml.v3" ) /* ---------------------------------------------------------------- * @@ -22,6 +25,8 @@ func (menu Menu) ShowMenu() (bool, error) { index int meta bool quit bool + cancel bool + err error ) var promptMessages []string var options [][2]string @@ -79,12 +84,18 @@ func (menu Menu) ShowMenu() (bool, error) { opt := menu.Options[index] // Entweder Untermenü öffnen oder Action ausführen if opt.Submenu != nil { - quit, _ = opt.Submenu.ShowMenu() + quit, err = opt.Submenu.ShowMenu() if quit { - return true, nil + return true, err } } else if opt.Action != nil { - opt.Action() + cancel, err = opt.Action() + if err != nil { + logging.LogError(err) + } + if cancel { + continue + } quit := logging.PromptAnyKeyToContinue() // Falls während der Action der User Meta+D klickt, -> quit: if quit { @@ -100,3 +111,69 @@ func (menu Menu) ShowMenu() (bool, error) { } } } + +/* ---------------------------------------------------------------- * + * METHOD prompt values + * ---------------------------------------------------------------- */ + +func (query *PromptValueQuery) Prompt() (bool, error) { + var lines = []interface{}{} + var responsePtr = query.Response + var ( + line string + cancel bool + err error + ) + + // prüfen, dass response ein Ptr auf eine Struct ist: + if !(reflect.ValueOf(responsePtr).Kind() == reflect.Ptr) || !(reflect.ValueOf(responsePtr).Elem().Kind() == reflect.Struct) { + panic("Input muss ein Pointer auf einen struct sein") + } + + // prompt message vorbereiten: + lines = append(lines, fmt.Sprintf("%s, \033[1;3m%s\033[0m, als \033[1m%s\033[0m bitte eingeben.", query.Description, query.Name, query.Type)) + if query.ValidExamples != nil && len(*query.ValidExamples) > 0 { + if len(*query.ValidExamples) == 1 { + example := (*query.ValidExamples)[0] + lines = append(lines, fmt.Sprintf(" \033[3;4mBeispiel von Input im gültigen Format:\033[0m \033[2m%s\033[0m", example)) + } else { + lines = append(lines, fmt.Sprintf(" \033[3;4mBeispiele von Inputs im gültigen Format:\033[0m")) + for _, example := range *query.ValidExamples { + lines = append(lines, fmt.Sprintf(" - \033[2m%s\033[0m", example)) + } + lines = append(lines, fmt.Sprintf(" - \033[2;3metc.\033[0m")) + } + } + if query.Requirements != nil && len(*query.Requirements) > 0 { + if len(*query.Requirements) == 1 { + requirement := (*query.Requirements)[0] + lines = append(lines, fmt.Sprintf(" \033[3;4mInput muss erfüllen:\033[0m \033[2m%s\033[0m", requirement)) + } else { + lines = append(lines, fmt.Sprintf(" \033[3;4mInput muss erfüllen:\033[0m")) + for _, requirement := range *query.Requirements { + lines = append(lines, fmt.Sprintf(" - \033[2m%s\033[0m", requirement)) + } + } + } + + // prompt Eingabe eines Werts: + for { + line, cancel, err = logging.Prompt(lines...) + if cancel { + return true, nil + } + if err != nil { + logging.LogError(err, "") + continue + } + line = fmt.Sprintf("\"response\": %s", line) + err = yaml.Unmarshal([]byte(line), query.Response) + if err != nil { + logging.LogError(err, "") + continue + } + break + } + + return cancel, err +} diff --git a/code/golang/internal/menus/menus_prompt.go b/code/golang/internal/menus/menus_prompt.go index 16188e7..eebe80a 100644 --- a/code/golang/internal/menus/menus_prompt.go +++ b/code/golang/internal/menus/menus_prompt.go @@ -6,7 +6,6 @@ package menus import ( "fmt" - "reflect" "ads/internal/core/logging" "ads/internal/core/utils" @@ -76,16 +75,3 @@ func PromptListOfOptions(messages []string, options [][2]string, breaks []int, d } return choice, false } - -/* ---------------------------------------------------------------- * - * METHOD prompt values - * ---------------------------------------------------------------- */ - -func PromptValue(varname string, typename string, example string, t reflect.Type) (bool, error) { - _, cancel, err := logging.Prompt( - fmt.Sprintf("Bitte den Wert von \033[1m%s\033[0m als \033[1m%s\033[0m eingeben.", varname, typename), - example, - ) - // TODO: input parsen - return cancel, err -} diff --git a/code/golang/internal/menus/menus_type.go b/code/golang/internal/menus/menus_type.go index 0077605..58dfd6c 100644 --- a/code/golang/internal/menus/menus_type.go +++ b/code/golang/internal/menus/menus_type.go @@ -14,7 +14,7 @@ type MenuOption struct { Label string SubLabel string Submenu *Menu - Action func() error // NOTE: in go, this is automatically a pointer type + Action func() (bool, error) // NOTE: in go, this is automatically a pointer type } type Menu struct { @@ -24,3 +24,34 @@ type Menu struct { Options []MenuOption DefaultOption int } + +/* ---------------------------------------------------------------- * + * TYPES - prompt + * ---------------------------------------------------------------- */ + +/* +Usage + +- Name: Variablenname + +- Description: Kurze Beschreibung der Variablen + +- Type: Beschreiben des erwarteten Types der Variablen. + +- Requirements: Liste von Anforderungen. + +- Response: Ptr zur Struct, d. h. &responseType{}, wobei responseType eine struct der folgenden Form ist: + +type responseType struct { Response #### `yaml:"response"` } + +wobei #### = ein Typ +*/ +type PromptValueQuery struct { + Name string + Description string + Type string + ValidExamples *[]string + Requirements *[]string + // Response muss ein Ptr auf eine Struct sein: + Response interface{} +} diff --git a/code/golang/internal/setup/cli/cli.go b/code/golang/internal/setup/cli/cli.go index 56b847b..4efac83 100644 --- a/code/golang/internal/setup/cli/cli.go +++ b/code/golang/internal/setup/cli/cli.go @@ -39,6 +39,12 @@ var optionsDebug = argparse.Options{ Default: false, } +var optionsInteractive = argparse.Options{ + Help: "Startet die App im interaktiven Modus.", + Required: false, + Default: false, +} + var optionsColour = argparse.Options{ Help: "Ob Logging färblich angezeigt wird.", Required: false, @@ -60,15 +66,15 @@ func ParseCli(args []string) (*types.CliArguments, error) { var err error Parser = argparse.NewParser("cli parser", "Liest Optionen + Flags von Kommandozeile.") arguments := types.CliArguments{ - ModeHelp: Parser.NewCommand("help", ""), - ModeVersion: Parser.NewCommand("version", "Ruft Endpunkt auf, der die Version anzeigt."), - ModeRun: Parser.NewCommand("run", "Ruft Endpunkt auf, der die Algorithmen laufen lässt."), - ModeInteractive: Parser.NewCommand("it", "Startet die App im interaktiven Modus."), - Quiet: Parser.Flag("q", "quiet", &optionsQuiet), - Debug: Parser.Flag("", "debug", &optionsDebug), - Checks: Parser.String("", "checks", &optionsChecks), - Colour: Parser.String("", "colour", &optionsColour), - ConfigFile: Parser.String("", "config", &optionsConfigFile), + ModeHelp: Parser.NewCommand("help", ""), + ModeVersion: Parser.NewCommand("version", "Ruft Endpunkt auf, der die Version anzeigt."), + ModeRun: Parser.NewCommand("run", "Ruft Endpunkt auf, der die Algorithmen laufen lässt."), + Quiet: Parser.Flag("q", "quiet", &optionsQuiet), + Debug: Parser.Flag("", "debug", &optionsDebug), + Interactive: Parser.Flag("", "it", &optionsInteractive), + Checks: Parser.String("", "checks", &optionsChecks), + Colour: Parser.String("", "colour", &optionsColour), + ConfigFile: Parser.String("", "config", &optionsConfigFile), } err = Parser.Parse(args) return &arguments, err diff --git a/code/golang/internal/types/types_cli.go b/code/golang/internal/types/types_cli.go index 4705d77..4806f2e 100644 --- a/code/golang/internal/types/types_cli.go +++ b/code/golang/internal/types/types_cli.go @@ -15,15 +15,15 @@ import ( * ---------------------------------------------------------------- */ type CliArguments struct { - ModeHelp *argparse.Command - ModeVersion *argparse.Command - ModeRun *argparse.Command - ModeInteractive *argparse.Command - Quiet *bool - Debug *bool - Checks *string - Colour *string - ConfigFile *string + ModeHelp *argparse.Command + ModeVersion *argparse.Command + ModeRun *argparse.Command + Quiet *bool + Debug *bool + Interactive *bool + Checks *string + Colour *string + ConfigFile *string } /* ---------------------------------------------------------------- * @@ -38,6 +38,10 @@ func (arguments CliArguments) DebugModeOn() bool { return *arguments.Debug } +func (arguments CliArguments) InteractiveMode() bool { + return *arguments.Interactive +} + func (arguments CliArguments) ShowChecks() bool { return !utils.IsFalse(*arguments.Checks) } diff --git a/code/golang/main.go b/code/golang/main.go index d724dff..3f06add 100644 --- a/code/golang/main.go +++ b/code/golang/main.go @@ -66,9 +66,11 @@ func main() { if arguments.ModeVersion.Happened() { endpoints.Version() } else if arguments.ModeRun.Happened() { - err = endpoints.RunNoninteractive(arguments.GetConfigFile()) - } else if arguments.ModeInteractive.Happened() { - err = endpoints.RunInteractive() + if arguments.InteractiveMode() { + err = endpoints.RunInteractive() + } else { + err = endpoints.RunNonInteractive(arguments.GetConfigFile()) + } } else { // } else if arguments.Help.Happened() { endpoints.Help() }