package main import ( "database/sql" "flag" "fmt" "math/rand" "os" "os/exec" _ "modernc.org/sqlite" ) var verbose = flag.Bool("v", false, "debug output") var file = flag.String("f", "cards", "cards file") type segmentTree struct { N int seg []int } // Build segment tree func (s segmentTree) build(a *sql.Rows, l, r, n int) { if l == r { a.Next() a.Scan(&s.seg[n]) return } m := (l + r) >> 1 s.build(a, l, m, n<<1) s.build(a, m+1, r, n<<1|1) s.seg[n] = s.seg[n<<1] + s.seg[n<<1|1] } // Update value at index x func (s segmentTree) update(x, v, l, r, n int) { if l == r { s.seg[n] = v return } m := (l + r) >> 1 if x <= m { s.update(x, v, l, m, n<<1) } else { s.update(x, v, m+1, r, n<<1|1) } s.seg[n] = s.seg[n<<1] + s.seg[n<<1|1] } // Find element with prefix sum v func (s segmentTree) query(v, l, r, n int) (int, int) { if l == r { return s.seg[n], l } m := (l + r) >> 1 if s.seg[n<<1] >= v { return s.query(v, l, m, n<<1) } else { return s.query(v-s.seg[n<<1], m+1, r, n<<1|1) } } func main() { flag.Parse() db, err := sql.Open("sqlite", *file) if err != nil { panic(err) } // Get number of cards var N int _ = db.QueryRow("SELECT COUNT(*) FROM cards").Scan(&N) s := segmentTree{N, make([]int, 4*N)} // Build segment tree rows, err := db.Query("SELECT weight FROM cards") if err != nil { panic(err) } s.build(rows, 0, N-1, 1) rows.Close() if *verbose { fmt.Println(s) } // https://stackoverflow.com/questions/14094190/function-similar-to-getchar // disable input buffering exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run() // do not display entered characters on the screen exec.Command("stty", "-F", "/dev/tty", "-echo").Run() // restore the echoing state when exiting defer exec.Command("stty", "-F", "/dev/tty", "echo").Run() for { // Choose a random card x := rand.Intn(s.seg[1]) w, i := s.query(x, 0, N-1, 1) if *verbose { fmt.Println(x) fmt.Println(w) fmt.Println(i) } // Get card contents from database var key, val string db.QueryRow("SELECT key, val FROM cards WHERE idx=?", i).Scan(&key, &val) fmt.Println(key) // Wait for confirmation var b []byte = make([]byte, 1) os.Stdin.Read(b) fmt.Println(val) // Read user input os.Stdin.Read(b) if b[0] == byte('y') { w >>= 1 } else if b[0] == byte('n') { w <<= 1 } else { os.Exit(0) } // Update segment tree and database s.update(i, w, 0, N-1, 1) _, err = db.Exec("UPDATE cards SET weight=? WHERE idx=?", w, i) if err != nil { panic(err) } } }