package main import ( "bufio" "fmt" "math/rand" "os" "os/exec" "strings" "time" ) const max_rows = 23 const max_cols = 80 const max_len = max_rows * max_cols + max_rows - 1 type player struct { row int col int dir int p string } type bg struct { tick int tunnel string l int r int c_count int c_to int pl player } var game bg func SetCharMode(on bool) { cmd := exec.Command("stty", "-cbreak", "echo") if on { cmd = exec.Command("stty", "cbreak", "-echo") } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout err := cmd.Run() if err != nil { panic(err) } fmt.Print("\033[?25l") } func makeBg(p player) bg { out := bg{0, "", 3, 77, 0, 5, p} var line strings.Builder line.WriteString("####") for i := 0; i < 72; i++ { line.WriteString(" ") } line.WriteString("####") out.tunnel = line.String() return out } func makePlayer() player { return player{4, max_cols / 2, 0, "!"} } func exit(code int) { SetCharMode(false) fmt.Print("\033[2J\033[0;0H\033[?25h") os.Exit(0) } func getInput(ch chan int) { var m int reader := bufio.NewReader(os.Stdin) for { r, _, err := reader.ReadRune() if err != nil { close(ch) panic("Rune read error") } switch r { case 'h', 'H': m = -1 case 'l', 'L': m = 1 case 'k', 'K': m = 0 case 'r', 'R': m = 100 case 'q', 'Q': exit(0) default: continue } ch <- m } } func (b *bg) newLeft() int { rand.Seed(time.Now().UnixNano()) var change int if b.c_count > 0 && b.c_count < b.c_to { b.c_count++ if b.l > 4 { return rand.Intn(2) - 1 } return rand.Intn(2) } else { b.c_count = 0 } change = rand.Intn(4) - 1 if b.r - b.l < 5 { change = rand.Intn(2) - 1 b.c_count++ b.c_to = rand.Intn(8) + rand.Intn(4) + 1 } if b.l < 4 || change > 1 { change = 1 } if change < -1 { change = -1 } return change } func (b *bg) newRight() int { rand.Seed(time.Now().UnixNano()) var change int = 100 if b.c_count > 1 { if b.r < max_cols - 5 { return 1 } return rand.Intn(2) - 1 } if b.r - b.l < 4 { change = rand.Intn(2) } if b.r > max_cols - 4 { change = -1 } if change > 10 { change = rand.Intn(2) - 1 } if change > 1 { change = 1 } if change < -1 { change = -1 } return change } func (b *bg) buildNextLine() string { b.l = b.l + b.newLeft() b.r = b.r + b.newRight() if b.l < 4 { b.l = 4 } if b.r > max_cols - 4 { b.r = max_cols - 4 } var out strings.Builder out.WriteString("\n") out.WriteString(strings.Repeat("#", b.l)) out.WriteString(strings.Repeat(" ", b.r - b.l)) out.WriteString(strings.Repeat("#", max_cols - b.r)) return out.String() } func (b *bg) collision() bool { if b.tunnel[b.pl.row * max_cols + b.pl.col + b.pl.row - 1] == '#' { return true } return false } func (b *bg) update() { ln := b.buildNextLine() var out strings.Builder if len(b.tunnel) <= max_len { out.WriteString(b.tunnel) } else { out.WriteString(b.tunnel[max_cols + 1:]) } out.WriteString(ln) b.tunnel = out.String() b.pl.col += b.pl.dir if b.pl.col < 1 { b.pl.col = 1 } else if b.pl.col > max_cols - 1 { b.pl.col = max_cols - 1 } } func (b *bg) draw() { fmt.Print("\033[0;0H") fmt.Print(b.tunnel) fmt.Printf("\nDepth: %d ft.", b.tick * 6) fmt.Printf("\033[%d;%dH%s",b.pl.row,b.pl.col,b.pl.p) } func main(){ var ch = make(chan int) defer close(ch) SetCharMode(true) defer SetCharMode(false) game := makeBg(makePlayer()) restart: rand.Seed(time.Now().UnixNano()) for i := 0; i < max_rows; i++ { game.update() } fmt.Print("\033[2J\033[0;0H") fmt.Print(` _ _ _ __| |_ _ _ __ _ __ ___| |_ | '_ \ | || | ' \| ' \/ -_) _| | .__/_|\_,_|_|_|_|_|_|_\___|\__| |_|`) fmt.Printf("\033[8;%dHTHREE",max_cols/2-3) time.Sleep(1 * time.Second) fmt.Printf("\033[9;%dHTWO",max_cols/2-2) time.Sleep(1 * time.Second) fmt.Printf("\033[10;%dHONE",max_cols/2-2) time.Sleep(1 * time.Second) go getInput(ch) oldtime := time.Now().UnixNano() / 1000000 for { select { case val, ok := <-ch: if ok && val < 5 { game.pl.dir = val game.pl.col += game.pl.dir game.pl.dir = 0 } default: // do nothing } game.tick++ game.update() game.draw() if game.collision() { time.Sleep(1 * time.Second) fmt.Printf("\033[2J\033[10;%dHGame Over",max_cols/2-6) fmt.Printf("\033[11;%dHDepth: %d ft.",max_cols/2-8, game.tick * 6) fmt.Printf("\033[13;%dHR To Restart",max_cols/2-7) fmt.Printf("\033[14;%dHQ To Exit",max_cols/2-6) for { val := <-ch if val == 100 { game = makeBg(makePlayer()) goto restart } } } newtime := time.Now().UnixNano() / 1000000 if dt := newtime - oldtime; dt < 100 && dt >= 0 { diff := time.Duration(100 - dt) time.Sleep(diff * time.Millisecond) } oldtime = newtime } }