From 0adeba4daa2b57a8bc602d49558d24d097e8e2aa Mon Sep 17 00:00:00 2001 From: Hayden Johnson Date: Thu, 19 Feb 2026 20:38:25 -0800 Subject: [PATCH] add debug memory monitoring --- main.odin | 32 +++++++++++++++++++++++++++++--- phase10.odin | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/main.odin b/main.odin index 8dba82b..52d1816 100644 --- a/main.odin +++ b/main.odin @@ -2,6 +2,7 @@ ---------------- Phase 10 Tracker ---------------- + Author: Hayden Johnson This program helps the user keep track of a game of Phase 10 as it is being played. It will keep track of players and their scores over the course of the @@ -14,9 +15,36 @@ */ package phase10 +import "core:os" import "core:fmt" +import "core:mem" main :: proc() { + // Memory tracking when building in debug mode + when ODIN_DEBUG { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + defer { + if len(track.allocation_map) > 0 { + fmt.eprintf( + "=== %v allocations not freed: ===\n", + len(track.allocation_map) + ) + for _, entry in track.allocation_map { + fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location) + } + } + mem.tracking_allocator_destroy(&track) + } + } + + // TODO don't just test this, actually read the fields into a game struct + if len(os.args) > 1 { + importGame(nil, os.args[1]) + } + // Prompt for names buf: [2048]byte names := getNames(buf[:]) @@ -40,13 +68,11 @@ main :: proc() { winner = checkWinner(&game) } - // Print winner + // Print winner and export game fmt.printfln( "%v wins! They had %v points!", game.names[winner], getScore(&game, winner) ) - exportGame(&game, "game.csv") - } diff --git a/phase10.odin b/phase10.odin index 34cc561..1a37517 100644 --- a/phase10.odin +++ b/phase10.odin @@ -137,7 +137,11 @@ getNames :: proc(backingBuffer: []byte) -> []string { // 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) + handle, err := os.open( + filename, + flags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC, + mode = 0o644 + ) if err != nil { fmt.eprintln("Could not open file:", filename) return @@ -152,6 +156,8 @@ exportGame :: proc(game: ^Game, filename: string) { w: csv.Writer csv.writer_init(&w, stream) + // We will store all rows as slices of strings. We will be allocating + // memory for the strings, so we need to free that memory too. records: [dynamic][]string defer { for record in records { @@ -189,7 +195,27 @@ exportGame :: proc(game: ^Game, filename: string) { } // import an existing game from a csv file -importGame :: proc(filename: string) -> ^Game { - // TODO - return nil +importGame :: proc(game: ^Game, filename: string) { + r: csv.Reader + r.trim_leading_space = true + r.reuse_record = true + r.reuse_record_buffer = true + defer csv.reader_destroy(&r) + + csv_data, success := os.read_entire_file(filename, context.allocator) + defer delete(csv_data) + + if success == true { + csv.reader_init_with_string(&r, string(csv_data)) + } else { + fmt.eprintfln("Unable to open file: %v", filename) + return + } + + for r, i, err in csv.iterator_next(&r) { + if err != nil { /* Do something with error */ } + for f, j in r { + fmt.printfln("Record %v, field %v: %q", i, j, f) + } + } }