diff options
| author | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
|---|---|---|
| committer | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
| commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
| tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/old/examples/tetris | |
| download | cli-tools-windows-master.tar.gz cli-tools-windows-master.tar.bz2 cli-tools-windows-master.zip  | |
Diffstat (limited to 'v_windows/v/old/examples/tetris')
| -rw-r--r-- | v_windows/v/old/examples/tetris/README.md | 9 | ||||
| -rw-r--r-- | v_windows/v/old/examples/tetris/screenshot.png | bin | 0 -> 23598 bytes | |||
| -rw-r--r-- | v_windows/v/old/examples/tetris/tetris.v | 518 | 
3 files changed, 527 insertions, 0 deletions
diff --git a/v_windows/v/old/examples/tetris/README.md b/v_windows/v/old/examples/tetris/README.md new file mode 100644 index 0000000..353db1e --- /dev/null +++ b/v_windows/v/old/examples/tetris/README.md @@ -0,0 +1,9 @@ +<img src='https://raw.githubusercontent.com/vlang/v/master/examples/tetris/screenshot.png' width=300> + +### Dependencies (Ubuntu) +```sh +sudo apt install libx11-dev +sudo apt install libxi-dev +sudo apt install libxcursor-dev +sudo apt install libgl-dev +``` diff --git a/v_windows/v/old/examples/tetris/screenshot.png b/v_windows/v/old/examples/tetris/screenshot.png Binary files differnew file mode 100644 index 0000000..8b457a1 --- /dev/null +++ b/v_windows/v/old/examples/tetris/screenshot.png diff --git a/v_windows/v/old/examples/tetris/tetris.v b/v_windows/v/old/examples/tetris/tetris.v new file mode 100644 index 0000000..01589a3 --- /dev/null +++ b/v_windows/v/old/examples/tetris/tetris.v @@ -0,0 +1,518 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import rand +import time +import gx +import gg +// import sokol.sapp + +const ( +	block_size      = 20 // virtual pixels +	field_height    = 20 // # of blocks +	field_width     = 10 +	tetro_size      = 4 +	win_width       = block_size * field_width +	win_height      = block_size * field_height +	timer_period    = 250 // ms +	text_size       = 24 +	limit_thickness = 3 +) + +const ( +	text_cfg = gx.TextCfg{ +		align: .left +		size: text_size +		color: gx.rgb(0, 0, 0) +	} +	over_cfg = gx.TextCfg{ +		align: .left +		size: text_size +		color: gx.white +	} +) + +const ( +	// Tetros' 4 possible states are encoded in binaries +	// 0000 0  0000 0  0000 0  0000 0  0000 0  0000 0 +	// 0000 0  0000 0  0000 0  0000 0  0011 3  0011 3 +	// 0110 6  0010 2  0011 3  0110 6  0001 1  0010 2 +	// 0110 6  0111 7  0110 6  0011 3  0001 1  0010 2 +	// There is a special case 1111, since 15 can't be used. +	b_tetros         = [ +		[66, 66, 66, 66], +		[27, 131, 72, 232], +		[36, 231, 36, 231], +		[63, 132, 63, 132], +		[311, 17, 223, 74], +		[322, 71, 113, 47], +		[1111, 9, 1111, 9], +	] +	// Each tetro has its unique color +	colors           = [ +		gx.rgb(0, 0, 0), /* unused ? */ +		gx.rgb(255, 242, 0), /* yellow quad */ +		gx.rgb(174, 0, 255), /* purple triple */ +		gx.rgb(60, 255, 0), /* green short topright */ +		gx.rgb(255, 0, 0), /* red short topleft */ +		gx.rgb(255, 180, 31), /* orange long topleft */ +		gx.rgb(33, 66, 255), /* blue long topright */ +		gx.rgb(74, 198, 255), /* lightblue longest */ +		gx.rgb(0, 170, 170), +	] +	background_color = gx.white +	ui_color         = gx.rgba(255, 0, 0, 210) +) + +// TODO: type Tetro [tetro_size]struct{ x, y int } +struct Block { +mut: +	x int +	y int +} + +enum GameState { +	paused +	running +	gameover +} + +struct Game { +mut: +	// Score of the current game +	score int +	// Lines of the current game +	lines int +	// State of the current game +	state GameState +	// Block size in screen dimensions +	block_size int = block_size +	// Field margin +	margin int +	// Position of the current tetro +	pos_x int +	pos_y int +	// field[y][x] contains the color of the block with (x,y) coordinates +	// "-1" border is to avoid bounds checking. +	// -1 -1 -1 -1 +	// -1  0  0 -1 +	// -1  0  0 -1 +	// -1 -1 -1 -1 +	field [][]int +	// TODO: tetro Tetro +	tetro []Block +	// TODO: tetros_cache []Tetro +	tetros_cache []Block +	// Index of the current tetro. Refers to its color. +	tetro_idx int +	// Idem for the next tetro +	next_tetro_idx int +	// Index of the rotation (0-3) +	rotation_idx int +	// gg context for drawing +	gg          &gg.Context = voidptr(0) +	font_loaded bool +	show_ghost  bool = true +	// frame/time counters: +	frame     int +	frame_old int +	frame_sw  time.StopWatch = time.new_stopwatch() +	second_sw time.StopWatch = time.new_stopwatch() +} + +fn remap(v f32, min f32, max f32, new_min f32, new_max f32) f32 { +	return (((v - min) * (new_max - new_min)) / (max - min)) + new_min +} + +[if showfps ?] +fn (mut game Game) showfps() { +	game.frame++ +	last_frame_ms := f64(game.frame_sw.elapsed().microseconds()) / 1000.0 +	ticks := f64(game.second_sw.elapsed().microseconds()) / 1000.0 +	if ticks > 999.0 { +		fps := f64(game.frame - game.frame_old) * ticks / 1000.0 +		$if debug { +			eprintln('fps: ${fps:5.1f} | last frame took: ${last_frame_ms:6.3f}ms | frame: ${game.frame:6} ') +		} +		game.second_sw.restart() +		game.frame_old = game.frame +	} +} + +fn frame(mut game Game) { +	ws := gg.window_size() +	bs := remap(block_size, 0, win_height, 0, ws.height) +	m := (f32(ws.width) - bs * field_width) * 0.5 +	game.block_size = int(bs) +	game.margin = int(m) +	game.frame_sw.restart() +	game.gg.begin() +	game.draw_scene() +	game.showfps() +	game.gg.end() +} + +fn main() { +	mut game := &Game{ +		gg: 0 +	} +	mut fpath := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) +	$if android { +		fpath = 'fonts/RobotoMono-Regular.ttf' +	} +	game.gg = gg.new_context( +		bg_color: gx.white +		width: win_width +		height: win_height +		create_window: true +		window_title: 'V Tetris' // +		user_data: game +		frame_fn: frame +		event_fn: on_event +		font_path: fpath // wait_events: true +	) +	game.init_game() +	go game.run() // Run the game loop in a new thread +	game.gg.run() // Run the render loop in the main thread +} + +fn (mut g Game) init_game() { +	g.parse_tetros() +	g.next_tetro_idx = rand.intn(b_tetros.len) // generate initial "next" +	g.generate_tetro() +	g.field = [] +	// Generate the field, fill it with 0's, add -1's on each edge +	for _ in 0 .. field_height + 2 { +		mut row := [0].repeat(field_width + 2) +		row[0] = -1 +		row[field_width + 1] = -1 +		g.field << row.clone() +	} +	for j in 0 .. field_width + 2 { +		g.field[0][j] = -1 +		g.field[field_height + 1][j] = -1 +	} +	g.score = 0 +	g.lines = 0 +	g.state = .running +} + +fn (mut g Game) parse_tetros() { +	for b_tetros0 in b_tetros { +		for b_tetro in b_tetros0 { +			for t in parse_binary_tetro(b_tetro) { +				g.tetros_cache << t +			} +		} +	} +} + +fn (mut g Game) run() { +	for { +		if g.state == .running { +			g.move_tetro() +			g.delete_completed_lines() +		} +		// glfw.post_empty_event() // force window redraw +		time.sleep(timer_period * time.millisecond) +	} +} + +fn (g &Game) draw_ghost() { +	if g.state != .gameover && g.show_ghost { +		pos_y := g.move_ghost() +		for i in 0 .. tetro_size { +			tetro := g.tetro[i] +			g.draw_block_color(pos_y + tetro.y, g.pos_x + tetro.x, gx.rgba(125, 125, 225, +				40)) +		} +	} +} + +fn (g Game) move_ghost() int { +	mut pos_y := g.pos_y +	mut end := false +	for !end { +		for block in g.tetro { +			y := block.y + pos_y + 1 +			x := block.x + g.pos_x +			if g.field[y][x] != 0 { +				end = true +				break +			} +		} +		pos_y++ +	} +	return pos_y - 1 +} + +fn (mut g Game) move_tetro() bool { +	// Check each block in current tetro +	for block in g.tetro { +		y := block.y + g.pos_y + 1 +		x := block.x + g.pos_x +		// Reached the bottom of the screen or another block? +		if g.field[y][x] != 0 { +			// The new tetro has no space to drop => end of the game +			if g.pos_y < 2 { +				g.state = .gameover +				return false +			} +			// Drop it and generate a new one +			g.drop_tetro() +			g.generate_tetro() +			return false +		} +	} +	g.pos_y++ +	return true +} + +fn (mut g Game) move_right(dx int) bool { +	// Reached left/right edge or another tetro? +	for i in 0 .. tetro_size { +		tetro := g.tetro[i] +		y := tetro.y + g.pos_y +		x := tetro.x + g.pos_x + dx +		if g.field[y][x] != 0 { +			// Do not move +			return false +		} +	} +	g.pos_x += dx +	return true +} + +fn (mut g Game) delete_completed_lines() { +	for y := field_height; y >= 1; y-- { +		g.delete_completed_line(y) +	} +} + +fn (mut g Game) delete_completed_line(y int) { +	for x := 1; x <= field_width; x++ { +		if g.field[y][x] == 0 { +			return +		} +	} +	g.score += 10 +	g.lines++ +	// Move everything down by 1 position +	for yy := y - 1; yy >= 1; yy-- { +		for x := 1; x <= field_width; x++ { +			g.field[yy + 1][x] = g.field[yy][x] +		} +	} +} + +// Place a new tetro on top +fn (mut g Game) generate_tetro() { +	g.pos_y = 0 +	g.pos_x = field_width / 2 - tetro_size / 2 +	g.tetro_idx = g.next_tetro_idx +	g.next_tetro_idx = rand.intn(b_tetros.len) +	g.rotation_idx = 0 +	g.get_tetro() +} + +// Get the right tetro from cache +fn (mut g Game) get_tetro() { +	idx := g.tetro_idx * tetro_size * tetro_size + g.rotation_idx * tetro_size +	g.tetro = g.tetros_cache[idx..idx + tetro_size].clone() +} + +// TODO mut +fn (mut g Game) drop_tetro() { +	for i in 0 .. tetro_size { +		tetro := g.tetro[i] +		x := tetro.x + g.pos_x +		y := tetro.y + g.pos_y +		// Remember the color of each block +		g.field[y][x] = g.tetro_idx + 1 +	} +} + +fn (g &Game) draw_tetro() { +	for i in 0 .. tetro_size { +		tetro := g.tetro[i] +		g.draw_block(g.pos_y + tetro.y, g.pos_x + tetro.x, g.tetro_idx + 1) +	} +} + +fn (g &Game) draw_next_tetro() { +	if g.state != .gameover { +		idx := g.next_tetro_idx * tetro_size * tetro_size +		next_tetro := g.tetros_cache[idx..idx + tetro_size].clone() +		pos_y := 0 +		pos_x := field_width / 2 - tetro_size / 2 +		for i in 0 .. tetro_size { +			block := next_tetro[i] +			g.draw_block_color(pos_y + block.y, pos_x + block.x, gx.rgb(220, 220, 220)) +		} +	} +} + +fn (g &Game) draw_block_color(i int, j int, color gx.Color) { +	g.gg.draw_rect(f32((j - 1) * g.block_size) + g.margin, f32((i - 1) * g.block_size), +		f32(g.block_size - 1), f32(g.block_size - 1), color) +} + +fn (g &Game) draw_block(i int, j int, color_idx int) { +	color := if g.state == .gameover { gx.gray } else { colors[color_idx] } +	g.draw_block_color(i, j, color) +} + +fn (g &Game) draw_field() { +	for i := 1; i < field_height + 1; i++ { +		for j := 1; j < field_width + 1; j++ { +			if g.field[i][j] > 0 { +				g.draw_block(i, j, g.field[i][j]) +			} +		} +	} +} + +fn (mut g Game) draw_ui() { +	ws := gg.window_size() +	textsize := int(remap(text_size, 0, win_width, 0, ws.width)) +	g.gg.draw_text(1, 3, g.score.str(), text_cfg) +	lines := g.lines.str() +	g.gg.draw_text(ws.width - lines.len * textsize, 3, lines, text_cfg) +	if g.state == .gameover { +		g.gg.draw_rect(0, ws.height / 2 - textsize, ws.width, 5 * textsize, ui_color) +		g.gg.draw_text(1, ws.height / 2 + 0 * textsize, 'Game Over', over_cfg) +		g.gg.draw_text(1, ws.height / 2 + 2 * textsize, 'Space to restart', over_cfg) +	} else if g.state == .paused { +		g.gg.draw_rect(0, ws.height / 2 - textsize, ws.width, 5 * textsize, ui_color) +		g.gg.draw_text(1, ws.height / 2 + 0 * textsize, 'Game Paused', text_cfg) +		g.gg.draw_text(1, ws.height / 2 + 2 * textsize, 'SPACE to resume', text_cfg) +	} +	// g.gg.draw_rect(0, block_size, win_width, limit_thickness, ui_color) +} + +fn (mut g Game) draw_scene() { +	g.draw_ghost() +	g.draw_next_tetro() +	g.draw_tetro() +	g.draw_field() +	g.draw_ui() +} + +fn parse_binary_tetro(t_ int) []Block { +	mut t := t_ +	mut res := [Block{}].repeat(4) +	mut cnt := 0 +	horizontal := t == 9 // special case for the horizontal line +	ten_powers := [1000, 100, 10, 1] +	for i := 0; i <= 3; i++ { +		// Get ith digit of t +		p := ten_powers[i] +		mut digit := t / p +		t %= p +		// Convert the digit to binary +		for j := 3; j >= 0; j-- { +			bin := digit % 2 +			digit /= 2 +			if bin == 1 || (horizontal && i == tetro_size - 1) { +				res[cnt].x = j +				res[cnt].y = i +				cnt++ +			} +		} +	} +	return res +} + +fn on_event(e &gg.Event, mut game Game) { +	// println('code=$e.char_code') +	if e.typ == .key_down { +		game.key_down(e.key_code) +	} +	if e.typ == .touches_began || e.typ == .touches_moved { +		if e.num_touches > 0 { +			touch_point := e.touches[0] +			game.touch_event(touch_point) +		} +	} +} + +fn (mut game Game) rotate_tetro() { +	old_rotation_idx := game.rotation_idx +	game.rotation_idx++ +	if game.rotation_idx == tetro_size { +		game.rotation_idx = 0 +	} +	game.get_tetro() +	if !game.move_right(0) { +		game.rotation_idx = old_rotation_idx +		game.get_tetro() +	} +	if game.pos_x < 0 { +		// game.pos_x = 1 +	} +} + +fn (mut game Game) key_down(key gg.KeyCode) { +	// global keys +	match key { +		.escape { +			exit(0) +		} +		.space { +			if game.state == .running { +				game.state = .paused +			} else if game.state == .paused { +				game.state = .running +			} else if game.state == .gameover { +				game.init_game() +				game.state = .running +			} +		} +		else {} +	} +	if game.state != .running { +		return +	} +	// keys while game is running +	match key { +		.up { +			// Rotate the tetro +			game.rotate_tetro() +		} +		.left { +			game.move_right(-1) +		} +		.right { +			game.move_right(1) +		} +		.down { +			game.move_tetro() // drop faster when the player presses <down> +		} +		.d { +			for game.move_tetro() { +			} +		} +		.g { +			game.show_ghost = !game.show_ghost +		} +		else {} +	} +} + +fn (mut game Game) touch_event(touch_point C.sapp_touchpoint) { +	ws := gg.window_size() +	tx := touch_point.pos_x +	ty := touch_point.pos_y +	if ty < f32(ws.height) * 0.5 { +		game.rotate_tetro() +	} else { +		if tx <= f32(ws.width) * 0.5 { +			game.move_right(-1) +		} else { +			game.move_right(1) +		} +	} +}  | 
