diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2024-08-24 21:34:48 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2024-08-24 21:34:48 -0700 |
| commit | a2e1ec9472d7ff1d3387dc9d059dcee019a84996 (patch) | |
| tree | 9fe251af8607c4844cf2432e9b9c5e1a221417e7 | |
| download | pongo-a2e1ec9472d7ff1d3387dc9d059dcee019a84996.tar.zst | |
Initial commit
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | flake.lock | 26 | ||||
| -rw-r--r-- | flake.nix | 73 | ||||
| -rw-r--r-- | go.mod | 11 | ||||
| -rw-r--r-- | go.sum | 8 | ||||
| -rw-r--r-- | ping.aup3 | bin | 0 -> 57344 bytes | |||
| -rw-r--r-- | ping.mp3 | bin | 0 -> 4016 bytes | |||
| -rw-r--r-- | pongo.go | 215 |
8 files changed, 335 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e72e976 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/pongo +/result diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d26d37b --- /dev/null +++ b/flake.lock @@ -0,0 +1,26 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1724224976, + "narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c374d94f1536013ca8e92341b540eba4c22f9c62", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..b415c15 --- /dev/null +++ b/flake.nix @@ -0,0 +1,73 @@ +{ + description = "pong"; + + # Nixpkgs / NixOS version to use. + inputs.nixpkgs.url = "nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + + # to work with older version of flakes + lastModifiedDate = self.lastModifiedDate or self.lastModified or "19700101"; + + # Generate a user-friendly version number. + version = builtins.substring 0 8 lastModifiedDate; + + # System types to support. + supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; + + # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + + # Nixpkgs instantiated for supported system types. + nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); + + deps = pkgs: with pkgs; [ libGL xorg.libXi xorg.libXcursor xorg.libXrandr xorg.libXinerama wayland libxkbcommon ]; + + in + { + + # Provide some binary packages for selected system types. + packages = forAllSystems (system: + let + pkgs = nixpkgsFor.${system}; + in + { + pongo = pkgs.buildGoModule { + pname = "pongo"; + inherit version; + # In 'nix develop', we don't need a copy of the source tree + # in the Nix store. + src = ./.; + + vendorHash = "sha256-ZWMZkwDMSUuuWCInVJ1YTd0W5GJedGrxYugKv1jhx2U="; + + buildInputs = deps pkgs; + + proxyVendor = true; + postFixup = '' + patchelf $out/bin/pongo --add-rpath ${pkgs.lib.makeLibraryPath [ pkgs.alsa-lib ]} + ''; + }; + }); + + # Add dependencies that are only needed for development + devShells = forAllSystems (system: + let + pkgs = nixpkgsFor.${system}; + in + { + default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ go gopls gotools go-tools ] ++ deps pkgs; + shellHook = '' + export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath [ pkgs.alsa-lib ]} + ''; + }; + }); + + # The default package for 'nix build'. This makes sense if the + # flake provides only one package or there is a clear "main" + # package. + defaultPackage = forAllSystems (system: self.packages.${system}.pongo); + }; +} @@ -0,0 +1,11 @@ +module gitlab.com/rhogenson/pongo + +go 1.22 + +require github.com/gen2brain/raylib-go/raylib v0.0.0-20240807111636-8861ee437da9 + +require ( + github.com/ebitengine/purego v0.7.1 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/sys v0.20.0 // indirect +) @@ -0,0 +1,8 @@ +github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= +github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/gen2brain/raylib-go/raylib v0.0.0-20240807111636-8861ee437da9 h1:voUyZVwDxeiKv31gHmzHY95Oq/+0+ozj8zmKDiaag/o= +github.com/gen2brain/raylib-go/raylib v0.0.0-20240807111636-8861ee437da9/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/ping.aup3 b/ping.aup3 Binary files differnew file mode 100644 index 0000000..da7c415 --- /dev/null +++ b/ping.aup3 diff --git a/ping.mp3 b/ping.mp3 Binary files differnew file mode 100644 index 0000000..87145b8 --- /dev/null +++ b/ping.mp3 diff --git a/pongo.go b/pongo.go new file mode 100644 index 0000000..f4dc038 --- /dev/null +++ b/pongo.go @@ -0,0 +1,215 @@ +package main + +import ( + _ "embed" + "github.com/gen2brain/raylib-go/raylib" + "math" + "strconv" +) + +const ( + screenWidth = 1920 + screenHeight = 1080 + + ballSpeed = 5 + + paddleSpeed = 5 +) + +func printCenter(text string, size int32, color rl.Color) { + rl.DrawText(text, screenWidth/2-rl.MeasureText(text, size), screenHeight/2, size, color) +} + +type rect rl.Rectangle + +func (r rect) bottom() float32 { + return r.Y + r.Height +} + +func (r rect) right() float32 { + return r.X + r.Width +} + +func (r rect) collides(s rect) bool { + return rl.CheckCollisionRecs(rl.Rectangle(r), rl.Rectangle(s)) +} + +func (r rect) draw(color rl.Color) { + rl.DrawRectangleRec(rl.Rectangle(r), color) +} + +type ball struct { + pos rl.Vector2 + velocity rl.Vector2 +} + +func (b *ball) bounds() rect { + const ballSize = 30 + return rect{ + X: b.pos.X, + Y: b.pos.Y, + Width: ballSize, + Height: ballSize, + } +} + +func (b *ball) predictIntersection(targetX float32) (float32, bool) { + if b.velocity.X <= 0 { + return 0, false + } + x := b.pos.X + y := b.pos.Y + velocity := b.velocity + dist := targetX - x + prediction := y + velocity.Y*dist/velocity.X + if prediction < 0 { + bounces := int(-prediction / screenHeight) + if bounces%2 == 0 { + return -prediction - float32(screenHeight*bounces), true + } + return prediction + float32(screenHeight*(bounces+1)), true + } + if prediction > screenHeight { + bounces := int(prediction / screenHeight) + if bounces%2 == 0 { + return prediction - float32(screenHeight*bounces), true + } + return -prediction + float32(screenHeight*(bounces+1)), true + } + return prediction, true +} + +type side int + +const ( + left side = iota + right +) + +func (b *ball) reset(side side) { + b.pos = rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2} + b.velocity = rl.Vector2{X: ballSpeed} + if side == left { + b.velocity.X = -b.velocity.X + } +} + +type paddle struct { + pos float32 + side side +} + +func (p *paddle) bounds() rect { + const ( + paddleHeight = 200 + thickness = 10 + padding = 100 + ) + r := rect{ + X: padding - thickness/2, + Y: p.pos - paddleHeight/2, + Width: thickness, + Height: paddleHeight, + } + if p.side == right { + r.X = screenWidth - padding - thickness/2 + } + return r +} + +//go:embed ping.mp3 +var pingMP3 []byte + +func main() { + rl.InitWindow(screenWidth, screenHeight, "pongo") + defer rl.CloseWindow() + rl.SetTargetFPS(120) + + rl.InitAudioDevice() + defer rl.CloseAudioDevice() + + pingWave := rl.LoadWaveFromMemory(".mp3", pingMP3, int32(len(pingMP3))) + defer rl.UnloadWave(pingWave) + ping := rl.LoadSoundFromWave(pingWave) + defer rl.UnloadSound(ping) + + playerScore := 0 + aiScore := 0 + + var ball ball + ball.reset(left) + + paddle1 := paddle{pos: screenHeight / 2, side: left} + paddle2 := paddle{pos: screenHeight / 2, side: right} + + for !rl.WindowShouldClose() { + switch { + case rl.IsKeyDown(rl.KeyUp) && paddle1.bounds().Y >= 0: + paddle1.pos -= paddleSpeed + case rl.IsKeyDown(rl.KeyDown) && paddle1.bounds().bottom() <= screenHeight: + paddle1.pos += paddleSpeed + } + + if prediction, ok := ball.predictIntersection(paddle2.bounds().X); ok { + switch { + case paddle2.pos < prediction && paddle2.bounds().bottom() <= screenHeight: + paddle2.pos += paddleSpeed + case paddle2.pos > prediction && paddle2.bounds().Y >= 0: + paddle2.pos -= paddleSpeed + } + } + + ball.pos = rl.Vector2Add(ball.pos, ball.velocity) + switch { + case ball.bounds().collides(paddle1.bounds()): + rl.PlaySound(ping) + d := ball.pos.Y - paddle1.pos + if d < 0 { + d = -d + } + angle := d / (paddle1.bounds().Height/2 + ball.bounds().Height) * (75 * 2 * math.Pi / 360) + if ball.pos.Y < paddle1.pos { + angle = -angle + } + speed := ballSpeed * (1 + 4*d/(paddle1.bounds().Height/2+ball.bounds().Height)) + ball.velocity = rl.Vector2{X: speed * float32(math.Cos(float64(angle))), Y: speed * float32(math.Sin(float64(angle)))} + case ball.bounds().collides(paddle2.bounds()): + rl.PlaySound(ping) + d := ball.pos.Y - paddle2.pos + if d < 0 { + d = -d + } + angle := d / (paddle2.bounds().Height/2 + ball.bounds().Height) * (75 * 2 * math.Pi / 360) + if ball.pos.Y < paddle2.pos { + angle = -angle + } + angle += math.Pi + speed := ballSpeed * (1 + 4*d/(paddle2.bounds().Height/2+ball.bounds().Height)) + ball.velocity = rl.Vector2{X: speed * float32(math.Cos(float64(angle))), Y: speed * float32(math.Sin(float64(angle)))} + case ball.bounds().right() <= paddle1.bounds().X: + aiScore++ + ball.reset(right) + case ball.bounds().X >= paddle2.bounds().X: + playerScore++ + ball.reset(left) + case ball.bounds().Y <= 0 || ball.bounds().bottom() >= screenHeight: + ball.velocity.Y = -ball.velocity.Y + rl.PlaySound(ping) + } + + rl.BeginDrawing() + + rl.ClearBackground(rl.Black) + rl.DrawLine(screenWidth/2, 0, screenWidth/2, screenHeight, rl.Gray) + + paddle1.bounds().draw(rl.RayWhite) + paddle2.bounds().draw(rl.RayWhite) + ball.bounds().draw(rl.RayWhite) + + const fontSize = 120 + rl.DrawText(strconv.Itoa(playerScore), int32(paddle1.bounds().X)+20, 30, fontSize, rl.RayWhite) + rl.DrawText(strconv.Itoa(aiScore), int32(paddle2.bounds().X)-rl.MeasureText("9999", fontSize), 30, fontSize, rl.RayWhite) + + rl.EndDrawing() + } +} |
