Skip to content

Instantly share code, notes, and snippets.

@dhaninugraha
Last active December 13, 2023 13:04
Show Gist options
  • Save dhaninugraha/d3ec1ae8f9824d3bda5db1a4e7ffc4e8 to your computer and use it in GitHub Desktop.
Save dhaninugraha/d3ec1ae8f9824d3bda5db1a4e7ffc4e8 to your computer and use it in GitHub Desktop.
golang - Christmas tree
package main
import (
"fmt"
"math/rand"
"os"
"os/exec"
"strconv"
"strings"
"time"
)
const (
defaultBushChar = "*"
defaultLightChar = "O"
defaultPadChar = " "
)
var (
lightColors = []string{
"0;31",
"0;33",
"0;34",
"0;35",
"0;36",
"1;31",
"1;33",
"1;34",
"1;35",
"1;36",
}
greetings = []string{
"Wishing you a very merry Christmas :)",
" ",
"@dhaninugraha",
}
/* start with lit lights */
isLit = true
)
func getTermSize() (rows int, cols int) {
size := []string{}
cmdGetSize := exec.Command("stty", "size")
cmdGetSize.Stdin = os.Stdin
outSize, err := cmdGetSize.Output()
if err == nil {
cleanSize := strings.TrimSuffix(string(outSize), "\n")
size = strings.Split(cleanSize, " ")
if len(size) != 2 {
return 0, 0
}
r, eR := strconv.Atoi(size[0])
c, eC := strconv.Atoi(size[1])
if eR != nil || eC != nil {
return 0, 0
}
return r, c
}
return 0, 0
}
func drawTree(termHeight int, termWidth int, greetings []string) {
if termHeight == 0 || termWidth == 0 {
return
}
/*
we use string builder since it's more
efficient than appending chars to a string
*/
var b strings.Builder
/* clear screen */
fmt.Fprintf(&b, "%s", "\033[H\033[2J")
/* top padding: 1/5 terminal height */
for i := 0; i < termHeight/5; i++ {
fmt.Fprintf(&b, "%s", "\n")
}
/* tree bush */
bushHeight := termHeight / 2
for i := 0; i < bushHeight; i++ {
bushCharLen := (i * 2) + 1
padding := (termWidth / 2) - bushCharLen + i
/* align to the center by using spaces to left-pad */
fmt.Fprintf(&b, "%s", strings.Repeat(defaultPadChar, padding))
for j := 0; j < bushCharLen; j++ {
if !isLit {
fmt.Fprintf(&b, "\033[0;32m%s\033[0m", defaultBushChar)
} else {
rand.Seed(time.Now().UnixNano())
if rand.Intn(5) == 1 {
rand.Seed(time.Now().UnixNano())
colorIdx := rand.Intn(len(lightColors))
fmt.Fprintf(&b, "\033[%sm%s\033[0m", lightColors[colorIdx], defaultLightChar)
} else {
fmt.Fprintf(&b, "\033[0;32m%s\033[0m", defaultBushChar)
}
}
}
fmt.Fprintf(&b, "%s", "\n")
}
/* tree base */
baseW := 5
baseH := 5
for i := 0; i < baseH; i++ {
padding := (termWidth / 2) - baseW
if i < baseH-1 {
fmt.Fprintf(&b, "\033[33m%s|%s|\033[0m\n", strings.Repeat(defaultPadChar, padding), strings.Repeat("/", (baseW-1)*2))
} else {
fmt.Fprintf(&b, "\033[33m%s%s\033[0m\n", strings.Repeat(defaultPadChar, padding), strings.Repeat("-", baseW*2))
}
}
/* gap between tree & greetings: 1/20 terminal height */
for i := 0; i < termHeight/20; i++ {
fmt.Fprintf(&b, "%s", "\n")
}
/* greetings */
for i := 0; i < len(greetings); i++ {
width := len(greetings[i])
padding := (termWidth / 2) - width
fmt.Fprintf(&b, "%s%s\n", strings.Repeat(defaultPadChar, padding+(width/2)), greetings[i])
}
/* bottom padding: 1/20 terminal height */
for i := 0; i < termHeight/20; i++ {
fmt.Fprintf(&b, "%s", "\n")
}
/* print everything */
fmt.Println(b.String())
}
func main() {
termHeight, termWidth := getTermSize()
for {
drawTree(termHeight, termWidth, greetings)
time.Sleep(250 * time.Millisecond)
/* toggle light state */
isLit = !isLit
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment