A little game for your terminal
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
4.8 KiB

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
}
}