add: bouncing ball exercise #2

This commit is contained in:
Inanc Gumus
2019-03-14 21:16:15 +03:00
parent 3271dd5e63
commit cc32a31c1b
5 changed files with 249 additions and 27 deletions

View File

@ -2,13 +2,11 @@
Use the following tips only when you get stuck. This document isn't in a particular order, please do not follow it like so. Use the following tips only when you get stuck. This document isn't in a particular order, please do not follow it like so.
## CALCULATING THE VELOCITY
## Ball position and velocity
You can use velocity to change the ball's speed and position. In my example, the speed is constant, so I always use unit value: 1. You can use velocity to change the ball's speed and position. In my example, the speed is constant, so I always use unit value: 1.
On each loop step: Add velocities to ball's position. This will make the ball move. * On each loop step: Add velocities to ball's position. This will make the ball move.
* **Velocity means: Speed and Direction** * **Velocity means: Speed and Direction**
@ -18,36 +16,26 @@ On each loop step: Add velocities to ball's position. This will make the ball mo
* Y velocity = -1 -> _ball moves up_ * Y velocity = -1 -> _ball moves up_
* **For more information on graphics and velocity:** * **For more information on graphics and velocity:**
* Youtube: Crash Course: 2D Graphics
-> https://www.youtube.com/watch?v=7Jr0SFMQ4Rs&t=529
* Youtube: Crash Course: Velocity * [Youtube: Crash Course: 2D Graphics](https://www.youtube.com/watch?v=7Jr0SFMQ4Rs&t=529)
-> https://www.youtube.com/watch?v=ZM8ECpBuQYE
* [Youtube: Crash Course: Velocity](https://www.youtube.com/watch?v=ZM8ECpBuQYE)
## CREATE THE BOARD ## CREATING THE BOARD
I use `[][]bool` for the board but you can use anything you like. For example, you can directly use `[][]rune` or `[]rune`. Experiment with them and decide which one is the best for you. I use `[][]bool` for the board but you can use anything you like. For example, you can directly use `[][]rune` or `[]rune`. Experiment with them and decide which one is the best for you.
## CLEARING THE SCREEN
## CLEAR THE SCREEN * Before the loop, clear the screen once by using my [screen package](https://github.com/inancgumus/screen), click on the link. You can find its [documentation here](https://godoc.org/github.com/inancgumus/screen).
* Before the loop, clear the screen once by using my screen package.
* After each loop step, move the cursor to the top-left position by using the screen package. So that you can draw the animation frame all over again in the same position. * After each loop step, move the cursor to the top-left position by using the screen package. So that you can draw the animation frame all over again in the same position.
* It's [here](https://github.com/inancgumus/screen). * You can find more information about the screen package and screen clearing in the [Retro Clock project section lectures](https://github.com/inancgumus/learngo/tree/master/15-project-retro-led-clock).
* Its documentation is [here](https://godoc.org/github.com/inancgumus/screen).
* You can find more information about it in the Retro Clock project section.
## DRAWING THE BOARD
## DRAW THE BOARD Instead of drawing the board and the ball to the screen everytime, you will fill a buffer, and when you complete, you can draw the board and the ball once by printing the buffer. I use a `[]rune` slice as a buffer because `rune` can store an emoji character.
Instead of drawing the board and the ball to the screen everytime, you will fill a buffer, and when you complete, you can draw the board and the ball once by printing the buffer.
I use a `[]rune` buffer because `rune` can store an emoji character, and I use a slice of runes because I want to draw empty cells and a ball in the end.
* Make a large enough rune slice named `buf` using the `make` function. * Make a large enough rune slice named `buf` using the `make` function.
@ -65,10 +53,10 @@ var buffer []rune
str := string(buffer) str := string(buffer)
``` ```
## SLOWING DOWN THE SPEED
## SLOW DOWN THE SPEED
Call the `time.Sleep` function to slow down the speed of the loop a little bit, so you can see the ball :) Call the `time.Sleep` function to slow down the speed of the loop a little bit, so you can see the ball :)
`time.Sleep(time.Second / 20)` ```go
time.Sleep(time.Second / 20)
```

View File

@ -20,6 +20,9 @@
// //
// 1. Go here: https://godoc.org/golang.org/x/crypto/ssh/terminal // 1. Go here: https://godoc.org/golang.org/x/crypto/ssh/terminal
// //
// Download the package:
// go get -u golang.org/x/crypto/ssh/terminal
//
// 2. Find the function that gives you the width and height // 2. Find the function that gives you the width and height
// of the terminal. // of the terminal.
// //
@ -47,7 +50,7 @@
// columns (or cells). Ordinary characters have a // columns (or cells). Ordinary characters have a
// single column. // single column.
// //
// 1. Get the width of the ball emoji using function // 1. Get the width of the ball emoji using a function
// from the following package: // from the following package:
// //
// go get -u github.com/mattn/go-runewidth // go get -u github.com/mattn/go-runewidth

View File

@ -0,0 +1,126 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"fmt"
"time"
"github.com/inancgumus/screen"
"github.com/mattn/go-runewidth"
)
// ---------------------------------------------------------
// EXERCISE: Previous positions
//
// Let's optimize the program once more. This time you're
// going to optimize the clearing off the previous positions.
//
// 1. Find the code below marked as "remove the previous ball"
//
// 2. Instead of clearing every position on the board to false,
// only set the previous position to false. So, don't use
// a loop, remove it.
//
// 3. Change the velocity of the ball like so:
//
// vx, vy = 5, 2
//
// 4. Run the program and solve the problem
//
//
// HINT
//
// Don't forget saving the previous position.
//
// ---------------------------------------------------------
func main() {
const (
cellEmpty = ' '
cellBall = '⚾'
maxFrames = 1200
speed = time.Second / 20
)
var (
px, py int // ball position
vx, vy = 1, 1 // velocities
cell rune // current cell (for caching)
)
// you can get the width and height using the screen package easily:
width, height := screen.Size()
// get the rune width of the ball emoji
ballWidth := runewidth.RuneWidth(cellBall)
// adjust the width and height
width /= ballWidth
height-- // there is a 1 pixel border in my terminal
// create the board
board := make([][]bool, width)
for row := range board {
board[row] = make([]bool, height)
}
// create a drawing buffer
buf := make([]rune, 0, width*height)
// clear the screen once
screen.Clear()
for i := 0; i < maxFrames; i++ {
// calculate the next ball position
px += vx
py += vy
// when the ball hits a border reverse its direction
if px <= 0 || px >= width-1 {
vx *= -1
}
if py <= 0 || py >= height-1 {
vy *= -1
}
// remove the previous ball
for y := range board[0] {
for x := range board {
board[x][y] = false
}
}
// put the new ball
board[px][py] = true
// rewind the buffer (allow appending from the beginning)
buf = buf[:0]
// draw the board into the buffer
for y := range board[0] {
for x := range board {
cell = cellEmpty
if board[x][y] {
cell = cellBall
}
buf = append(buf, cell, ' ')
}
buf = append(buf, '\n')
}
// print the buffer
screen.MoveTopLeft()
fmt.Print(string(buf))
// slow down the animation
time.Sleep(speed)
}
}

View File

@ -0,0 +1,101 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"fmt"
"time"
"github.com/inancgumus/screen"
"github.com/mattn/go-runewidth"
)
func main() {
const (
cellEmpty = ' '
cellBall = '⚾'
maxFrames = 1200
speed = time.Second / 20
)
var (
px, py int // ball position
ppx, ppy int // previous ball position
vx, vy = 5, 2 // velocities
cell rune // current cell (for caching)
)
// you can get the width and height using the screen package easily:
width, height := screen.Size()
// get the rune width of the ball emoji
ballWidth := runewidth.RuneWidth(cellBall)
// adjust the width and height
width /= ballWidth
height-- // there is a 1 pixel border in my terminal
// create the board
board := make([][]bool, width)
for row := range board {
board[row] = make([]bool, height)
}
// create a drawing buffer
buf := make([]rune, 0, width*height)
// clear the screen once
screen.Clear()
for i := 0; i < maxFrames; i++ {
// calculate the next ball position
px += vx
py += vy
// when the ball hits a border reverse its direction
if px <= 0 || px >= width-1 {
vx *= -1
}
if py <= 0 || py >= height-1 {
vy *= -1
}
// check whether the ball goes beyond the borders
if px < width && py < height {
// remove the previous ball and put the new ball
board[px][py], board[ppx][ppy] = true, false
// save the previous positions
ppx, ppy = px, py
}
// rewind the buffer (allow appending from the beginning)
buf = buf[:0]
// draw the board into the buffer
for y := range board[0] {
for x := range board {
cell = cellEmpty
if board[x][y] {
cell = cellBall
}
buf = append(buf, cell, ' ')
}
buf = append(buf, '\n')
}
// print the buffer
screen.MoveTopLeft()
fmt.Print(string(buf))
// slow down the animation
time.Sleep(speed)
}
}

View File

@ -4,3 +4,7 @@
In this exercise, your goal is getting the width and height of the terminal screen from your operating system (instead of setting the width and height manually). In this exercise, your goal is getting the width and height of the terminal screen from your operating system (instead of setting the width and height manually).
2. **[Previous positions](https://github.com/inancgumus/learngo/tree/master/18-bouncing-ball-project/exercises/02-previous-positions)**
Let's optimize the program once more. This time you're going to optimize the clearing off the previous positions.