aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/examples/term.ui/vyper.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/examples/term.ui/vyper.v')
-rw-r--r--v_windows/v/old/examples/term.ui/vyper.v475
1 files changed, 475 insertions, 0 deletions
diff --git a/v_windows/v/old/examples/term.ui/vyper.v b/v_windows/v/old/examples/term.ui/vyper.v
new file mode 100644
index 0000000..cf41e60
--- /dev/null
+++ b/v_windows/v/old/examples/term.ui/vyper.v
@@ -0,0 +1,475 @@
+// import modules for use in app
+import term.ui as termui
+import rand
+
+// define some global constants
+const (
+ block_size = 1
+ buffer = 10
+ green = termui.Color{0, 255, 0}
+ grey = termui.Color{150, 150, 150}
+ white = termui.Color{255, 255, 255}
+ blue = termui.Color{0, 0, 255}
+ red = termui.Color{255, 0, 0}
+ black = termui.Color{0, 0, 0}
+)
+
+// what edge of the screen are you facing
+enum Orientation {
+ top
+ right
+ bottom
+ left
+}
+
+// what's the current state of the game
+enum GameState {
+ pause
+ gameover
+ game
+ oob // snake out-of-bounds
+}
+
+// simple 2d vector representation
+struct Vec {
+mut:
+ x int
+ y int
+}
+
+// determine orientation from vector (hacky way to set facing from velocity)
+fn (v Vec) facing() Orientation {
+ result := if v.x >= 0 {
+ Orientation.right
+ } else if v.x < 0 {
+ Orientation.left
+ } else if v.y >= 0 {
+ Orientation.bottom
+ } else {
+ Orientation.top
+ }
+ return result
+}
+
+// generate a random vector with x in [min_x, max_x] and y in [min_y, max_y]
+fn (mut v Vec) randomize(min_x int, min_y int, max_x int, max_y int) {
+ v.x = rand.int_in_range(min_x, max_x)
+ v.y = rand.int_in_range(min_y, max_y)
+}
+
+// part of snake's body representation
+struct BodyPart {
+mut:
+ pos Vec = Vec{
+ x: block_size
+ y: block_size
+ }
+ color termui.Color = green
+ facing Orientation = .top
+}
+
+// snake representation
+struct Snake {
+mut:
+ app &App
+ direction Orientation
+ body []BodyPart
+ velocity Vec = Vec{
+ x: 0
+ y: 0
+ }
+}
+
+// length returns the snake's current length
+fn (s Snake) length() int {
+ return s.body.len
+}
+
+// impulse provides a impulse to change the snake's direction
+fn (mut s Snake) impulse(direction Orientation) {
+ mut vec := Vec{}
+ match direction {
+ .top {
+ vec.x = 0
+ vec.y = -1 * block_size
+ }
+ .right {
+ vec.x = 2 * block_size
+ vec.y = 0
+ }
+ .bottom {
+ vec.x = 0
+ vec.y = block_size
+ }
+ .left {
+ vec.x = -2 * block_size
+ vec.y = 0
+ }
+ }
+ s.direction = direction
+ s.velocity = vec
+}
+
+// move performs the calculations for the snake's movements
+fn (mut s Snake) move() {
+ mut i := s.body.len - 1
+ width := s.app.width
+ height := s.app.height
+ // move the parts of the snake as appropriate
+ for i = s.body.len - 1; i >= 0; i-- {
+ mut piece := s.body[i]
+ if i > 0 { // just move the body of the snake up one position
+ piece.pos = s.body[i - 1].pos
+ piece.facing = s.body[i - 1].facing
+ } else { // verify that the move is valid and move the head if so
+ piece.facing = s.direction
+ new_x := piece.pos.x + s.velocity.x
+ new_y := piece.pos.y + s.velocity.y
+ piece.pos.x += if new_x > block_size && new_x < width - block_size {
+ s.velocity.x
+ } else {
+ 0
+ }
+ piece.pos.y += if new_y > block_size && new_y < height - block_size {
+ s.velocity.y
+ } else {
+ 0
+ }
+ }
+ s.body[i] = piece
+ }
+}
+
+// grow add another part to the snake when it catches the rat
+fn (mut s Snake) grow() {
+ head := s.get_tail()
+ mut pos := Vec{}
+ // add the segment on the opposite side of the previous tail
+ match head.facing {
+ .bottom {
+ pos.x = head.pos.x
+ pos.y = head.pos.y - block_size
+ }
+ .left {
+ pos.x = head.pos.x + block_size
+ pos.y = head.pos.y
+ }
+ .top {
+ pos.x = head.pos.x
+ pos.y = head.pos.y + block_size
+ }
+ .right {
+ pos.x = head.pos.x - block_size
+ pos.y = head.pos.y
+ }
+ }
+ s.body << BodyPart{
+ pos: pos
+ facing: head.facing
+ }
+}
+
+// get_body gets the parts of the snakes body
+fn (s Snake) get_body() []BodyPart {
+ return s.body
+}
+
+// get_head get snake's head
+fn (s Snake) get_head() BodyPart {
+ return s.body[0]
+}
+
+// get_tail get snake's tail
+fn (s Snake) get_tail() BodyPart {
+ return s.body[s.body.len - 1]
+}
+
+// randomize randomizes position and veolcity of snake
+fn (mut s Snake) randomize() {
+ speeds := [-2, 0, 2]
+ mut pos := s.get_head().pos
+ pos.randomize(buffer, buffer, s.app.width - buffer, s.app.height - buffer)
+ for pos.x % 2 != 0 || (pos.x < buffer && pos.x > s.app.width - buffer) {
+ pos.randomize(buffer, buffer, s.app.width - buffer, s.app.height - buffer)
+ }
+ s.velocity.y = rand.int_in_range(-1 * block_size, block_size)
+ s.velocity.x = speeds[rand.intn(speeds.len)]
+ s.direction = s.velocity.facing()
+ s.body[0].pos = pos
+}
+
+// check_overlap determine if the snake's looped onto itself
+fn (s Snake) check_overlap() bool {
+ h := s.get_head()
+ head_pos := h.pos
+ for i in 2 .. s.length() {
+ piece_pos := s.body[i].pos
+ if head_pos.x == piece_pos.x && head_pos.y == piece_pos.y {
+ return true
+ }
+ }
+ return false
+}
+
+fn (s Snake) check_out_of_bounds() bool {
+ h := s.get_head()
+ return h.pos.x + s.velocity.x <= block_size
+ || h.pos.x + s.velocity.x > s.app.width - s.velocity.x
+ || h.pos.y + s.velocity.y <= block_size
+ || h.pos.y + s.velocity.y > s.app.height - block_size - s.velocity.y
+}
+
+// draw draws the parts of the snake
+fn (s Snake) draw() {
+ mut a := s.app
+ for part in s.get_body() {
+ a.termui.set_bg_color(part.color)
+ a.termui.draw_rect(part.pos.x, part.pos.y, part.pos.x + block_size, part.pos.y + block_size)
+ $if verbose ? {
+ text := match part.facing {
+ .top { '^' }
+ .bottom { 'v' }
+ .right { '>' }
+ .left { '<' }
+ }
+ a.termui.set_color(white)
+ a.termui.draw_text(part.pos.x, part.pos.y, text)
+ }
+ }
+}
+
+// rat representation
+struct Rat {
+mut:
+ pos Vec = Vec{
+ x: block_size
+ y: block_size
+ }
+ captured bool
+ color termui.Color = grey
+ app &App
+}
+
+// randomize spawn the rat in a new spot within the playable field
+fn (mut r Rat) randomize() {
+ r.pos.randomize(2 * block_size + buffer, 2 * block_size + buffer, r.app.width - block_size - buffer,
+ r.app.height - block_size - buffer)
+}
+
+[heap]
+struct App {
+mut:
+ termui &termui.Context = 0
+ snake Snake
+ rat Rat
+ width int
+ height int
+ redraw bool = true
+ state GameState = .game
+}
+
+// new_game setups the rat and snake for play
+fn (mut a App) new_game() {
+ mut snake := Snake{
+ body: []BodyPart{len: 1, init: BodyPart{}}
+ app: a
+ }
+ snake.randomize()
+ mut rat := Rat{
+ app: a
+ }
+ rat.randomize()
+ a.snake = snake
+ a.rat = rat
+ a.state = .game
+ a.redraw = true
+}
+
+// initialize the app and record the width and height of the window
+fn init(x voidptr) {
+ mut app := &App(x)
+ w, h := app.termui.window_width, app.termui.window_height
+ app.width = w
+ app.height = h
+ app.new_game()
+}
+
+// event handles different events for the app as they occur
+fn event(e &termui.Event, x voidptr) {
+ mut app := &App(x)
+ match e.typ {
+ .mouse_down {}
+ .mouse_drag {}
+ .mouse_up {}
+ .key_down {
+ match e.code {
+ .up, .w { app.move_snake(.top) }
+ .down, .s { app.move_snake(.bottom) }
+ .left, .a { app.move_snake(.left) }
+ .right, .d { app.move_snake(.right) }
+ .r { app.new_game() }
+ .c {}
+ .p { app.state = if app.state == .game { GameState.pause } else { GameState.game } }
+ .escape, .q { exit(0) }
+ else { exit(0) }
+ }
+ if e.code == .c {
+ } else if e.code == .escape {
+ exit(0)
+ }
+ }
+ else {}
+ }
+ app.redraw = true
+}
+
+// frame perform actions on every tick
+fn frame(x voidptr) {
+ mut app := &App(x)
+ app.update()
+ app.draw()
+}
+
+// update perform any calculations that are needed before drawing
+fn (mut a App) update() {
+ if a.state == .game {
+ a.snake.move()
+ if a.snake.check_out_of_bounds() {
+ $if verbose ? {
+ a.snake.body[0].color = red
+ } $else {
+ a.state = .oob
+ }
+ }
+ if a.snake.check_overlap() {
+ a.state = .gameover
+ return
+ }
+ if a.check_capture() {
+ a.rat.randomize()
+ a.snake.grow()
+ }
+ }
+}
+
+// draw write to the screen
+fn (mut a App) draw() {
+ // reset screen
+ a.termui.clear()
+ a.termui.set_bg_color(white)
+ a.termui.draw_empty_rect(1, 1, a.width, a.height)
+ // determine if a special screen needs to be draw
+ match a.state {
+ .gameover {
+ a.draw_gameover()
+ a.redraw = false
+ }
+ .pause {
+ a.draw_pause()
+ }
+ else {
+ a.redraw = true
+ }
+ }
+ a.termui.set_color(blue)
+ a.termui.set_bg_color(white)
+ a.termui.draw_text(3 * block_size, a.height - (2 * block_size), 'p - (un)pause r - reset q - quit')
+ // draw the snake, rat, and score if appropriate
+ if a.redraw {
+ a.termui.set_bg_color(black)
+ a.draw_gamescreen()
+ if a.state == .oob {
+ a.state = .gameover
+ }
+ }
+ // write to the screen
+ a.termui.reset_bg_color()
+ a.termui.flush()
+}
+
+// move_snake move the snake in specified direction
+fn (mut a App) move_snake(direction Orientation) {
+ a.snake.impulse(direction)
+}
+
+// check_capture determine if the snake overlaps with the rat
+fn (a App) check_capture() bool {
+ snake_pos := a.snake.get_head().pos
+ rat_pos := a.rat.pos
+ return snake_pos.x <= rat_pos.x + block_size && snake_pos.x + block_size >= rat_pos.x
+ && snake_pos.y <= rat_pos.y + block_size && snake_pos.y + block_size >= rat_pos.y
+}
+
+fn (mut a App) draw_snake() {
+ a.snake.draw()
+}
+
+fn (mut a App) draw_rat() {
+ a.termui.set_bg_color(a.rat.color)
+ a.termui.draw_rect(a.rat.pos.x, a.rat.pos.y, a.rat.pos.x + block_size, a.rat.pos.y + block_size)
+}
+
+fn (mut a App) draw_gamescreen() {
+ $if verbose ? {
+ a.draw_debug()
+ }
+ a.draw_score()
+ a.draw_rat()
+ a.draw_snake()
+}
+
+fn (mut a App) draw_score() {
+ a.termui.set_color(blue)
+ a.termui.set_bg_color(white)
+ score := a.snake.length() - 1
+ a.termui.draw_text(a.width - (2 * block_size), block_size, '${score:03d}')
+}
+
+fn (mut a App) draw_pause() {
+ a.termui.set_color(blue)
+ a.termui.draw_text((a.width / 2) - block_size, 3 * block_size, 'Paused!')
+}
+
+fn (mut a App) draw_debug() {
+ a.termui.set_color(blue)
+ a.termui.set_bg_color(white)
+ snake := a.snake
+ a.termui.draw_text(block_size, 1 * block_size, 'Display_width: ${a.width:04d} Display_height: ${a.height:04d}')
+ a.termui.draw_text(block_size, 2 * block_size, 'Vx: ${snake.velocity.x:+02d} Vy: ${snake.velocity.y:+02d}')
+ a.termui.draw_text(block_size, 3 * block_size, 'F: $snake.direction')
+ snake_head := snake.get_head()
+ rat := a.rat
+ a.termui.draw_text(block_size, 4 * block_size, 'Sx: ${snake_head.pos.x:+03d} Sy: ${snake_head.pos.y:+03d}')
+ a.termui.draw_text(block_size, 5 * block_size, 'Rx: ${rat.pos.x:+03d} Ry: ${rat.pos.y:+03d}')
+}
+
+fn (mut a App) draw_gameover() {
+ a.termui.set_bg_color(white)
+ a.termui.set_color(red)
+ a.rat.pos = Vec{
+ x: -1
+ y: -1
+ }
+ x_offset := ' ##### '.len // take half of a line from the game over text and store the length
+ start_x := (a.width / 2) - x_offset
+ a.termui.draw_text(start_x, (a.height / 2) - 3 * block_size, ' ##### ####### ')
+ a.termui.draw_text(start_x, (a.height / 2) - 2 * block_size, ' # # ## # # ###### # # # # ###### ##### ')
+ a.termui.draw_text(start_x, (a.height / 2) - 1 * block_size, ' # # # ## ## # # # # # # # # ')
+ a.termui.draw_text(start_x, (a.height / 2) - 0 * block_size, ' # #### # # # ## # ##### # # # # ##### # # ')
+ a.termui.draw_text(start_x, (a.height / 2) + 1 * block_size, ' # # ###### # # # # # # # # ##### ')
+ a.termui.draw_text(start_x, (a.height / 2) + 2 * block_size, ' # # # # # # # # # # # # # # ')
+ a.termui.draw_text(start_x, (a.height / 2) + 3 * block_size, ' ##### # # # # ###### ####### ## ###### # # ')
+}
+
+fn main() {
+ mut app := &App{}
+ app.termui = termui.init(
+ user_data: app
+ event_fn: event
+ frame_fn: frame
+ init_fn: init
+ hide_cursor: true
+ frame_rate: 10
+ )
+ app.termui.run() ?
+}