зеркало из https://github.com/docker/doodle.git
Happy Halloween!
This commit is contained in:
Родитель
4ae3943fca
Коммит
00a4d5252e
|
@ -0,0 +1,10 @@
|
|||
FROM golang:1.13-alpine AS builder
|
||||
RUN apk add --no-cache git
|
||||
RUN go get github.com/pdevine/go-asciisprite && go get github.com/pdevine/goMoonPhase
|
||||
WORKDIR /project
|
||||
COPY *.go /project/
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o halloween halloween.go art.go
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /project/halloween /halloween
|
||||
ENTRYPOINT ["/halloween"]
|
|
@ -0,0 +1,20 @@
|
|||
FROM --platform=$BUILDPLATFORM golang:1.13-alpine AS builder
|
||||
RUN apk add --no-cache git
|
||||
RUN go get github.com/pdevine/go-asciisprite && go get github.com/pdevine/goMoonPhase
|
||||
WORKDIR /project
|
||||
COPY *.go /project/
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ENV GOOS=$TARGETOS GOARCH=$TARGETARCH
|
||||
RUN CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o halloween halloween.go art.go
|
||||
|
||||
FROM scratch AS release-linux
|
||||
COPY --from=builder /project/halloween /halloween
|
||||
ENTRYPOINT ["/summer"]
|
||||
|
||||
FROM mcr.microsoft.com/windows/nanoserver:1809 AS release-windows
|
||||
COPY --from=builder /project/halloween /halloween.exe
|
||||
ENTRYPOINT ["\\halloween.exe"]
|
||||
|
||||
FROM release-$TARGETOS
|
|
@ -0,0 +1,193 @@
|
|||
package main
|
||||
|
||||
const moon_p0 = `
|
||||
xxxx_..._
|
||||
xx.:::::::.
|
||||
x:::::::::::
|
||||
x:::::::::::
|
||||
x`+"`"+`:::::::::'
|
||||
xxx`+"`"+`':::''`
|
||||
|
||||
const moon_p1 = `
|
||||
xxxx_..._
|
||||
xx.::::. `+"`"+`.
|
||||
x:::::::. :
|
||||
x:::::::: :
|
||||
x`+"`"+`::::::' .'
|
||||
xxx`+"`"+`'::'-'`
|
||||
|
||||
const moon_p2 = `
|
||||
xxxx_..._
|
||||
xx.:::: `+"`"+`.
|
||||
x:::::: :
|
||||
x:::::: :
|
||||
x`+"`"+`::::: .'
|
||||
xxx`+"`"+`'::.-'`
|
||||
|
||||
const moon_p3 = `
|
||||
xxxx_..._
|
||||
xx.::' `+"`"+`.
|
||||
x::: :
|
||||
x::: :
|
||||
x`+"`"+`::. .'
|
||||
xxx`+"`"+`':..-'`
|
||||
|
||||
const moon_p4 = `
|
||||
xxxx_..._
|
||||
xx.' `+"`"+`.
|
||||
x: :
|
||||
x: :
|
||||
x`+"`"+`. .'
|
||||
xxx`+"`"+`-...-'`
|
||||
|
||||
const moon_p5 = `
|
||||
xxxx_..._
|
||||
xx.' `+"`"+`::.
|
||||
x: :::
|
||||
x: :::
|
||||
x`+"`"+`. .::'
|
||||
xxx`+"`"+`-..:''`
|
||||
|
||||
const moon_p6 = `
|
||||
xxxx_..._
|
||||
xx.' ::::.
|
||||
x: ::::::
|
||||
x: ::::::
|
||||
x`+"`"+`. :::::'
|
||||
xxx`+"`"+`-.::''`
|
||||
|
||||
const moon_p7 = `
|
||||
xxxx_..._
|
||||
xx.' .::::.
|
||||
x: ::::::::
|
||||
x: ::::::::
|
||||
x`+"`"+`. '::::::'
|
||||
xxx`+"`"+`-.::''`
|
||||
|
||||
const moon_p8 = `
|
||||
xxxx_..._
|
||||
xx.:::::::.
|
||||
x:::::::::::
|
||||
x:::::::::::
|
||||
x`+"`"+`:::::::::'
|
||||
xxx`+"`"+`':::''`
|
||||
|
||||
const happy_halloween_c0 = `
|
||||
@@@xx@@@xxx@@@@@@xxx@@@@@@@xxx@@@@@@@xxx@@@x@@@xxxxx@@@xx@@@xxx@@@@@@xxx@@@xxxxxxx@@@xxxxxxxx@@@@@@xxx@@@xx@@@xx@@@xx@@@@@@@@xx@@@@@@@@xx@@@xx@@@
|
||||
@@@xx@@@xx@@@@@@@@xx@@@@@@@@xx@@@@@@@@xx@@@x@@@xxxxx@@@xx@@@xx@@@@@@@@xx@@@xxxxxxx@@@xxxxxxx@@@@@@@@xx@@@xx@@@xx@@@xx@@@@@@@@xx@@@@@@@@xx@@@@x@@@
|
||||
@@!xx@@@xx@@!xx@@@xx@@!xx@@@xx@@!xx@@@xx@@!x!@@xxxxx@@!xx@@@xx@@!xx@@@xx@@!xxxxxxx@@!xxxxxxx@@!xx@@@xx@@!xx@@!xx@@!xx@@!xxxxxxx@@!xxxxxxx@@!@!@@@
|
||||
!@!xx@!@xx!@!xx@!@xx!@!xx@!@xx!@!xx@!@xx!@!x@!!xxxxx!@!xx@!@xx!@!xx@!@xx!@!xxxxxxx!@!xxxxxxx!@!xx@!@xx!@!xx!@!xx!@!xx!@!xxxxxxx!@!xxxxxxx!@!!@!@!
|
||||
@!@!@!@!xx@!@!@!@!xx@!@@!@!xxx@!@@!@!xxxx!@!@!xxxxxx@!@!@!@!xx@!@!@!@!xx@!!xxxxxxx@!!xxxxxxx@!@xx!@!xx@!!xx!!@xx@!@xx@!!!:!xxxx@!!!:!xxxx@!@x!!@!
|
||||
!!!@!!!!xx!!!@!!!!xx!!@!!!xxxx!!@!!!xxxxxx@!!!xxxxxx!!!@!!!!xx!!!@!!!!xx!!!xxxxxxx!!!xxxxxxx!@!xx!!!xx!@!xx!!!xx!@!xx!!!!!:xxxx!!!!!:xxxx!@!xx!!!
|
||||
!!:xx!!!xx!!:xx!!!xx!!:xxxxxxx!!:xxxxxxxxx!!:xxxxxxx!!:xx!!!xx!!:xx!!!xx!!:xxxxxxx!!:xxxxxxx!!:xx!!!xx!!:xx!!:xx!!:xx!!:xxxxxxx!!:xxxxxxx!!:xx!!!
|
||||
:!:xx!:!xx:!:xx!:!xx:!:xxxxxxx:!:xxxxxxxxx:!:xxxxxxx:!:xx!:!xx:!:xx!:!xxx:!:xxxxxxx:!:xxxxxx:!:xx!:!xx:!:xx:!:xx:!:xx:!:xxxxxxx:!:xxxxxxx:!:xx!:!
|
||||
::xxx:::xx::xxx:::xxx::xxxxxxxx::xxxxxxxxxx::xxxxxxx::xxx:::xx::xxx:::xxx:: ::::xxx:: ::::xx:::::x::xxx::::x::x:::xxxx::x::::xxx::x::::xxx::xxx::
|
||||
x:xxx:x:xxx:xxx:x:xxx:xxxxxxxxx:xxxxxxxxxxx:xxxxxxxxx:xxx:x:xxx:xxx:x:xx:x::x:x:xx:x::x:x:xxx:x:xx:xxxxx::x:xx:x:xxxx:x::x::xxx:x::x::xxx::xxxx:
|
||||
`
|
||||
|
||||
const whale_c0 = `xxxxxxxxxxxxxxx##xxxxxxxx.xxxxx
|
||||
xxxxxxxxx##x##x##xxxxxxx==xxxxx
|
||||
xxxxxx##x##x##x##xxxxxx===xxxxx
|
||||
xx/""""""""""""""""\___/x===xxx
|
||||
x{ /xx===x
|
||||
xx\______ X __/xxxxxxx
|
||||
xxxx\ \ __/xxxxxxxxxx
|
||||
xxxxx\____\______/xxxxxxxxxxxxx`
|
||||
|
||||
const whale_c0_rev = `xxxxx.xxxxxxxx##xxxxxxxxxxxxxxx
|
||||
xxxxx==xxxxxxx##x##x##xxxxxxxxx
|
||||
xxxxx===xxxxxx##x##x##x##xxxxxx
|
||||
xxx===x\___/""""""""""""""""\xx
|
||||
x===xx\ }x
|
||||
xxxxxxx\__ X ______/xx
|
||||
xxxxxxxxxx\__ / /xxxx
|
||||
xxxxxxxxxxxxx\______/____/xxxxx`
|
||||
|
||||
|
||||
const cloud_c0 = `
|
||||
xxxxxxxxx_ _ __.
|
||||
xxxxxxx(' ).
|
||||
xxxxxx( ).
|
||||
xxxxx_( '''.
|
||||
x.=('( . )
|
||||
(( (..____.:'-'
|
||||
'( )_),
|
||||
xx' _______.:' )
|
||||
xxxxxxxxxx-----'`
|
||||
|
||||
const cloud_c1 = `
|
||||
xxxx.--
|
||||
x.+( )
|
||||
x( . )
|
||||
( ( ))
|
||||
x'- __.'`
|
||||
|
||||
const coffin_top_c0 = `
|
||||
xxxxx,________________________________________,`
|
||||
|
||||
const coffin_c0 = `
|
||||
xxx,-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-,
|
||||
xx+==============================================+
|
||||
xx| |
|
||||
x(| ===== ===== ===== |)
|
||||
xx| |
|
||||
xx|______________________________________________|
|
||||
xxx`+"`"+`| |""""""""""""""""""""""""""""""""""""""| |'
|
||||
xxxx| |""""""""""""""""""""""""""""""""""""""| |
|
||||
xxxx| |""""""""""""""""""""""""""""""""""""""| |
|
||||
xxx==============================================`
|
||||
|
||||
|
||||
const bigFlame1 = `
|
||||
xx.
|
||||
x.,
|
||||
xooO,
|
||||
xoOOoO
|
||||
OoOOooO`
|
||||
|
||||
const bigFlame2 = `
|
||||
xx,
|
||||
xxx,+
|
||||
xx,Oo.
|
||||
x,ooOo
|
||||
oO+oOO+`
|
||||
|
||||
const bigFlame3 = `
|
||||
xxx.
|
||||
xx.,
|
||||
xx.oO,
|
||||
x+OO,O
|
||||
OoOOoOO`
|
||||
|
||||
|
||||
const torch = `
|
||||
.=======,
|
||||
( -. _ )
|
||||
`+"`"+`._____,'
|
||||
| |
|
||||
| |
|
||||
| , |
|
||||
| |
|
||||
| |
|
||||
| | |
|
||||
| |
|
||||
|. |
|
||||
=======`
|
||||
|
||||
|
||||
const ghost_c0 = `
|
||||
x.-.
|
||||
(o o)
|
||||
| O \
|
||||
x\ \
|
||||
xx`+"`"+`~~~'
|
||||
`
|
||||
|
||||
const ghost_c1 = `
|
||||
xxx.-.
|
||||
xx(o o)
|
||||
xx/ O |
|
||||
x/ /
|
||||
`+"`"+`~~~'
|
||||
`
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sprite "github.com/pdevine/go-asciisprite"
|
||||
tm "github.com/pdevine/go-asciisprite/termbox"
|
||||
MoonPhase "github.com/pdevine/goMoonPhase"
|
||||
)
|
||||
|
||||
var random *rand.Rand
|
||||
var allSprites sprite.SpriteGroup
|
||||
var Width int
|
||||
var Height int
|
||||
|
||||
const cloud_c0_timer = 22
|
||||
const cloud_c1_timer = 19
|
||||
|
||||
|
||||
type Title struct {
|
||||
sprite.BaseSprite
|
||||
Timer int
|
||||
TimeOut int
|
||||
}
|
||||
|
||||
type Whale struct {
|
||||
sprite.BaseSprite
|
||||
Facing int
|
||||
MinX int
|
||||
MaxX int
|
||||
Timer int
|
||||
TimeOut int
|
||||
TargetX int
|
||||
}
|
||||
|
||||
type Cloud struct {
|
||||
sprite.BaseSprite
|
||||
Timer int
|
||||
TimeOut int
|
||||
VX int
|
||||
}
|
||||
|
||||
type Moon struct {
|
||||
sprite.BaseSprite
|
||||
Timer int
|
||||
TimeOut int
|
||||
PosX float64
|
||||
PosY float64
|
||||
}
|
||||
|
||||
type Flame struct {
|
||||
sprite.BaseSprite
|
||||
Timer int
|
||||
TimeOut int
|
||||
}
|
||||
|
||||
type Ghost struct {
|
||||
sprite.BaseSprite
|
||||
Timer int
|
||||
TimeOut int
|
||||
}
|
||||
|
||||
func NewTitle() *Title {
|
||||
t := &Title{BaseSprite: sprite.BaseSprite{
|
||||
Visible: true,
|
||||
Y: 6},
|
||||
TimeOut: 5,
|
||||
}
|
||||
|
||||
title_list := strings.Split(happy_halloween_c0, "\n")
|
||||
|
||||
for c, _ := range title_list {
|
||||
t.AddCostume(sprite.NewCostume(strings.Join(title_list[0:c], "\n"), 'x'))
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Title) Update() {
|
||||
t.Timer++
|
||||
if t.Timer > t.TimeOut && t.CurrentCostume < len(t.Costumes)-1{
|
||||
t.CurrentCostume++
|
||||
t.Timer = 0
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func NewMoon() *Moon {
|
||||
m := &Moon{BaseSprite: sprite.BaseSprite{
|
||||
Visible: true},
|
||||
TimeOut: 60,
|
||||
}
|
||||
for _, c := range []string{moon_p0, moon_p1, moon_p2, moon_p3, moon_p4, moon_p5, moon_p6, moon_p7, moon_p8} {
|
||||
m.AddCostume(sprite.NewCostume(c, 'x'))
|
||||
}
|
||||
m.CurrentCostume = m.GetPhase()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Moon) GetPhase() int {
|
||||
moon := MoonPhase.New(time.Now())
|
||||
i := int(math.Floor(( moon.Phase() + 0.0625 ) * 8))
|
||||
return i
|
||||
}
|
||||
|
||||
func (m *Moon) Update() {
|
||||
m.Timer++
|
||||
if m.Timer > m.TimeOut {
|
||||
m.PosX++
|
||||
// arc the moon down in a parabola
|
||||
m.PosY = 0.00625 * m.PosX * m.PosX
|
||||
m.X = int(m.PosX)
|
||||
m.Y = int(m.PosY)
|
||||
if m.Y >= 25 {
|
||||
m.Visible = false
|
||||
}
|
||||
m.Timer = 0
|
||||
}
|
||||
}
|
||||
|
||||
func NewCoffin() *sprite.BaseSprite {
|
||||
c := &sprite.BaseSprite{
|
||||
Visible: true,
|
||||
X: 40,
|
||||
Y: 30,
|
||||
}
|
||||
c.AddCostume(sprite.NewCostume(coffin_c0, 'x'))
|
||||
return c
|
||||
}
|
||||
|
||||
func NewCoffinBack() *sprite.BaseSprite {
|
||||
c := &sprite.BaseSprite{
|
||||
Visible: true,
|
||||
X: 40,
|
||||
Y: 29,
|
||||
}
|
||||
c.AddCostume(sprite.NewCostume(coffin_top_c0, 'x'))
|
||||
return c
|
||||
}
|
||||
|
||||
func NewTorch(x, y int) *sprite.BaseSprite {
|
||||
t := &sprite.BaseSprite{
|
||||
Visible: true,
|
||||
X: x,
|
||||
Y: y,
|
||||
}
|
||||
t.AddCostume(sprite.NewCostume(torch, 'x'))
|
||||
return t
|
||||
}
|
||||
|
||||
func NewCloud(cloudType, posX, posY int) *Cloud {
|
||||
c := &Cloud{BaseSprite: sprite.BaseSprite{
|
||||
Visible: true,
|
||||
Y: posY,
|
||||
X: posX},
|
||||
VX: -1,
|
||||
}
|
||||
if cloudType == 0 {
|
||||
c.AddCostume(sprite.NewCostume(cloud_c0, 'x'))
|
||||
c.TimeOut = cloud_c0_timer
|
||||
} else if cloudType == 1 {
|
||||
c.AddCostume(sprite.NewCostume(cloud_c1, 'x'))
|
||||
c.TimeOut = cloud_c1_timer
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Cloud) Update() {
|
||||
c.Timer++
|
||||
if c.Timer > c.TimeOut {
|
||||
c.X = c.X + c.VX
|
||||
|
||||
if c.VX < 0 && c.X + c.Width < 0 {
|
||||
c.X = 102
|
||||
}
|
||||
c.Timer = 0
|
||||
}
|
||||
}
|
||||
|
||||
func NewWhale() *Whale {
|
||||
w := &Whale{BaseSprite: sprite.BaseSprite{
|
||||
Visible: true,
|
||||
X: 50,
|
||||
Y: 30},
|
||||
Facing: -1,
|
||||
MinX: 40,
|
||||
TimeOut: 20,
|
||||
}
|
||||
w.AddCostume(sprite.NewCostume(whale_c0, 'x'))
|
||||
w.AddCostume(sprite.NewCostume(whale_c0_rev, 'x'))
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *Whale) FaceLeft() {
|
||||
w.CurrentCostume = 0
|
||||
}
|
||||
|
||||
func (w *Whale) FaceRight() {
|
||||
w.CurrentCostume = 1
|
||||
}
|
||||
|
||||
func (w *Whale) Update() {
|
||||
w.Timer++
|
||||
|
||||
if w.Timer >= w.TimeOut && w.Y > 21 {
|
||||
w.Y--
|
||||
w.Timer = 0
|
||||
}
|
||||
}
|
||||
|
||||
func NewFlame(x, y int) *Flame {
|
||||
f := &Flame{BaseSprite: sprite.BaseSprite{
|
||||
Visible: true,
|
||||
X: x,
|
||||
Y: y},
|
||||
TimeOut: 3,
|
||||
}
|
||||
|
||||
for _, c := range []string{bigFlame1, bigFlame2, bigFlame3} {
|
||||
f.AddCostume(sprite.NewCostume(c, 'x'))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Flame) Update() {
|
||||
f.Timer++
|
||||
if f.Timer >= f.TimeOut {
|
||||
f.NextCostume()
|
||||
f.Timer = 0
|
||||
}
|
||||
}
|
||||
|
||||
func NewGhost() *Ghost {
|
||||
g := &Ghost{BaseSprite: sprite.BaseSprite{
|
||||
Visible: true,
|
||||
X: random.Intn(50)+25,
|
||||
Y: 30},
|
||||
TimeOut: 3,
|
||||
}
|
||||
g.AddCostume(sprite.NewCostume(ghost_c0, 'x'))
|
||||
g.AddCostume(sprite.NewCostume(ghost_c1, 'x'))
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *Ghost) Update() {
|
||||
var x int
|
||||
var y int
|
||||
g.Timer++
|
||||
if g.Timer >= g.TimeOut {
|
||||
x = random.Intn(3)
|
||||
x--
|
||||
y = random.Intn(3)
|
||||
y--
|
||||
if x < 0 {
|
||||
g.CurrentCostume = 0
|
||||
} else if x > 0 {
|
||||
g.CurrentCostume = 1
|
||||
}
|
||||
g.X += x
|
||||
g.Y += y
|
||||
g.Timer = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
// XXX - Wait a bit until the terminal is properly initialized
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
err := tm.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer tm.Close()
|
||||
|
||||
Width, Height = tm.Size()
|
||||
|
||||
random = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
event_queue := make(chan tm.Event)
|
||||
go func() {
|
||||
for {
|
||||
event_queue <- tm.PollEvent()
|
||||
}
|
||||
}()
|
||||
|
||||
txt := "Press 'ESC' to quit. 'Up' for more ghosts."
|
||||
c := sprite.NewCostume(txt, '~')
|
||||
text := sprite.NewBaseSprite(Width/2-len(txt)/2, Height-2, c)
|
||||
|
||||
w := NewWhale()
|
||||
m := NewMoon()
|
||||
cl0 := NewCloud(0, 102, 2)
|
||||
cl1 := NewCloud(1, 71, 2)
|
||||
cl2 := NewCloud(1, 23, 3)
|
||||
t := NewTitle()
|
||||
|
||||
f0 := NewFlame(15, 20)
|
||||
t0 := NewTorch(13, 25)
|
||||
f1 := NewFlame(110, 20)
|
||||
t1 := NewTorch(108, 25)
|
||||
|
||||
coffin := NewCoffin()
|
||||
coffinB := NewCoffinBack()
|
||||
|
||||
for _, spr := range []sprite.Sprite{m, t, cl0, cl1, cl2, coffinB, w, f0, t0, f1, t1, coffin, text} {
|
||||
allSprites.Sprites = append(allSprites.Sprites, spr)
|
||||
}
|
||||
|
||||
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.Key == tm.KeyArrowLeft {
|
||||
w.FaceLeft()
|
||||
} else if ev.Key == tm.KeyArrowRight {
|
||||
w.FaceRight()
|
||||
} else if ev.Key == tm.KeyArrowUp {
|
||||
g := NewGhost()
|
||||
allSprites.Sprites = append(allSprites.Sprites, g)
|
||||
}
|
||||
} else if ev.Type == tm.EventResize {
|
||||
Width = ev.Width
|
||||
Height = ev.Height
|
||||
}
|
||||
default:
|
||||
allSprites.Update()
|
||||
allSprites.Render()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче