Created
February 28, 2020 17:48
-
-
Save qrnik/6b8176f49662aa4ff1fad5844fedb1f3 to your computer and use it in GitHub Desktop.
Parser for Google HashCode 2020 online qualification round input & output
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"github.com/pwamej/parser/library" | |
"io/ioutil" | |
"os" | |
"sort" | |
"strconv" | |
"strings" | |
) | |
func main() { | |
if len(os.Args) < 2 { | |
printerr("Command missing, available commands: 'decode', 'encode'") | |
os.Exit(1) | |
} | |
input, err := ioutil.ReadAll(os.Stdin) | |
if err != nil { | |
printerr(fmt.Sprintf("Error while reading stdin: %e", err)) | |
os.Exit(1) | |
} | |
cmd := os.Args[1] | |
switch cmd { | |
case "decode": | |
fmt.Print(decodeInput(string(input))) | |
case "encode": | |
s, err := encodeOutput(string(input)) | |
if err != nil { | |
printerr(fmt.Sprintf("Error while parsing stdin: %e", err)) | |
os.Exit(1) | |
} | |
fmt.Print(s) | |
default: | |
printerr(fmt.Sprintf("Invalid command '%s', available commands: 'decode', 'encode'", cmd)) | |
os.Exit(1) | |
} | |
} | |
func decodeInput(input string) string { | |
result := new(strings.Builder) | |
lines := strings.Split(input, "\n") | |
header := strings.Fields(lines[0]) | |
books := atoi(header[0]) | |
libraryCount := atoi(header[1]) | |
result.WriteString(fmt.Sprintf("B=%d;\n", books)) | |
result.WriteString(fmt.Sprintf("L=%d;\n", libraryCount)) | |
result.WriteString("D=" + header[2] + ";\n") | |
scores := strings.Split(lines[1], " ") | |
result.WriteString("scores=[" + strings.Join(scores, ",") + "];\n") | |
bookCount := make([]int, 0, libraryCount) | |
signupProcessLength := make([]int, 0, libraryCount) | |
booksPerDay := make([]int, 0, libraryCount) | |
hasBook := make([][]bool, libraryCount) | |
for i := range hasBook { | |
hasBook[i] = make([]bool, books) | |
} | |
for i := 2; i <= 2*libraryCount; i += 2 { | |
libraryId := (i - 2) / 2 | |
libraryHeader := toIntSlice(lines[i]) | |
bookCount = append(bookCount, libraryHeader[0]) | |
signupProcessLength = append(signupProcessLength, libraryHeader[1]) | |
booksPerDay = append(booksPerDay, libraryHeader[2]) | |
libraryBooks := toSet(toIntSlice(lines[i+1])) | |
for bookId := range hasBook[libraryId] { | |
hasBook[libraryId][bookId] = contains(libraryBooks, bookId) | |
} | |
} | |
result.WriteString("book_count=" + intSliceToString(bookCount) + ";\n") | |
result.WriteString("signup_process_length=" + intSliceToString(signupProcessLength) + ";\n") | |
result.WriteString("books_per_day=" + intSliceToString(booksPerDay) + ";\n") | |
result.WriteString("has_book=" + boolMatrixToString(hasBook) + ";\n") | |
return result.String() | |
} | |
func encodeOutput(rawModelOutput string) (string, error) { | |
libraries, err := library.Parse(rawModelOutput) | |
if err != nil { | |
return "", err | |
} | |
sort.Sort(BySignupDay(libraries)) | |
for _, l := range libraries { | |
sort.Sort(ByScanDay(l.ScannedBooks)) | |
} | |
result := new(strings.Builder) | |
result.WriteString(fmt.Sprintf("%d\n", len(libraries))) | |
for _, l := range libraries { | |
result.WriteString(fmt.Sprintf("%d %d\n", l.Id, len(l.ScannedBooks))) | |
bookIds := make([]int, len(l.ScannedBooks)) | |
for i, v := range l.ScannedBooks { | |
bookIds[i] = v.Id | |
} | |
result.WriteString(strings.Trim(fmt.Sprint(bookIds), "[]") + "\n") | |
} | |
return result.String(), nil | |
} | |
type BySignupDay []*library.Library | |
func (a BySignupDay) Len() int { | |
return len(a) | |
} | |
func (a BySignupDay) Less(i, j int) bool { | |
return a[i].SignupDay < a[j].SignupDay | |
} | |
func (a BySignupDay) Swap(i, j int) { | |
a[i], a[j] = a[j], a[i] | |
} | |
type ByScanDay []library.Book | |
func (a ByScanDay) Len() int { | |
return len(a) | |
} | |
func (a ByScanDay) Less(i, j int) bool { | |
return a[i].ScanDay < a[j].ScanDay | |
} | |
func (a ByScanDay) Swap(i, j int) { | |
a[i], a[j] = a[j], a[i] | |
} | |
func boolMatrixToString(m [][]bool) string { | |
result := new(strings.Builder) | |
result.WriteString("[") | |
for _, a := range m { | |
result.WriteString("|" + boolSliceToString(a)) | |
} | |
result.WriteString("|]") | |
return result.String() | |
} | |
func boolSliceToString(a []bool) string { | |
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(a)), ","), "[]") | |
} | |
func intSliceToString(a []int) string { | |
return strings.Join(strings.Fields(fmt.Sprint(a)), ",") | |
} | |
func toSet(s []int) map[int]struct{} { | |
set := make(map[int]struct{}) | |
for _, v := range s { | |
set[v] = struct{}{} | |
} | |
return set | |
} | |
func contains(set map[int]struct{}, v int) bool { | |
_, ok := set[v] | |
return ok | |
} | |
func toIntSlice(s string) []int { | |
fields := strings.Fields(s) | |
ints := make([]int, len(fields)) | |
for i, v := range fields { | |
ints[i] = atoi(v) | |
} | |
return ints | |
} | |
func atoi(s string) int { | |
i, _ := strconv.Atoi(s) | |
return i | |
} | |
func printerr(s string) { | |
_, err := fmt.Fprintln(os.Stderr, s) | |
if err != nil { | |
panic("Error when writing to stderr") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment