package phase10 import "core:io" import "core:encoding/csv" import "core:os" import "core:strings" import "core:strconv" import "core:fmt" import "core:slice" // Struct to hold all information about a game Game :: struct { names: [dynamic]string, scores: [dynamic][dynamic]int, phases: [dynamic]int } // Clean up memory allocated for a Game struct deleteGameData :: proc(game: ^Game) { for scorelist in game.scores { delete(scorelist) } delete(game.scores) delete(game.names) delete(game.phases) } // Prompt user to enter scores for each player addScores :: proc(game: ^Game) { buf: [2048]byte fmt.print(" ") for name in game.names { fmt.print(name, "") } fmt.println() fmt.print("Scores: ") newScores := getSpaceDelimetedItems(buf[:]) if len(newScores) != len(game.names) { fmt.println("[ERROR] Invalid number of scores") return } defer delete(newScores) for score, index in newScores { intScore, _ := strconv.parse_int(score) append(&(game.scores[index]), intScore) } } // Update the status of each player's phase in a game based on scores each round updatePhasesByScores :: proc(game: ^Game) { for name, index in game.names { phase: int = 1 for score in game.scores[index] { if score < 50 { phase += 1 } } game.phases[index] = phase } } // Add a player to the game addPlayer :: proc(game: ^Game, name: string, score: int = 0, phase: int = 1) { append(&game.names, name) append(&game.phases, phase) // Calculate necesasry score sheet to result in score and phase scores: [dynamic]int for i := 1; i < phase - 1; i += 1 { append(&scores, 0) } if score >= 50 { append(&scores, 0) } if (phase != 1) { append(&scores, score) } append(&game.scores, scores) } // Get aggregate score of player by index getScore :: proc(game: ^Game, playerIndex: int) -> int { sum: int = 0 for score in game.scores[playerIndex] { sum += score } return sum } // Returns the index of the player who wins, or -1 if there is no winner checkWinner :: proc(game: ^Game) -> int { winners: [dynamic]int defer delete(winners) for player, index in game.names { if game.phases[index] > 10 { append(&winners, index) } } if len(winners) > 0 { // sort by lowest points context.user_ptr = game point_order :: proc(lhs, rhs: int) -> bool { game: ^Game = (^Game)(context.user_ptr) return getScore(game, lhs) < getScore(game, rhs) } slice.sort_by(winners[:], point_order) return winners[0] } return -1 } // Print state of the game as a nice table printGame :: proc(game: ^Game) { fmt.printf("Name\tScore\tPhase\n") fmt.printf("----\t-----\t-----\n") for name, index in game.names { fmt.printf("%v\t%v\t%v\n", name, getScore(game, index), game.phases[index]) } } // Return a slice of space delimited strings from stdin getSpaceDelimetedItems :: proc(backingBuffer: []byte) -> []string { count, _ := os.read(os.stdin, backingBuffer) response := string(backingBuffer[:count - 1]) // leave off the newline strings.trim_space(response) items, _ := strings.fields(response) return items } // Prompt user for names of all players getNames :: proc(backingBuffer: []byte) -> []string { fmt.print("Enter Names: ") return getSpaceDelimetedItems(backingBuffer) } // export a game as a csv exportGame :: proc(game: ^Game, filename: string) { // Open handle to file handle, err := os.open(filename, flags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC, mode = 0o666) if err != nil { fmt.eprintln("Could not open file:", filename) return } defer os.close(handle) // create stream from file handle stream := os.stream_from_handle(handle) defer io.destroy(stream) // Create CSV Writer w: csv.Writer csv.writer_init(&w, stream) records: [dynamic][]string defer { for record in records { for col in record { delete(col) } delete(record) } delete(records) } // First row is all the names, each subsequent row is the score for a // particular round. It is important that we create a new array for each of // these so we don't accidentally free the actual data in the game struct. nameRow: [dynamic]string for name in game.names { s := strings.clone(name) append(&nameRow, s) } append(&records, nameRow[:]) for i := 0 ; i < len(game.scores[0]); i += 1 { scoreRowString: [dynamic]string for playerScores in game.scores { s := strings.builder_make() strings.write_int(&s, playerScores[i]) scoreCol := strings.to_string(s) append(&scoreRowString, scoreCol) } append(&records, scoreRowString[:]) } // Write the records to the file csv.write_all(&w, records[:]) } // import an existing game from a csv file importGame :: proc(filename: string) -> ^Game { // TODO return nil }