Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM golang:1.11-alpine AS builder
RUN apk add --no-cache git
RUN go get github.com/pdevine/go-asciisprite
WORKDIR /project
COPY surprise.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o surprise surprise.go

FROM scratch
COPY --from=builder /project/surprise /surprise
ENTRYPOINT ["/surprise"]
20 changes: 20 additions & 0 deletions Dockerfile.cross
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM --platform=$BUILDPLATFORM golang:1.11-alpine AS builder
RUN apk add --no-cache git
RUN go get github.com/pdevine/go-asciisprite
WORKDIR /project
COPY surprise.go .

ARG TARGETOS
ARG TARGETARCH
ENV GOOS=$TARGETOS GOARCH=$TARGETARCH
RUN CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o surprise surprise.go

FROM scratch AS release-linux
COPY --from=builder /project/surprise /surprise
ENTRYPOINT ["/surprise"]

FROM microsoft/nanoserver AS release-windows
COPY --from=builder /project/surprise /surprise.exe
ENTRYPOINT ["\\surprise.exe"]

FROM release-$TARGETOS
198 changes: 198 additions & 0 deletions surprise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package main

import (
"math/rand"
"time"

sprite "github.com/pdevine/go-asciisprite"
tm "github.com/pdevine/go-asciisprite/termbox"
)

var allSprites sprite.SpriteGroup
var Width int
var Height int
var random *rand.Rand

const whale_c0 = `xxxxxxxxxxxxxxx##xxxxxxxx.xxxxx
xxxxxxxxx##x##x##xxxxxxx==xxxxx
xxxxxx##x##x##x##xxxxxx===xxxxx
xx/""""""""""""""""\___/x===xxx
x{ /xx===x
xx\______ o __/xxxxxxx
xxxx\ \ __/xxxxxxxxxx
xxxxx\____\______/xxxxxxxxxxxxx`


const birthday_c0 = `
@@@@@@@@@@@(_)@@@@@@@(_)
@@@@@@@@@@@@# @(_)@@@#@@@(_)@@@@@(_)
@@@@@@@@_-.-#=='-#-===#====#=(_)==.#=.==__
@@@@@@.' # # # # # # `+"`"+`.
@@@@@: # # # # :
@@@@@:. # .:
@@@@@| `+"`"+`-.__ __.-' |
@@@@@| `+"`````"+`""============""''''' |
@@@@@| . . .-. .-. .-. . . |
@@@@@| |-| |-| |-' |-' | |
@@@@@| ' ' ' ' ' ' ' |
@@@@@| .-. .-. .-. .-. . . .-. .-. . . . |
@@_.-| |( | |( | |-| | ) |-| | | |-._
.' '. '_' '-' ' ' ' . . '-' ' ' ' . .' `+"`"+`.
: `+"`"+`-.__ __.-' :
~. `+"`````"+`""""""""""""""""''''' .'
@@@`+"`"+`-.._ _..-'
@@@@@@@@`+"`````"+`""""-----_____------""""'''''`

type Cake struct {
sprite.BaseSprite
Timer int
TimeOut int
}

type Confetti struct {
sprite.BaseSprite
Timer int
TimeOut int
VY int
VYTimer int
VYTimeOut int
}

type Flicker struct {
sprite.BaseSprite
Timer int
}

func NewCake() *Cake {
s := &Cake{BaseSprite: sprite.BaseSprite{
Visible: true,
Y: Height/2},
}
s.AddCostume(sprite.NewCostume(birthday_c0, '@'))
s.X = Width/2 - s.Width/2

points := []sprite.Point{
sprite.Point{12, 0},
sprite.Point{22, 0},
sprite.Point{17, 1},
sprite.Point{27, 1},
sprite.Point{35, 1},
sprite.Point{30, 2},
}

for cnt, p := range points {
f := &Flicker{BaseSprite: sprite.BaseSprite{
Visible: true,
X: s.X+p.X,
Y: s.Y+p.Y,
},
}
f.AddCostume(sprite.NewCostume(")", '@'))
f.AddCostume(sprite.NewCostume("(", '@'))
f.CurrentCostume = cnt%2
allSprites.Sprites = append(allSprites.Sprites, f)
}

return s
}

func (f *Flicker) Update() {
f.Timer = f.Timer + 1
if f.Timer > 4 {
if f.CurrentCostume == 0 {
f.CurrentCostume = 1
} else {
f.CurrentCostume = 0
}
f.Timer = 0
}
}

func NewConfetti() *Confetti {
s := &Confetti{BaseSprite: sprite.BaseSprite{
Visible: true},
Timer: 0,
TimeOut: 3}
s.AddCostume(sprite.NewCostume(whale_c0, 'x'))
s.AddCostume(sprite.NewCostume(whale_c0, 'x'))
s.X = random.Intn(Width)
s.Y = -random.Intn(Height)
s.VY = random.Intn(2) + 1
s.VYTimer = 0
s.VYTimeOut = 2
return s
}

func (s *Confetti) Update() {
s.Timer = s.Timer + 1
s.VYTimer = s.VYTimer + 1
if s.Timer > 2 {
if s.CurrentCostume == 0 {
s.CurrentCostume = 1
} else {
s.CurrentCostume = 0
}
s.Timer = 0
}
if s.VYTimer > s.VYTimeOut {
s.Y = s.Y + s.VY
if s.Y > Height {
s.Y = 0 - s.Height
}
s.VYTimer = 0
}
}

func main() {
// XXX - Wait a bit until the terminal is properly initialized
time.Sleep(1000 * time.Millisecond)
random = rand.New(rand.NewSource(time.Now().UnixNano()))

err := tm.Init()
if err != nil {
panic(err)
}
defer tm.Close()

Width, Height = tm.Size()

event_queue := make(chan tm.Event)
go func() {
for {
event_queue <- tm.PollEvent()
}
}()

for n := 0; n < 40; n++ {
c := NewConfetti()
allSprites.Sprites = append(allSprites.Sprites, c)
}
cake := NewCake()
allSprites.Sprites = append(allSprites.Sprites, cake)

txt := "Press 'ESC' to quit."
c := sprite.NewCostume(txt, '~')
text := sprite.NewBaseSprite(Width/2-len(txt)/2, Height-2, c)
allSprites.Sprites = append(allSprites.Sprites, text)

mainloop:
for {
tm.Clear(tm.ColorDefault, tm.ColorDefault)

select {
case ev := <-event_queue:
if ev.Type == tm.EventKey {
if ev.Key == tm.KeyCtrlC || ev.Key == tm.KeyEsc || ev.Ch == 'q' {
break mainloop
}
} else if ev.Type == tm.EventResize {
Width = ev.Width
Height = ev.Height
}
default:
allSprites.Update()
allSprites.Render()
time.Sleep(30 * time.Millisecond)
}
}
}