inital work on pong
This commit is contained in:
parent
1482e6e679
commit
b0f31f40c1
2 changed files with 160 additions and 7 deletions
|
|
@ -2,11 +2,21 @@ const std = @import("std");
|
|||
const gpa = std.heap.page_allocator;
|
||||
|
||||
const Conway = @import("conway.zig");
|
||||
const Pong = @import("pong.zig");
|
||||
|
||||
const games = enum(u16) {
|
||||
@"Conways Game of Life",
|
||||
// Pong,
|
||||
};
|
||||
|
||||
fn run_game(game: i64, width: u32, height: u32, writer: *std.Io.Writer) !void {
|
||||
switch (game) {
|
||||
@intFromEnum(games.@"Conways Game of Life") => try Conway.play(width, height, writer),
|
||||
// @intFromEnum(games.Pong) => try Pong.play(width, height, writer),
|
||||
else => @panic("Game does not exist."),
|
||||
}
|
||||
}
|
||||
|
||||
fn ui(width: u32, height: u32, writer: *std.Io.Writer) !void {
|
||||
var fd = [_]std.posix.pollfd{.{
|
||||
.fd = std.posix.STDIN_FILENO,
|
||||
|
|
@ -42,7 +52,7 @@ fn ui(width: u32, height: u32, writer: *std.Io.Writer) !void {
|
|||
cursor_y = @intCast(std.math.clamp(cursor_y, 0, @typeInfo(games).@"enum".fields.len - 1));
|
||||
inline for (@typeInfo(games).@"enum".fields, 0..) |f, i| {
|
||||
try writer.print("\x1B[{};{}H", .{ // y, x
|
||||
((height + i) / 2) + 3,
|
||||
((height) / 2) + 3 + i,
|
||||
(width - f.name.len) / 2,
|
||||
});
|
||||
try writer.print("{s} {s} \x1B[0m\n", .{
|
||||
|
|
@ -65,7 +75,7 @@ fn ui(width: u32, height: u32, writer: *std.Io.Writer) !void {
|
|||
'k' => cursor_y -= 1,
|
||||
'g' => cursor_y = 1,
|
||||
'G' => cursor_y = @intFromEnum(games.@"Conways Game of Life"),
|
||||
' ' => {
|
||||
' ', '\n' => {
|
||||
// reset the terminal state for the game
|
||||
try std.posix.tcsetattr(std.posix.STDIN_FILENO, std.posix.TCSA.NOW, oldios);
|
||||
_ = try writer.write("\x1B[?25h"); // show cursor
|
||||
|
|
@ -73,10 +83,7 @@ fn ui(width: u32, height: u32, writer: *std.Io.Writer) !void {
|
|||
// not much we can do
|
||||
};
|
||||
|
||||
switch (cursor_y) {
|
||||
@intFromEnum(games.@"Conways Game of Life") => try Conway.play(width, height, writer),
|
||||
else => @panic("Game does not exist."),
|
||||
}
|
||||
try run_game(cursor_y, width, height, writer);
|
||||
|
||||
_ = try writer.write("\x1B[?25l"); // hide cursor
|
||||
try writer.flush();
|
||||
|
|
|
|||
146
src/subcmds/arcade/pong.zig
Normal file
146
src/subcmds/arcade/pong.zig
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
const Pong = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const gpa = std.heap.page_allocator;
|
||||
|
||||
width: u64,
|
||||
height: u64,
|
||||
writer: *std.io.Writer,
|
||||
players: []Player,
|
||||
ball: Ball,
|
||||
mode: Mode,
|
||||
cps: f32,
|
||||
|
||||
const Player = struct {
|
||||
score: u8 = 0,
|
||||
/// top y position
|
||||
paddle_y: u64,
|
||||
paddle_x: u64,
|
||||
|
||||
const paddle_height = 4;
|
||||
};
|
||||
const Ball = struct { x: f32, y: f32, vx: f16, vy: f16 };
|
||||
const Mode = enum{ easy, hard };
|
||||
|
||||
pub fn init(mode: Mode, width: u64, height: u64, writer: *std.Io.Writer) !Pong {
|
||||
std.debug.assert(width > 0);
|
||||
std.debug.assert(height > 0);
|
||||
|
||||
const self: Pong = .{
|
||||
.width = width,
|
||||
.height = height,
|
||||
.writer = writer,
|
||||
|
||||
.players = try gpa.alloc(Player, 2),
|
||||
.mode = mode,
|
||||
.ball = .{
|
||||
.x = @as(f32, @floatFromInt(width)) / 2.0,
|
||||
.y = @as(f32, @floatFromInt(height)) / 2.0,
|
||||
.vx = 0.5,
|
||||
.vy = 0.2,
|
||||
},
|
||||
.cps = switch (mode) {
|
||||
Mode.easy => @as(f32, @floatFromInt(width)) / 1.3,
|
||||
Mode.hard => @as(f32, @floatFromInt(width)) / 0.65,
|
||||
},
|
||||
};
|
||||
|
||||
self.players[0] = Player{
|
||||
.paddle_y = height / 2,
|
||||
.paddle_x = 0,
|
||||
.score = 0,
|
||||
};
|
||||
self.players[1] = .{
|
||||
.paddle_y = height / 2,
|
||||
.paddle_x = width - 1,
|
||||
.score = 0,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// must be called self.cps times per second
|
||||
pub fn step(self: *Pong) !void {
|
||||
const next_x = self.ball.x + 1 / self.ball.vx;
|
||||
const next_y = self.ball.y + 1 / self.ball.vy;
|
||||
|
||||
if (next_y < 0 or next_y > @as(f32, @floatFromInt(self.height - 1))) self.ball.vy = -self.ball.vy;
|
||||
for (self.players) |p| {
|
||||
if ((@floor(next_x) == @as(f32, @floatFromInt(p.paddle_x))) and
|
||||
(@floor(next_y) >= @as(f32, @floatFromInt(p.paddle_y)) and
|
||||
@floor(next_y) <= @as(f32, @floatFromInt(p.paddle_y + Player.paddle_height)))) {
|
||||
self.ball.vx = -self.ball.vx;
|
||||
|
||||
// The paddle is 4 high but divided into 8 sections.
|
||||
// This is done so that we may change the vy of the ball after its
|
||||
// hit the paddle which allows the ball to traverse an arbitrary
|
||||
// path.
|
||||
//
|
||||
// I've encoded it in the struct below which allows us to generate
|
||||
// the if branching at comptime.
|
||||
const strong = 0.8;
|
||||
const medium = 0.4;
|
||||
const weak = 0.2;
|
||||
const zero = 0;
|
||||
const result_vy = .{
|
||||
.{ 0.5, strong },
|
||||
.{ 1, medium },
|
||||
.{ 1.5, weak },
|
||||
.{ 2, zero },
|
||||
.{ 2.5, zero },
|
||||
.{ 3, weak },
|
||||
.{ 3.5, medium },
|
||||
.{ 4, strong },
|
||||
};
|
||||
self.ball.vy = vy: {
|
||||
inline for (result_vy) |f| {
|
||||
if (next_y <= @as(f32, @floatFromInt(p.paddle_y)) + f[0]) {
|
||||
break: vy f[1];
|
||||
}
|
||||
}
|
||||
unreachable; // it should be impossible to be outside of the paddle
|
||||
};
|
||||
} else if (@floor(next_x) == @as(f32, @floatFromInt(p.paddle_x))) {
|
||||
// scored
|
||||
var mult: f16 = 1;
|
||||
if (std.meta.eql(p, self.players[0])) {
|
||||
self.players[1].score += 1;
|
||||
mult = -mult;
|
||||
} else {
|
||||
self.players[0].score += 1;
|
||||
mult = mult;
|
||||
}
|
||||
|
||||
self.ball = .{
|
||||
.x = @as(f32, @floatFromInt(self.width)) / 2.0,
|
||||
.y = @as(f32, @floatFromInt(self.height)) / 2.0,
|
||||
.vx = 0.5 * mult,
|
||||
.vy = self.ball.vy,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
self.ball.x += self.ball.vx;
|
||||
self.ball.y += self.ball.vy;
|
||||
}
|
||||
|
||||
pub fn play(width: u32, height: u32, writer: *std.Io.Writer) !void {
|
||||
var pong = try Pong.init(.hard, width, height, writer);
|
||||
|
||||
// var fd = [_]std.posix.pollfd{.{
|
||||
// .fd = std.posix.STDIN_FILENO,
|
||||
// .events = std.posix.POLL.IN,
|
||||
// .revents = 0
|
||||
// }};
|
||||
|
||||
|
||||
|
||||
while (true) {
|
||||
try pong.writer.print("\x1B[{}A", .{height});
|
||||
|
||||
try pong.writer.print("\x1B[{};{}H", .{pong.ball.y, pong.ball.x});
|
||||
try pong.writer.writeAll("o");
|
||||
try pong.writer.flush();
|
||||
try pong.step();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue