Implimented submitting run times, more map listing utility commands, usage help and more!

This commit is contained in:
Rebekah 2024-06-18 15:56:10 -04:00
parent 165ab812f0
commit 82e992f6dd
Signed by: oneechanhax
GPG Key ID: 0074BF373B812798

519
main.go
View File

@ -3,9 +3,11 @@ package main
import (
"encoding/json"
"fmt"
"math/rand"
"os"
"path/filepath"
"slices"
"sort"
"strconv"
"strings"
"time"
@ -142,15 +144,21 @@ type JumpDatabaseMapEntry struct {
} `json:"zones"`
}
type PreviousRun struct {
Duration float64 `json:"duration"`
Date float64 `json:"date"`
}
type JumpmapRunTimeEntry struct {
Nontempusrun bool `json:"nontempusrun"`
ManuallyCreated bool `json:"manually_created"`
ID int `json:"id"`
Duration float64 `json:"duration"`
Rank int `json:"rank"`
Date float64 `json:"date"`
Class string `json:"class"`
Name string `json:"name"`
Nontempusrun bool `json:"nontempusrun"`
ManuallyCreated bool `json:"manually_created"`
ID int `json:"id"`
Duration float64 `json:"duration"`
Rank int `json:"rank"`
Date float64 `json:"date"`
Class string `json:"class"`
Name string `json:"name"`
PreviousRuns []PreviousRun `json:"previous_runs"`
}
func check(e error) {
@ -159,6 +167,26 @@ func check(e error) {
}
}
func CreateBaseRunTimeEntry(jump_map_name string) JumpmapRunTimeEntry {
base_entry_str := `{
"nontempusrun": true,
"manually_created": true,
"id": -1,
"duration": 0,
"rank": -1,
"date": 0,
"class": "SOLDIER",
"name": "jump_basejson"
}`
var jump_map JumpmapRunTimeEntry
if err := json.Unmarshal([]byte(base_entry_str), &jump_map); err != nil {
check(err)
}
jump_map.Name = jump_map_name
return jump_map
}
func ConvertPrettyTimeToSeconds(time string) float64 {
parts := strings.Split(time, ":")
seconds := 0.0
@ -201,6 +229,13 @@ func ExtractBareFilenameFromPath(in_path string) string {
return append(slice[:s], slice[s+1:]...)
}*/
func GetCurtime() float64 {
currentTime := time.Now()
fractional_nanosecond := float64(currentTime.UnixNano())
unix_seconds := fractional_nanosecond / 1e9
return unix_seconds
}
const (
JumpmapTypeSoldier = 0
JumpmapTypeDemoman = 1
@ -234,8 +269,16 @@ func StringToJumpmapRunType(jump_map_wanted_type string) int {
var mapinfo_database_location = filepath.Join("database", "map_info")
func GetDatabaseFilePath(jump_map_name string) string {
return filepath.Join(mapinfo_database_location, jump_map_name+".json")
}
func GetRecordFilePath(jump_map_type int, jump_map_name string) string {
return filepath.Join("personal", "completed."+GetFileNameFromJumpmapRunTypeEnum(jump_map_type), jump_map_name+".json")
}
func ReadDatabaseFile(jump_map_name string) JumpDatabaseMapEntry {
data, error := os.ReadFile(filepath.Join(mapinfo_database_location, jump_map_name+".json"))
data, error := os.ReadFile(GetDatabaseFilePath(jump_map_name))
check(error)
var jump_map JumpDatabaseMapEntry
@ -243,11 +286,15 @@ func ReadDatabaseFile(jump_map_name string) JumpDatabaseMapEntry {
check(err)
}
if jump_map.Name != ExtractBareFilenameFromPath(jump_map_name) {
panic("Malformed Map Database entry: " + jump_map_name + " Has: " + jump_map.Name)
}
return jump_map
}
func ReadRunRecordFile(jump_map_type int, jump_map_name string) JumpmapRunTimeEntry {
data, error := os.ReadFile(filepath.Join("personal", "completed."+GetFileNameFromJumpmapRunTypeEnum(jump_map_type), jump_map_name+".json"))
data, error := os.ReadFile(GetRecordFilePath(jump_map_type, jump_map_name))
check(error)
var jump_map JumpmapRunTimeEntry
@ -255,26 +302,85 @@ func ReadRunRecordFile(jump_map_type int, jump_map_name string) JumpmapRunTimeEn
check(err)
}
if jump_map.Name != ExtractBareFilenameFromPath(jump_map_name) {
panic("Malformed Run Record: " + jump_map_name + " Has: " + jump_map.Name)
}
return jump_map
}
func SubmitNewTime(jump_map_type int, jump_map_name string, duration float64) {
record_filepath := GetRecordFilePath(jump_map_type, jump_map_name)
var new_entry JumpmapRunTimeEntry
// Try to read the file firs
_, err := os.Stat(record_filepath)
run_exists := err == nil
if run_exists {
// if it exists, read it and verify run is fast
new_entry = ReadRunRecordFile(jump_map_type, jump_map_name)
if duration >= new_entry.Duration {
if new_entry.Duration == duration {
fmt.Printf("Time is duplicate or the same as current time: %f - %s\n", new_entry.Duration, jump_map_name+": "+GetPrettyTime(new_entry.Duration))
} else {
how_slow := (new_entry.Duration - duration)
fmt.Printf("Time is too slow by %f seconds: %s\n", how_slow, jump_map_name)
fmt.Printf("Get Faster by: %s\n", GetPrettyTime(how_slow))
}
panic("Time too slow!!! Get faster then try again.")
}
// back it up
ConvertRunToPrevious := func(new_entry JumpmapRunTimeEntry) PreviousRun {
var prev_run PreviousRun
prev_run.Date = new_entry.Date
prev_run.Duration = new_entry.Duration
return prev_run
}
new_entry.PreviousRuns = append(new_entry.PreviousRuns, ConvertRunToPrevious(new_entry))
// Create a fresh database entry
} else {
new_entry = CreateBaseRunTimeEntry(jump_map_name)
}
// Set the time within the record
new_entry.Duration = duration
new_entry.Date = GetCurtime()
// Assemble and Write it to a file
json_file_data, err := json.MarshalIndent(new_entry, "", "\t")
check(err)
err = os.WriteFile(record_filepath, json_file_data, 0777)
check(err)
fmt.Println("Submitted NEW time for map: " + jump_map_name)
}
func PrintRecord(printable_record any) string {
data, error := json.MarshalIndent(printable_record, "", "\t")
check(error)
return string(data)
}
func DoesFileExist(filepath string) bool {
if _, err := os.Stat(filepath); err == nil {
return true
}
return false
}
func FindMapNameFromPartial(partial_jump_map_name string) string {
if !strings.HasPrefix(partial_jump_map_name, "jump_") {
if !strings.HasPrefix(partial_jump_map_name, "jump_") && !strings.HasPrefix(partial_jump_map_name, "rj_") {
partial_jump_map_name = "jump_" + partial_jump_map_name
}
found_files, err := filepath.Glob(filepath.Join(mapinfo_database_location, partial_jump_map_name+"*.json"))
found_files, err := filepath.Glob(GetDatabaseFilePath(partial_jump_map_name + "*"))
check(err)
found_file_count := len(found_files)
if found_file_count > 1 {
found_map := filepath.Join(filepath.Join(mapinfo_database_location, partial_jump_map_name+".json"))
if _, err := os.Stat(found_map); err == nil {
found_map := GetDatabaseFilePath(partial_jump_map_name)
if DoesFileExist(found_map) {
return ExtractBareFilenameFromPath(found_map)
} else {
fmt.Printf("There are too many maps that match your request: \"%s\"\n", partial_jump_map_name)
@ -285,18 +391,27 @@ func FindMapNameFromPartial(partial_jump_map_name string) string {
os.Exit(2)
}
}
if len(found_files) == 0 {
fmt.Println("Unable to find mapname: " + partial_jump_map_name)
panic("Attempting to find map that doesnt exist: " + partial_jump_map_name)
}
// should be just one
return ExtractBareFilenameFromPath(found_files[0])
}
func GetPrettyMapTime(jump_map_type int, jump_map_name string) string {
run_record := ReadRunRecordFile(jump_map_type, jump_map_name)
duration := time.Duration(run_record.Duration) * time.Second
func GetPrettyTime(in_time float64) string {
duration := time.Duration(in_time) * time.Second
hours := int(duration.Hours())
minutes := int(duration.Minutes()) % 60
seconds := int(duration.Seconds()) % 60
return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
}
func GetPrettyMapTime(jump_map_type int, jump_map_name string) string {
run_record := ReadRunRecordFile(jump_map_type, jump_map_name)
return GetPrettyTime(run_record.Duration)
}
func GetMapList() []string {
found_files, err := filepath.Glob(filepath.Join(mapinfo_database_location, "*.json"))
check(err)
@ -337,28 +452,55 @@ func IsOnMapIgnorelist(jump_map_name string) bool {
return false
}
func PrintMapUsageInfo() {
fmt.Println("usage: " + os.Args[0] + "[operation] <MAP_NAME> <SOLDIER/DEMOMAN(s/d)>")
func PrintMapRuntime(jump_map_type int, jump_map_name string) {
fmt.Println("Run time for " + jump_map_name + ": " + GetPrettyMapTime(jump_map_type, jump_map_name))
}
/*func PrintClassUsageInfo() {
fmt.Println("usage: " + os.Args[0] + "[operation] <SOLDIER/DEMOMAN(s/d)>")
}*/
func PrintUsageInfo() {
fmt.Println("usage: " + os.Args[0] + " [operation] <Additional Args>")
fmt.Println("try running \"" + os.Args[0] + " help\" for additional info on operations")
}
func PrintMapUsageInfo() {
fmt.Println("usage: " + os.Args[0] + " [operation] <MAP_NAME> <SOLDIER/DEMOMAN(s/d)>")
}
func PrintClassUsageInfo() {
fmt.Println("usage: " + os.Args[0] + " [operation] <SOLDIER/DEMOMAN(s/d)>")
}
func PrintSubmitTimeInfo() {
fmt.Println("usage: " + os.Args[0] + " [operation] <MAP_NAME> <TIME> <SOLDIER/DEMOMAN(s/d)>")
fmt.Println("Format the time as follows: HH:MM:SS.MS")
}
func PrintHelpInfo() {
PrintUsageInfo()
fmt.Println("help - Get the help you are currently seeing")
fmt.Println("list-maps - Get a list of all maps available")
fmt.Println("get-curtime - Get the current unix date, time in seconds, for use in storing times in the database")
fmt.Println("convert-time - Convert a time from HH:MM:SS.MS to seconds, for use in storing times in the database")
fmt.Println("")
PrintMapUsageInfo()
fmt.Println("get-time - Get a personal maps completion time")
fmt.Println("list-tier - Get the difficulty tier of a particular map")
fmt.Println("")
PrintClassUsageInfo()
fmt.Println("list-tiers - Get all of the difficulty tiers listed out, useful for combining with grep!")
fmt.Println("list-maps - Get a list of all maps available")
fmt.Println("convert-time - Convert a time from HH:MM:SS.MS to seconds, for use in storing times in the database")
fmt.Println("get-curtime - Get the current unix date, time in seconds")
fmt.Println("list-completed-tiers - List out personal completed maps with their tiers")
fmt.Println("list-uncompleted-tiers - List out personal unbeaten maps with their tiers, useful for combining with grep!")
fmt.Println("list-completed-times - Lists out the completed maps with their times sorted by the time with newest first.")
fmt.Println("list-old-completed-times - Lists out the completed maps with their times sorted by the time with oldest first.")
fmt.Println("list-recently-completed-times - Lists out the last 6 maps completed.")
fmt.Println("list-few-old-completed-times - Lists out the 6 maps with the oldest completion time.")
fmt.Println("list-uncompleted-random/list-handful - Lists out the 6 maps to play that you havent completed or are very old, within a low tier if difficulty")
fmt.Println("")
PrintSubmitTimeInfo()
fmt.Println("submit-time - Submit a run, will create a file or will read the file and verify it then backup the run and submit if better")
fmt.Println("")
}
func main() {
@ -368,12 +510,7 @@ func main() {
os.Exit(42)
}
if os.Args[1] == "help" {
PrintHelpInfo()
os.Exit(0)
}
if os.Args[1] == "get-time" {
ParseMapArgs := func() (int, string) {
if argument_length < 3 {
fmt.Println("Error: No map name provided")
PrintMapUsageInfo()
@ -387,53 +524,54 @@ func main() {
}
// try {
wanted_map := FindMapNameFromPartial(os.Args[2])
//} catch
fmt.Println("Run time for " + wanted_map + ": " + GetPrettyMapTime(wanted_class, wanted_map))
os.Exit(0)
return wanted_class, wanted_map
}
ParseClassArgs := func() int {
wanted_class := JumpmapTypeSoldier
if argument_length < 3 {
fmt.Println("Warning: No classname provided, using soldier as default")
} else {
wanted_class = StringToJumpmapRunType(os.Args[2])
}
return wanted_class
}
if os.Args[1] == "list-tier" {
switch os.Args[1] {
case "help":
PrintHelpInfo()
case "list-maps":
map_list := GetMapList()
fmt.Println("Found maps:")
for _, mapp := range map_list {
fmt.Println(mapp)
}
case "get-curtime":
fmt.Printf("Curtime: %f\n", GetCurtime())
case "convert-time":
if argument_length < 3 {
fmt.Println("Error: No map name provided")
PrintMapUsageInfo()
fmt.Println("Error: No time to convert into seconds???...???")
os.Exit(1)
}
wanted_class := JumpmapTypeSoldier
if argument_length < 4 {
fmt.Println("Warning: No classname provided, using soldier as default")
} else {
wanted_class = StringToJumpmapRunType(os.Args[3])
}
wanted_map := FindMapNameFromPartial(os.Args[2])
fmt.Printf("Converted-Time-To-Seconds: %f\n", ConvertPrettyTimeToSeconds(os.Args[2]))
case "get-time":
wanted_class, wanted_map := ParseMapArgs()
PrintMapRuntime(wanted_class, wanted_map)
case "list-tier":
wanted_class, wanted_map := ParseMapArgs()
fmt.Printf("%s: T%d\n", wanted_map, GetMapTier(wanted_class, wanted_map))
os.Exit(0)
}
if os.Args[1] == "list-tiers" {
wanted_class := JumpmapTypeSoldier
if argument_length < 3 {
fmt.Println("Warning: No classname provided, using soldier as default")
} else {
wanted_class = StringToJumpmapRunType(os.Args[3])
}
case "list-tiers":
wanted_class := ParseClassArgs()
map_list := GetMapList()
fmt.Println("Found map tiers:")
for _, mapp := range map_list {
fmt.Printf("%s: T%d\n", mapp, GetMapTier(wanted_class, mapp))
}
os.Exit(0)
}
if os.Args[1] == "list-completed-tiers" {
wanted_class := JumpmapTypeSoldier
if argument_length < 3 {
fmt.Println("Warning: No classname provided, using soldier as default")
} else {
wanted_class = StringToJumpmapRunType(os.Args[3])
}
case "list-completed-tiers":
wanted_class := ParseClassArgs()
map_list := GetCompletedMapList(wanted_class)
fmt.Println("Found completed map tiers:")
@ -442,15 +580,10 @@ func main() {
fmt.Printf("%s: T%d\n", mapp, GetMapTier(wanted_class, mapp))
}
}
os.Exit(0)
}
if os.Args[1] == "list-uncompleted-tiers" {
wanted_class := JumpmapTypeSoldier
if argument_length < 3 {
fmt.Println("Warning: No classname provided, using soldier as default")
} else {
wanted_class = StringToJumpmapRunType(os.Args[3])
}
case "list-uncompleted-tiers":
wanted_class := ParseClassArgs()
map_list := GetMapList()
completed_map_list := GetCompletedMapList(wanted_class)
fmt.Println("Found uncompleted map tiers:")
@ -459,34 +592,230 @@ func main() {
fmt.Printf("%s: T%d\n", mapp, GetMapTier(wanted_class, mapp))
}
}
os.Exit(0)
}
if os.Args[1] == "list-maps" {
map_list := GetMapList()
fmt.Println("Found maps:")
for _, mapp := range map_list {
fmt.Println(mapp)
case "list-completed-times":
wanted_class := ParseClassArgs()
// map_list := GetMapList()
completed_map_list := GetCompletedMapList(wanted_class)
// Read in all completion records!
var records []JumpmapRunTimeEntry
for _, mapp := range completed_map_list {
if !IsOnMapIgnorelist(mapp) {
run_record := ReadRunRecordFile(wanted_class, mapp)
records = append(records, run_record)
}
}
os.Exit(0)
}
if os.Args[1] == "convert-time" {
// Sort
sort.Slice(records, func(i, j int) bool {
return records[i].Date > records[j].Date
})
fmt.Println("Completed Maps:")
for _, mapp := range records {
fmt.Printf("%s: Completed: %s in %s\n", mapp.Name, time.Unix(int64(mapp.Date), 0).Format("2006-01-02/15:04"), GetPrettyMapTime(wanted_class, mapp.Name))
}
case "list-recently-completed-times":
wanted_class := ParseClassArgs()
// map_list := GetMapList()
completed_map_list := GetCompletedMapList(wanted_class)
// Read in all completion records!
var records []JumpmapRunTimeEntry
for _, mapp := range completed_map_list {
if !IsOnMapIgnorelist(mapp) {
run_record := ReadRunRecordFile(wanted_class, mapp)
records = append(records, run_record)
}
}
// Sort
sort.Slice(records, func(i, j int) bool {
return records[i].Date > records[j].Date
})
fmt.Println("Recently Completed Maps:")
for i, mapp := range records {
fmt.Printf("%s: Completed: %s in %s\n", mapp.Name, time.Unix(int64(mapp.Date), 0).Format("2006-01-02/15:04"), GetPrettyMapTime(wanted_class, mapp.Name))
if i >= 6 {
break
}
}
case "list-old-completed-times":
wanted_class := ParseClassArgs()
// map_list := GetMapList()
completed_map_list := GetCompletedMapList(wanted_class)
// Read in all completion records!
var records []JumpmapRunTimeEntry
for _, mapp := range completed_map_list {
if !IsOnMapIgnorelist(mapp) {
run_record := ReadRunRecordFile(wanted_class, mapp)
records = append(records, run_record)
}
}
// Sort
sort.Slice(records, func(i, j int) bool {
return records[i].Date < records[j].Date
})
fmt.Println("Old Completed Maps:")
for _, mapp := range records {
fmt.Printf("%s: Completed: %s in %s\n", mapp.Name, time.Unix(int64(mapp.Date), 0).Format("2006-01-02/15:04"), GetPrettyMapTime(wanted_class, mapp.Name))
}
case "list-few-old-completed-times":
wanted_class := ParseClassArgs()
// map_list := GetMapList()
completed_map_list := GetCompletedMapList(wanted_class)
// Read in all completion records!
var records []JumpmapRunTimeEntry
for _, mapp := range completed_map_list {
if !IsOnMapIgnorelist(mapp) {
run_record := ReadRunRecordFile(wanted_class, mapp)
records = append(records, run_record)
}
}
// Sort
sort.Slice(records, func(i, j int) bool {
return records[i].Date < records[j].Date
})
fmt.Println("Oldest Completed Maps:")
for i, mapp := range records {
fmt.Printf("%s: Completed: %s in %s\n", mapp.Name, time.Unix(int64(mapp.Date), 0).Format("2006-01-02/15:04"), GetPrettyMapTime(wanted_class, mapp.Name))
if i >= 6 {
break
}
}
case "list-uncompleted-random":
fallthrough
case "list-handful":
wanted_class := ParseClassArgs()
map_list := GetMapList()
completed_map_list := GetCompletedMapList(wanted_class)
// Gather Tier 1-3 (Easy Stuff) and 15% of the total number of the tier below its maps... eventually...
var mapinfo_pool []string
var tier_three_count int
for _, mapp := range map_list {
if slices.Contains(completed_map_list, mapp) {
continue
}
map_tier := GetMapTier(wanted_class, mapp)
if map_tier != 1 && map_tier != 2 && map_tier != 3 {
continue
}
if map_tier == 3 {
tier_three_count++
}
mapinfo_pool = append(mapinfo_pool, fmt.Sprintf("%s: T%d", mapp, map_tier))
}
// Add the 15% of Tier 4
tier_four_percentage := int(float64(tier_three_count) * 0.30)
var tier_four_mapinfo_pool []string
for _, mapp := range map_list {
if slices.Contains(completed_map_list, mapp) {
continue
}
map_tier := GetMapTier(wanted_class, mapp)
if map_tier != 4 {
continue
}
tier_four_mapinfo_pool = append(tier_four_mapinfo_pool, fmt.Sprintf("%s: T%d", mapp, map_tier))
}
rand.Shuffle(len(tier_four_mapinfo_pool), func(i, j int) {
tier_four_mapinfo_pool[i], tier_four_mapinfo_pool[j] = tier_four_mapinfo_pool[j], tier_four_mapinfo_pool[i]
})
for i, mapp := range tier_four_mapinfo_pool {
mapinfo_pool = append(mapinfo_pool, mapp)
if i >= tier_four_percentage {
break
}
}
// get 10% of the oldest and add them into the canidates
// Read in all completion records!
var records []JumpmapRunTimeEntry
for _, mapp := range completed_map_list {
if !IsOnMapIgnorelist(mapp) {
run_record := ReadRunRecordFile(wanted_class, mapp)
records = append(records, run_record)
}
}
sort.Slice(records, func(i, j int) bool {
return records[i].Date < records[j].Date
})
oldest_percentage := int(float64(len(records)) * 0.08)
for i, mapp := range records {
mapinfo_pool = append(mapinfo_pool, fmt.Sprintf("%s: Oldest Completed: %s in %s", mapp.Name, time.Unix(int64(mapp.Date), 0).Format("2006-01-02/15:04"), GetPrettyMapTime(wanted_class, mapp.Name)))
if i >= oldest_percentage {
break
}
}
// do the above with the 3% slowest times!, re-using the above record list but re-sorting and selecting from it.
sort.Slice(records, func(i, j int) bool {
return records[i].Duration > records[j].Duration
})
slowest_percentage := int(float64(len(records)) * 0.03)
for i, mapp := range records {
mapinfo_pool = append(mapinfo_pool, fmt.Sprintf("%s: Slowest Completed: %s in %s", mapp.Name, time.Unix(int64(mapp.Date), 0).Format("2006-01-02/15:04"), GetPrettyMapTime(wanted_class, mapp.Name)))
if i >= slowest_percentage {
break
}
}
// Manipulate
rand.Shuffle(len(mapinfo_pool), func(i, j int) {
mapinfo_pool[i], mapinfo_pool[j] = mapinfo_pool[j], mapinfo_pool[i]
})
// Print out
fmt.Println("Found list of maps to complete:")
for i, mapinfo_output := range mapinfo_pool {
fmt.Println(mapinfo_output)
if i >= 12 {
break
}
}
case "submit-time":
if argument_length < 3 {
fmt.Println("Error: No time to convert into seconds???...???")
fmt.Println("Error: No map name provided")
PrintSubmitTimeInfo()
os.Exit(1)
}
fmt.Printf("Converted-Time-To-Seconds: %f\n", ConvertPrettyTimeToSeconds(os.Args[2]))
os.Exit(0)
}
if os.Args[1] == "get-curtime" {
currentTime := time.Now()
fractional_nanosecond := float64(currentTime.UnixNano())
unix_seconds := fractional_nanosecond / 1e9
if argument_length < 4 {
fmt.Println("Error: No time to convert into seconds???...???")
PrintSubmitTimeInfo()
os.Exit(1)
}
wanted_class := JumpmapTypeSoldier
if argument_length < 5 {
fmt.Println("Warning: No classname provided, using soldier as default")
} else {
wanted_class = StringToJumpmapRunType(os.Args[4])
}
submitted_time := ConvertPrettyTimeToSeconds(os.Args[3])
wanted_map := FindMapNameFromPartial(os.Args[2])
fmt.Printf("Curtime: %f\n", unix_seconds)
os.Exit(0)
SubmitNewTime(wanted_class, wanted_map, submitted_time)
PrintMapRuntime(wanted_class, wanted_map)
default:
PrintUsageInfo()
}
PrintUsageInfo()
os.Exit(0)
}