Basics of Go
Priskribo de la ĉi-paĝa enhavo
Konverto de malnovaj BASIC-programoj al Go por lerni la fundamentojn de ĉi-tiu lingvo.
Etikedoj:
3D Plot
/*
3D Plot
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), ca. 1980.
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-28.
.
Last modified: 20250105T1150+0100.
*/
package main
import "fmt"
import "math"
const space rune = ' '
const dot rune = '*'
const width int = 56
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func input(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Display the credits and wait for a keypress.
func printCredits() {
fmt.Println("3D Plot\n")
fmt.Println("Original version in BASIC:")
fmt.Println(" Creative computing (Morristown, New Jersey, USA), ca. 1980.\n")
fmt.Println("This version in Go:")
fmt.Println(" Copyright (c) 2024, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
input("Press Enter to start the program. ")
}
func a(z float64) float64 {
return 30 * math.Exp(-z*z/100)
}
func draw() {
var l = 0
var z = 0
var y1 = 0
var line [width]rune
for x := -30.0; x <= 30.0; x += 1.5 {
for pos := 0; pos < width; pos++ {
line[pos] = space
}
l = 0
y1 = 5 * int(math.Sqrt(900-x*x)/5)
for y := y1; y >= -y1; y += -5 {
z = int(25 + a(math.Sqrt(x*x+float64(y*y))) - 0.7*float64(y))
if z > l {
l = z
line[z] = dot
}
} // y loop
for pos := 0; pos < width; pos++ {
fmt.Printf(string(line[pos]))
}
fmt.Println()
} // x loop
}
func main() {
clearScreen()
printCredits()
clearScreen()
draw()
}
Bagels
/*
Bagels
Original version in BASIC:
D. Resek, P. Rowe, 1978.
Creative Computing (Morristown, New Jersey, USA), 1978.
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-29
Last modified: 20250108T1445+0100.
*/
package main
import "fmt"
import "math/rand"
import "strings"
import "time"
import "unicode"
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func input(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Clear the screen, display the credits and wait for a keypress.
func printCredits() {
clearScreen()
fmt.Println("Bagels")
fmt.Println("Number guessing game\n")
fmt.Println("Original source unknown but suspected to be:")
fmt.Println(" Lawrence Hall of Science, U.C. Berkely.\n")
fmt.Println("Original version in BASIC:")
fmt.Println(" D. Resek, P. Rowe, 1978.")
fmt.Println(" Creative computing (Morristown, New Jersey, USA), 1978.\n")
fmt.Println("This version in Go:")
fmt.Println(" Copyright (c) 2024, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
input("Press Enter to read the instructions. ")
}
// Clear the screen, print the instructions and wait for a keypress.
func printInstructions() {
clearScreen()
fmt.Println("Bagels")
fmt.Println("Number guessing game\n")
fmt.Println("I am thinking of a three-digit number that has no two digits the same.")
fmt.Println("Try to guess it and I will give you clues as follows:\n")
fmt.Println(" PICO - one digit correct but in the wrong position")
fmt.Println(" FERMI - one digit correct and in the right position")
fmt.Println(" BAGELS - no digits correct")
input("\nPress Enter to start. ")
}
const DIGITS int = 3
var randomDigit [DIGITS]int
// Return three random digits.
func random() []int {
for i := 0; i < DIGITS; i++ {
digitLoop:
for {
randomDigit[i] = rand.Intn(10)
for j := 0; j < i; j++ {
if i != j && randomDigit[i] == randomDigit[j] {
continue digitLoop
}
}
break
}
}
return randomDigit[:]
}
// Return `true` if any of the given numbers is repeated; otherwise return
// `false`.
func isAnyRepeated(num []int) bool {
for i0, n0 := range num[:] {
for _, n1 := range num[i0+1:] {
if n0 == n1 {
return true
}
}
}
return false
}
// Print the given prompt and update the array whose address is given with a
// three-digit number from the user.
func getInput(prompt string, userDigit *[DIGITS]int) {
const ASCII_0 int = 48
getLoop:
for {
var input string = input(prompt)
if len(input) != DIGITS {
fmt.Printf("Remember it's a %v-digit number.\n", DIGITS)
continue getLoop
}
for pos, digit := range input {
if unicode.IsDigit(digit) {
// XXX TODO simplify
userDigit[pos] = int(digit) - ASCII_0
} else {
fmt.Println("What?")
continue getLoop
}
}
if isAnyRepeated(userDigit[:]) {
fmt.Println("Remember my number has no two digits the same.")
continue getLoop
}
break
}
}
// Return `true` if the given string is "yes" or a synonym.
func isYes(s string) bool {
s = strings.ToLower(s)
for _, valid := range []string{"ok", "y", "yeah", "yes"} {
if s == valid {
return true
}
}
return false
}
// Return `true` if the given string is "no" or a synonym.
func isNo(s string) bool {
s = strings.ToLower(s)
for _, valid := range []string{"n", "no", "nope"} {
if s == valid {
return true
}
}
return false
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
func yes(prompt string) bool {
for {
var answer = input(prompt)
if isYes(answer) {
return true
}
if isNo(answer) {
return false
}
}
}
// Init and run the game loop.
func play() {
rand.Seed(time.Now().UTC().UnixNano())
const TRIES int = 20
var score int
var fermi int // counter
var pico int // counter
var userNumber [DIGITS]int
for {
clearScreen()
var computerNumber = random()
fmt.Println("O.K. I have a number in mind.")
for guess := 1; guess <= TRIES; guess++ {
//fmt.Printf("My number: %v\n", computerNumber) // XXX TMP
getInput(fmt.Sprintf("Guess #%2v: ", guess), &userNumber)
fermi = 0
pico = 0
for i := 0; i < DIGITS; i++ {
for j := 0; j < DIGITS; j++ {
if userNumber[i] == computerNumber[j] {
if i == j {
fermi += 1
} else {
pico += 1
}
}
}
}
var picos = strings.Repeat("PICO ", pico)
var fermis = strings.Repeat("FERMI ", fermi)
fmt.Print(picos)
fmt.Print(fermis)
if pico+fermi == 0 {
fmt.Print("BAGELS")
}
fmt.Println()
if fermi == DIGITS {
break
}
}
if fermi == DIGITS {
fmt.Println("You got it!!!")
score += 1
} else {
fmt.Println("Oh well.")
fmt.Printf("That's %v guesses. My number was ", TRIES)
for i := 0; i < DIGITS; i++ {
fmt.Print(computerNumber[i])
}
fmt.Println(".")
}
if !yes("Play again? ") {
break
}
}
if score != 0 {
fmt.Printf("A %v-point bagels, buff!!\n", score)
}
fmt.Println("Hope you had fun. Bye.")
}
func main() {
printCredits()
printInstructions()
play()
}
Bunny
/*
Bunny
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), 1978.
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-29.
Last modified: 20250105T1151+0100.
*/
package main
import "fmt"
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func input(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Print the credits and wait for a keypress.
func printCredits() {
fmt.Println("Bunny\n")
fmt.Println("Original version in BASIC:")
fmt.Println(" Creative Computing (Morristown, New Jersey, USA), 1978.\n")
fmt.Println("This version in Go:")
fmt.Println(" Copyright (c) 2024, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
input("Press Enter to start the program. ")
}
const width int = 53
var line [width]uint8 // buffer
// Clear the line buffer with spaces.
func clearLine() {
for column := 0; column < len(line); column++ {
line[column] = ' '
}
}
var letter = []uint8{'B', 'U', 'N', 'N', 'Y'}
var letters = int8(len(letter))
const end = -1 // end of line identifier
var data = []int8{
1, 2, end, 0, 2, 45, 50, end, 0, 5, 43, 52, end, 0, 7, 41, 52, end,
1, 9, 37, 50, end, 2, 11, 36, 50, end, 3, 13, 34, 49, end, 4, 14,
32, 48, end, 5, 15, 31, 47, end, 6, 16, 30, 45, end, 7, 17, 29, 44,
end, 8, 19, 28, 43, end, 9, 20, 27, 41, end, 10, 21, 26, 40, end,
11, 22, 25, 38, end, 12, 22, 24, 36, end, 13, 34, end, 14, 33, end,
15, 31, end, 17, 29, end, 18, 27, end, 19, 26, end, 16, 28, end,
13, 30, end, 11, 31, end, 10, 32, end, 8, 33, end, 7, 34, end, 6,
13, 16, 34, end, 5, 12, 16, 35, end, 4, 12, 16, 35, end, 3, 12, 15,
35, end, 2, 35, end, 1, 35, end, 2, 34, end, 3, 34, end, 4, 33,
end, 6, 33, end, 10, 32, 34, 34, end, 14, 17, 19, 25, 28, 31, 35,
35, end, 15, 19, 23, 30, 36, 36, end, 14, 18, 21, 21, 24, 30, 37, 37,
end, 13, 18, 23, 29, 33, 38, end, 12, 29, 31, 33, end, 11, 13, 17,
17, 19, 19, 22, 22, 24, 31, end, 10, 11, 17, 18, 22, 22, 24, 24, 29,
29, end, 22, 23, 26, 29, end, 27, 29, end, 28, 29, end}
var dataLen = len(data)
var dataIndex = 0 // data pointer
func datum() int8 {
dataIndex += 1
return data[dataIndex-1]
}
// Draw the graphic out of `data` and `letter`.
func draw() {
clearLine()
for dataIndex < dataLen {
var firstColumn = datum()
if firstColumn == end {
fmt.Printf("%s\n", line)
clearLine()
} else {
var lastColumn = datum()
for column := firstColumn; column <= lastColumn; column++ {
line[column] = letter[column%letters]
}
}
}
}
func main() {
clearScreen()
printCredits()
clearScreen()
draw()
}
Diamond
/*
Diamond
Original version in BASIC:
Example included in Vintage BASIC 1.0.3.
http://www.vintage-basic.net
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-28.
Last modified: 20241231T0218+0100.
*/
package main
import "fmt"
const lines int = 17
func main() {
for i := 1; i <= lines/2+1; i++ {
for j := 1; j <= (lines+1)/2-i+1; j++ {
fmt.Print(" ")
}
for j := 1; j <= i*2-1; j++ {
fmt.Print("*")
}
fmt.Println()
}
for i := 1; i <= lines/2; i++ {
for j := 1; j <= i+1; j++ {
fmt.Print(" ")
}
for j := 1; j <= ((lines+1)/2-i)*2-1; j++ {
fmt.Print("*")
}
fmt.Println()
}
}
High Noon
/*
High Noon
Original version in BASIC:
Designed and programmed by Chris Gaylo, Syosset High School, New York, 1970-09-12.
http://mybitbox.com/highnoon-1970/
http://mybitbox.com/highnoon/
Transcriptions:
https://github.com/MrMethor/Highnoon-BASIC/
https://github.com/mad4j/basic-highnoon/
Version modified for QB64:
By Daniele Olmisani, 2014.
https://github.com/mad4j/basic-highnoon/
This improved remake in Go:
Copyright (c) 2025, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2025-01-05.
Last modified: 20250112T0201+0100.
*/
package main
import "fmt"
import "math/rand"
import "strconv"
import "strings"
import "time"
// Global variables and constants {{{1
// =============================================================
const defaultInk = foreground + whiteColor
const inputInk = foreground + bright + greenColor
const instructionsInk = foreground + yellowColor
const titleInk = foreground + bright + redColor
const initialDistance int = 100
const initialBullets int = 4
const maxWateringTroughs int = 3
var distance int // distance between both gunners, in paces
var strategy string // player's strategy
var playerBullets int
var opponentBullets int
// Console {{{1
// =============================================================
const blackColor = 0
const redColor = 1
const greenColor = 2
const yellowColor = 3
const blueColor = 4
const magentaColor = 5
const cyanColor = 6
const whiteColor = 7
const defaultColor = 9
const foreground = +30
const background = +40
const bright = +60
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
// Set the color.
func set_color(color int) {
fmt.Printf("\x1B[%vm", color)
}
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters an integer.
func inputInt(prompt string) int {
var number int64
var err error
set_color(inputInk)
defer set_color(defaultInk)
for {
number, err = strconv.ParseInt(inputString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
// Print the given prompt and wait until the user enters a string.
func inputString(prompt string) string {
set_color(inputInk)
defer set_color(defaultInk)
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Print the given prompt and wait until the user presses Enter.
func pressEnter(prompt string) {
inputString(prompt)
}
// Return `true` if the given string is "yes" or a synonym.
func isYes(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "ok", "yeah", "yes", "y":
return true
default:
return false
}
}
// Return `true` if the given string is "no" or a synonym.
func isNo(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "n", "no", "nope":
return true
default:
return false
}
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
func yes(prompt string) bool {
for {
var answer = inputString(prompt)
if isYes(answer) {
return true
}
if isNo(answer) {
return false
}
}
}
// Title, instructions and credits {{{1
// =============================================================
// Print the title at the current cursor position.
func printTitle() {
set_color(titleInk)
fmt.Println("High Noon")
set_color(defaultInk)
}
func printCredits() {
printTitle()
fmt.Println("\nOriginal version in BASIC:")
fmt.Println(" Designed and programmend by Chris Gaylo, 1970.")
fmt.Println(" http://mybitbox.com/highnoon-1970/")
fmt.Println(" http://mybitbox.com/highnoon/")
fmt.Println("Transcriptions:")
fmt.Println(" https://github.com/MrMethor/Highnoon-BASIC/")
fmt.Println(" https://github.com/mad4j/basic-highnoon/")
fmt.Println("Version modified for QB64:")
fmt.Println(" By Daniele Olmisani, 2014.")
fmt.Println(" https://github.com/mad4j/basic-highnoon/")
fmt.Println("This improved remake in Go:")
fmt.Println(" Copyright (c) 2025, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair")
}
func printInstructions() {
printTitle()
set_color(instructionsInk)
defer set_color(defaultInk)
fmt.Println("\nYou have been challenged to a showdown by Black Bart, one of")
fmt.Println("the meanest desperadoes west of the Allegheny mountains.")
fmt.Println("\nWhile you are walking down a dusty, deserted side street,")
fmt.Println("Black Bart emerges from a saloon one hundred paces away.")
fmt.Printf("\nBy agreement, you each have %v bullets in your six-guns.", initialBullets)
fmt.Println("\nYour marksmanship equals his. At the start of the walk nei-")
fmt.Println("ther of you can possibly hit the other, and at the end of")
fmt.Println("the walk, neither can miss. the closer you get, the better")
fmt.Println("your chances of hitting black Bart, but he also has beter")
fmt.Println("chances of hitting you.")
}
// Game loop {{{1
// =============================================================
func pluralSuffix(n int) string {
switch n {
case 1:
return ""
default:
return "s"
}
}
func printShellsLeft() {
if playerBullets == opponentBullets {
fmt.Printf("Both of you have %v bullets.\n", playerBullets)
} else {
fmt.Printf(
"You now have %v bullet%s to Black Bart's %v bullet%s.\n",
playerBullets,
pluralSuffix(playerBullets),
opponentBullets,
pluralSuffix(opponentBullets))
}
}
func printCheck() {
fmt.Println("******************************************************")
fmt.Println("* *")
fmt.Println("* BANK OF DODGE CITY *")
fmt.Println("* CASHIER'S RECEIT *")
fmt.Println("* *")
fmt.Printf("* CHECK NO. %04v AUGUST %vTH, 1889 *\n",
rand.Intn(1000),
10+rand.Intn(10))
fmt.Println("* *")
fmt.Println("* *")
fmt.Println("* PAY TO THE BEARER ON DEMAND THE SUM OF *")
fmt.Println("* *")
fmt.Println("* TWENTY THOUSAND DOLLARS-------------------$20,000 *")
fmt.Println("* *")
fmt.Println("******************************************************")
}
func getReward() {
fmt.Println("As mayor of Dodge City, and on behalf of its citizens,")
fmt.Println("I extend to you our thanks, and present you with this")
fmt.Println("reward, a check for $20,000, for killing Black Bart.\n\n")
printCheck()
fmt.Println("\n\nDon't spend it all in one place.")
}
func moveTheOpponent() {
var paces = 2 + rand.Intn(8)
fmt.Printf("Black Bart moves %v paces.\n", paces)
distance -= paces
}
// Maybe move the opponent; if so, return `true`, otherwise return `false`. A
// true `silent` flag allows to omit the message when the opponent doesn't
// move.
func theOpponentMoves(silent bool) bool {
if rand.Intn(2) == 0 { // 50% chances
moveTheOpponent()
return true
} else {
if !silent {
fmt.Println("Black Bart stands still.")
}
return false
}
}
func missedShot() bool {
return rand.Float64()*10 <= float64(distance/10)
}
// Handle the opponent's shot and return a flag with the result: if the
// opponent kills the player, return `true`; otherwise return `false`.
func theOpponentFiresAndKills() bool {
fmt.Println("Black Bart fires…")
opponentBullets -= 1
if missedShot() {
fmt.Println("A miss…")
switch opponentBullets {
case 3:
fmt.Println("Whew, were you lucky. That bullet just missed your head.")
case 2:
fmt.Println("But Black Bart got you in the right shin.")
case 1:
fmt.Println("Though Black Bart got you on the left side of your jaw.")
case 0:
fmt.Println("Black Bart must have jerked the trigger.")
}
} else {
if strategy == "j" {
fmt.Println("That trick just saved yout life. Black Bart's bullet")
fmt.Println("was stopped by the wood sides of the trough.")
} else {
fmt.Println("Black Bart shot you right through the heart that time.")
fmt.Println("You went kickin' with your boots on.")
return true
}
}
return false
}
// Handle the opponent's strategy and return a flag with the result: if the
// opponent runs or kills the player, return `true`; otherwise return `false`.
func theOpponentKillsOrRuns() bool {
if distance >= 10 || playerBullets == 0 {
if theOpponentMoves(true) {
return false
}
}
if opponentBullets > 0 {
return theOpponentFiresAndKills()
} else {
if playerBullets > 0 {
if rand.Intn(2) == 0 { // 50% chances
fmt.Println("Now is your chance, Black Bart is out of bullets.")
} else {
fmt.Println("Black Bart just hi-tailed it out of town rather than face you")
fmt.Println("without a loaded gun. You can rest assured that Black Bart")
fmt.Println("won't ever show his face around this town again.")
return true
}
}
}
return false
}
func play() {
rand.Seed(time.Now().UTC().UnixNano())
distance = initialDistance
var wateringTroughs = 0
playerBullets = initialBullets
opponentBullets = initialBullets
showdown:
for {
fmt.Printf("You are now %v paces apart from Black Bart.\n", distance)
printShellsLeft()
set_color(instructionsInk)
fmt.Println("\nStrategies:")
fmt.Println(" [A]dvance")
fmt.Println(" [S]tand still")
fmt.Println(" [F]ire")
fmt.Println(" [J]ump behind the watering trough")
fmt.Println(" [G]ive up")
fmt.Println(" [T]urn tail and run")
set_color(defaultInk)
strategy = strings.ToLower(inputString("What is your strategy? "))
switch strategy {
case "a": // advance
for {
var paces = inputInt("How many paces do you advance? ")
if paces < 0 {
fmt.Println("None of this negative stuff, partner, only positive numbers.")
} else if paces > 10 {
fmt.Println("Nobody can walk that fast.")
} else {
distance -= paces
break
}
}
case "s": // stand still
fmt.Println("That move made you a perfect stationary target.")
case "f": // fire
if playerBullets == 0 {
fmt.Println("You don't have any bullets left.")
} else {
playerBullets -= 1
if missedShot() {
switch playerBullets {
case 2:
fmt.Println("Grazed Black Bart in the right arm.")
case 1:
fmt.Println("He's hit in the left shoulder, forcing him to use his right")
fmt.Println("hand to shoot with.")
}
fmt.Println("What a lousy shot.")
if playerBullets == 0 {
fmt.Println("Nice going, ace, you've run out of bullets.")
if opponentBullets != 0 {
fmt.Println("Now Black Bart won't shoot until you touch noses.")
fmt.Println("You better think of something fast (like run).")
}
}
} else {
fmt.Println("What a shot, you got Black Bart right between the eyes.")
pressEnter("\nPress the Enter key to get your reward. ")
clearScreen()
getReward()
break showdown
}
}
case "j": // jump
if wateringTroughs == maxWateringTroughs {
fmt.Println("How many watering troughs do you think are on this street?")
strategy = ""
} else {
wateringTroughs += 1
fmt.Println("You jump behind the watering trough.")
fmt.Println("Not a bad maneuver to threw Black Bart's strategy off.")
}
case "g": // give up
fmt.Println("Black Bart accepts. The conditions are that he won't shoot you")
fmt.Println("if you take the first stage out of town and never come back.")
if yes("Agreed? ") {
fmt.Println("A very wise decision.")
break showdown
} else {
fmt.Println("Oh well, back to the showdown.")
}
case "t": // turn tail and run
// The more bullets of the opponent, the less chances to escape.
if rand.Intn(opponentBullets+2) == 0 {
fmt.Println("Man, you ran so fast even dogs couldn't catch you.")
} else {
switch opponentBullets {
case 0:
fmt.Println("You were lucky, Black Bart can only throw his gun at you, he")
fmt.Println("doesn't have any bullets left. You should really be dead.")
case 1:
fmt.Println("Black Bart fires his last bullet…")
fmt.Println("He got you right in the back. That's what you deserve, for running.")
case 2:
fmt.Println("Black Bart fires and got you twice: in your back")
fmt.Println("and your ass. Now you can't even rest in peace.")
case 3:
fmt.Println("Black Bart unloads his gun, once in your back")
fmt.Println("and twice in your ass. Now you can't even rest in peace.")
case 4:
fmt.Println("Black Bart unloads his gun, once in your back")
fmt.Println("and three times in your ass. Now you can't even rest in peace.")
}
opponentBullets = 0
}
break showdown
default:
fmt.Println("You sure aren't going to live very long if you can't even follow directions.")
} // strategy switch
if theOpponentKillsOrRuns() {
break
}
if playerBullets+opponentBullets == 0 {
fmt.Println("The showdown must end, because nobody has bullets left.")
break
}
fmt.Println()
} // showdown loop
}
// Main {{{1
// =============================================================
func main() {
clearScreen()
printCredits()
pressEnter("\nPress the Enter key to read the instructions. ")
clearScreen()
printInstructions()
pressEnter("\nPress the Enter key to start. ")
clearScreen()
play()
}
Math
/*
Math
Original version in BASIC:
Example included in Vintage BASIC 1.0.3.
http://www.vintage-basic.net
This version in Go:
Copyright (c) 2024, 2025, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-29, 2025-03-22.
Last modified: 20250322T2328+0100.
*/
package main
import "fmt"
import "math"
import "strconv"
func inputString(prompt string) string {
fmt.Print(prompt)
s := ""
fmt.Scanf("%s", &s)
return s
}
func inputFloat(prompt string) float64 {
var float float64
var err error
for {
float, err = strconv.ParseFloat(inputString(prompt), 64)
if err == nil {
break
} else {
fmt.Println("Real number expected.")
}
}
return float
}
func Sign(a float64) int {
switch {
case a < 0:
return -1
case a > 0:
return +1
}
return 0
}
func main() {
var n float64 = inputFloat("Enter a number: ")
fmt.Printf("ABS(%[1]v) -> math.Abs(%[1]v) -> %[2]v\n", n, math.Abs(n))
fmt.Printf("ATN(%[1]v) -> math.Atan(%[1]v) -> %[2]v\n", n, math.Atan(n))
fmt.Printf("COS(%[1]v) -> math.Cos(%[1]v) -> %[2]v\n", n, math.Cos(n))
fmt.Printf("EXP(%[1]v) -> math.Exp(%[1]v) -> %[2]v\n", n, math.Exp(n))
fmt.Printf("INT(%[1]v) -> int(%[1]v) -> %[2]v\n", n, int(n))
fmt.Printf("LOG(%[1]v) -> math.Log(%[1]v) -> %[2]v\n", n, math.Log(n))
fmt.Printf("SGN(%[1]v) -> Sign(%[1]v) /* ad hoc */ -> %[2]v\n", n, Sign(n))
fmt.Printf("SQR(%[1]v) -> math.Sqrt(%[1]v) -> %[2]v\n", n, math.Sqrt(n))
fmt.Printf("TAN(%[1]v) -> math.Tan(%[1]v) -> %[2]v\n", n, math.Tan(n))
}
Mugwump
/*
Mugwump
Original version in BASIC:
Written by Bud Valenti's students of Project SOLO (Pittsburg, Pennsylvania, USA).
Slightly modified by Bob Albrecht of People's Computer Company.
Published by Creative Computing (Morristown, New Jersey, USA), 1978.
- https://www.atariarchives.org/basicgames/showpage.php?page=114
- http://vintage-basic.net/games.html
This version in Go:
Copyright (c) 2025, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2025-01-08.
Last modified: 20250108T1558+0100.
*/
package main
import "fmt"
import "math"
import "math/rand"
import "strconv"
import "strings"
// Console {{{1
// =============================================================
const blackColor = 0
const redColor = 1
const greenColor = 2
const yellowColor = 3
const blueColor = 4
const magentaColor = 5
const cyanColor = 6
const whiteColor = 7
const defaultColor = 9
const normalAttributes = 0
const foreground = +30
const background = +40
const bright = +60
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func moveCursorHome() {
fmt.Print("\x1B[H")
}
func hideCursor() {
fmt.Print("\x1B[?25l")
}
func showCursor() {
fmt.Print("\x1B[?25h")
}
func setColor(color int) {
fmt.Printf("\x1B[%vm", color)
}
// Global variables and constants {{{1
// =============================================================
const defaultInk = foreground + whiteColor
const inputInk = foreground + bright + greenColor
const instructionsInk = foreground + yellowColor
const titleInk = foreground + bright + redColor
const gridSize int = 10
const turns int = 10
const mugwumps int = 4
type Mugwump struct {
x int
y int
hidden bool
}
var mugwump [mugwumps]Mugwump
var found int // counter
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters a string.
func getString(prompt string) string {
setColor(inputInk)
defer setColor(defaultInk)
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Print the given prompt and wait until the user enters an integer.
func getNumber(prompt string) int {
var number int64
var err error
setColor(inputInk)
defer setColor(defaultInk)
for {
number, err = strconv.ParseInt(getString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
// Print the given prompt and wait until the user presses Enter.
func pressEnter(prompt string) {
getString(prompt)
}
// Return `true` if the given string is "yes" or a synonym.
func isYes(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "ok", "yeah", "yes", "y":
return true
default:
return false
}
}
// Return `true` if the given string is "no" or a synonym.
func isNo(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "n", "no", "nope":
return true
default:
return false
}
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
func yes(prompt string) bool {
for {
var answer = getString(prompt)
if isYes(answer) {
return true
}
if isNo(answer) {
return false
}
}
}
// Title, instructions and credits {{{1
// =============================================================
func printTitle() {
setColor(titleInk)
defer setColor(defaultInk)
fmt.Println("Mugwump\n")
}
// Clear the screen, print the credits and ask the user to press enter.
func printCredits() {
clearScreen()
printTitle()
setColor(instructionsInk)
defer setColor(defaultInk)
fmt.Println("Original version in BASIC:")
fmt.Println(" Written by Bud Valenti's students of Project SOLO (Pittsburg, Pennsylvania, USA).")
fmt.Println(" Slightly modified by Bob Albrecht of People's Computer Company.")
fmt.Println(" Published by Creative Computing (Morristown, New Jersey, USA), 1978.")
fmt.Println(" - https://www.atariarchives.org/basicgames/showpage.php?page=114")
fmt.Println(" - http://vintage-basic.net/games.html\n")
fmt.Println("This version in Go:")
fmt.Println(" Copyright (c) 2025, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
pressEnter("Press Enter to read the instructions. ")
}
// Clear the screen, print the instructions and ask the user to press enter.
func printInstructions() {
clearScreen()
printTitle()
setColor(instructionsInk)
defer setColor(defaultInk)
fmt.Println("The object of this game is to find four mugwumps")
fmt.Println("hidden on a 10 by 10 grid. Homebase is position 0,0.")
fmt.Println("Any guess you make must be two numbers with each")
fmt.Println("number between 0 and 9, inclusive. First number")
fmt.Println("is distance to right of homebase and second number")
fmt.Println("is distance above homebase.\n")
fmt.Printf("You get %v tries. After each try, you will see\n", turns)
fmt.Println("how far you are from each mugwump.\n")
pressEnter("Press Enter to start. ")
}
// Stock functions {{{1
// =============================================================
// Return the given plural ending if the given number is greater than 1;
// otherwise return the given singular ending.
func plural_(n int, plural string, singular string) string {
if n > 1 {
return plural
} else {
return singular
}
}
// Return the plural "s" ending if the given number is greater than 1;
// otherwise return an empty string.
func plural(n int) string {
return plural_(n, "s", "")
}
// Game {{{1
// =============================================================
// Init the mugwumps' positions, `hidden` flags and count.
func hideMugwumps() {
for m := 0; m < mugwumps; m++ {
mugwump[m].x = rand.Intn(gridSize)
mugwump[m].y = rand.Intn(gridSize)
mugwump[m].hidden = true
}
found = 0 // counter
}
// Print the given prompt, wait until the user enters a valid coord and return
// it.
func getCoord(prompt string) (coord int) {
for {
coord = getNumber(prompt)
if coord < 0 || coord >= gridSize {
fmt.Printf("Invalid value %v: not in range [0, %v].\n", coord, gridSize-1)
} else {
break
}
}
return
}
// Return `true` if the given mugwump is hidden in the given coords.
func isHere(m int, x int, y int) bool {
return mugwump[m].hidden && mugwump[m].x == x && mugwump[m].y == y
}
// Return the distance between the given mugwump and the given coords.
func distance(m int, x int, y int) int {
return int(math.Sqrt(
math.Pow(float64(mugwump[m].x-x), 2) +
math.Pow(float64(mugwump[m].y-y), 2)))
}
// Run the game.
func play() {
var x int
var y int
var turn = 0 // counter
for {
clearScreen()
hideMugwumps()
turnsLoop:
for turn = 1; turn <= turns; turn += 1 {
fmt.Printf("Turn number %v\n\n", turn)
fmt.Printf("What is your guess (in range [0, %v])?\n", gridSize-1)
x = getCoord("Distance right of homebase (x-axis): ")
y = getCoord("Distance above homebase (y-axis): ")
fmt.Printf("\nYour guess is (%v, %v).\n", x, y)
for m := 0; m < mugwumps; m++ {
if isHere(m, x, y) {
mugwump[m].hidden = false
found += 1
fmt.Printf("You have found mugwump %v!\n", m)
if found == mugwumps {
break turnsLoop
}
}
}
for m := 0; m < mugwumps; m++ {
if mugwump[m].hidden {
fmt.Printf("You are %v units from mugwump %v.\n", distance(m, x, y), m)
}
}
fmt.Println()
} // turns
if found == mugwumps {
fmt.Printf("\nYou got them all in %v turn%s!\n\n", turn, plural(turn))
fmt.Println("That was fun! let's play again…")
fmt.Println("Four more mugwumps are now in hiding.")
} else {
fmt.Printf("\nSorry, that's %v tr%s.\n\n", turns, plural_(turns, "ies", "y"))
fmt.Println("Here is where they're hiding:")
for m := 0; m < mugwumps; m++ {
if mugwump[m].hidden {
fmt.Printf("Mugwump %v is at (%v, %v).\n", m, mugwump[m].x, mugwump[m].y)
}
}
}
fmt.Println()
if !yes("Do you want to play again? ") {
break
}
}
}
// Main {{{1
// =============================================================
func main() {
printCredits()
printInstructions()
play()
}
Name
/*
Name
Original version in BASIC:
Example included in Vintage BASIC 1.0.3.
http://www.vintage-basic.net
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-31.
Last modified: 20241231T0218+0100.
*/
package main
import "fmt"
import "strconv"
func inputString(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
func inputInt(prompt string) int {
var number int64
var err error
for {
number, err = strconv.ParseInt(inputString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
func main() {
var name string = inputString("What is your name? ")
var number int = inputInt("Enter a number: ")
for i := 0; i < number; i++ {
fmt.Printf("Hello, %v!\n", name)
}
}
Poetry
/*
Poetry
Original version in BASIC:
Unknown author.
Modified and reworked by Jim Bailey, Peggy Ewing, and Dave Ahl at DEC.
Published in "BASIC Computer Games", Creative Computing (Morristown, New Jersey, USA), 1978.
https://archive.org/details/Basic_Computer_Games_Microcomputer_Edition_1978_Creative_Computing
https://github.com/chaosotter/basic-games/tree/master/games/BASIC%20Computer%20Games/Poetry
http://vintage-basic.net/games.html
This improved remake in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-29.
Last modified: 20250105T1152+0100.
*/
package main
import "fmt"
import "math/rand"
import "time"
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func input(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
func printCredits() {
fmt.Println("Poetry")
fmt.Println("\nOriginal version in BASIC:")
fmt.Println(" Unknown author.")
fmt.Println(" Published in \"BASIC Computer Games\",")
fmt.Println(" Creative Computing (Morristown, New Jersey, USA), 1978.\n")
fmt.Println("This improved remake in Go:")
fmt.Println(" Copyright (c) 2024, Marcos Cruz (programandala.Net)")
fmt.Println(" SPDX-License-Identifier: Fair")
}
func isEven(n int) bool {
return n%2 == 0
}
func play() {
rand.Seed(time.Now().UTC().UnixNano())
const maxPhrasesAndVerses int = 20
// counters:
var action = 0
var phrase = 0
var phrasesAndVerses = 0
var verseChunks = 0
verse:
for {
var manageTheVerseContinuation = true
var maybeAddComma = true
switch action {
case 0, 1:
switch phrase {
case 0:
fmt.Print("MIDNIGHT DREARY")
case 1:
fmt.Print("FIERY EYES")
case 2:
fmt.Print("BIRD OR FIEND")
case 3:
fmt.Print("THING OF EVIL")
case 4:
fmt.Print("PROPHET")
}
case 2:
switch phrase {
case 0:
fmt.Print("BEGUILING ME")
verseChunks = 2
case 1:
fmt.Print("THRILLED ME")
case 2:
fmt.Print("STILL SITTING…")
maybeAddComma = false
case 3:
fmt.Print("NEVER FLITTING")
verseChunks = 2
case 4:
fmt.Print("BURNED")
}
case 3:
switch phrase {
case 0:
fmt.Print("AND MY SOUL")
case 1:
fmt.Print("DARKNESS THERE")
case 2:
fmt.Print("SHALL BE LIFTED")
case 3:
fmt.Print("QUOTH THE RAVEN")
case 4:
if verseChunks != 0 {
fmt.Print("SIGN OF PARTING")
}
}
case 4:
switch phrase {
case 0:
fmt.Print("NOTHING MORE")
case 1:
fmt.Print("YET AGAIN")
case 2:
fmt.Print("SLOWLY CREEPING")
case 3:
fmt.Print("…EVERMORE")
case 4:
fmt.Print("NEVERMORE")
}
case 5:
action = 0
fmt.Println()
if phrasesAndVerses > maxPhrasesAndVerses {
fmt.Println()
verseChunks = 0
phrasesAndVerses = 0
action = 2
continue verse
} else {
manageTheVerseContinuation = false
}
}
if manageTheVerseContinuation {
time.Sleep(250 * time.Millisecond)
if maybeAddComma && !(verseChunks == 0 || rand.Float64() > 0.19) {
fmt.Print(",")
verseChunks = 2
}
if rand.Float64() > 0.65 {
fmt.Println()
verseChunks = 0
} else {
fmt.Print(" ")
verseChunks += 1
}
}
action += 1
phrase = rand.Intn(5)
phrasesAndVerses += 1
if !(verseChunks > 0 || isEven(action)) {
fmt.Print(" ")
}
} // verse loop
}
func main() {
clearScreen()
printCredits()
input("\nPress the Enter key to start. ")
clearScreen()
play()
}
Russian Roulette
/*
Russian Roulette
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), ca. 1980.
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-29.
Last modified: 20250105T1153+0100.
*/
package main
import "fmt"
import "math/rand"
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func input(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
func pressEnterToStart() {
input("Press Enter to start. ")
}
func printCredits() {
clearScreen()
fmt.Println("Russian Roulette\n")
fmt.Println("Original version in BASIC:")
fmt.Println(" Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n")
fmt.Println("This version in Go:")
fmt.Println(" Copyright (c) 2024, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
pressEnterToStart()
}
func printInstructions() {
clearScreen()
fmt.Println("Here is a revolver.")
fmt.Println("Type 'f' to spin chamber and pull trigger.")
fmt.Println("Type 'g' to give up, and play again.")
fmt.Println("Type 'q' to quit.\n")
}
func play() {
var times = 0
for { // game loop
printInstructions()
times = 0
playLoop:
for {
switch input("> ") {
case "f": // fire
if rand.Intn(100) > 83 {
fmt.Println("Bang! You're dead!")
fmt.Println("Condolences will be sent to your relatives.")
break playLoop
} else {
times += 1
if times > 10 {
fmt.Println("You win!")
fmt.Println("Let someone else blow his brains out.")
break playLoop
} else {
fmt.Println("Click.")
}
}
case "g": // give up
fmt.Println("Chicken!")
break playLoop
case "q": // quit
return
}
} // play loop
pressEnterToStart()
} // game loop
}
func main() {
printCredits()
play()
fmt.Println("Bye!")
}
Sine Wave
/*
Sine Wave
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), ca. 1980.
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-28.
Last modified: 20250105T1150+0100.
*/
package main
import "fmt"
import "math"
import "strings"
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func input(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
var word [2]string
// Ask the user to enter two words and store them.
func getWords() {
var order = [2]string{"first", "second"}
clearScreen()
for n := 0; n <= 1; n++ {
for word[n] == "" {
word[n] = input(fmt.Sprintf("Enter the %v word: ", order[n]))
}
}
}
// Display the credits and wait for a keypress.
func printCredits() {
fmt.Println("Sine Wave\n")
fmt.Println("Original version in BASIC:")
fmt.Println(" Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n")
fmt.Println("This version in Go:")
fmt.Println(" Copyright (c) 2024, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
input("Press Enter to start the program. ")
}
func draw() {
var even = true
for angle := 0.0; angle <= 40.0; angle += 0.25 {
fmt.Print(strings.Repeat(" ", int(26+25*math.Sin(angle))))
if even {
fmt.Println(word[0])
} else {
fmt.Println(word[1])
}
even = !even
}
}
func main() {
clearScreen()
printCredits()
getWords()
clearScreen()
draw()
}
Slots
/*
Slots
A slot machine simulation.
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA).
Produced by Fred Mirabelle and Bob Harper on 1973-01-29.
This version in Go:
Copyright (c) 2025, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2025-01-06/07, 2025-04-07.
Last modified: 20250407T2100+0200.
*/
package main
import "fmt"
import "math/rand"
import "strconv"
import "time"
const reels = 3
const blackColor = 0
const redColor = 1
const greenColor = 2
const yellowColor = 3
const blueColor = 4
const magentaColor = 5
const cyanColor = 6
const whiteColor = 7
const defaultColor = 9
const normalAttributes = 0
const foreground = +30
const background = +40
const bright = +60
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func moveCursorHome() {
fmt.Print("\x1B[H")
}
func hideCursor() {
fmt.Print("\x1B[?25l")
}
func showCursor() {
fmt.Print("\x1B[?25h")
}
func setColor(color int) {
fmt.Printf("\x1B[%vm", color)
}
const defaultInk = foreground + whiteColor
const inputInk = foreground + bright + greenColor
const instructionsInk = foreground + yellowColor
const titleInk = foreground + bright + redColor
var image = []string{" BAR ", " BELL ", "ORANGE", "LEMON ", " PLUM ", "CHERRY"}
var reel [reels]int
const bar int = 0 // position of "BAR" in `image`.
var color = []int{
foreground + whiteColor,
foreground + cyanColor,
foreground + yellowColor,
foreground + bright + yellowColor,
foreground + bright + whiteColor,
foreground + bright + redColor}
const maxBet int = 100
const minBet int = 1
func printCredits() {
clearScreen()
fmt.Println("Slots")
fmt.Println("A slot machine simulation.\n")
fmt.Println("\nOriginal version in BASIC:")
fmt.Println(" Creative computing (Morristown, New Jersey, USA).")
fmt.Println(" Produced by Fred Mirabelle and Bob Harper on 1973-01-29.\n")
fmt.Println("\nThis version in Go:")
fmt.Println(" Copyright (c) 2025, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair\n")
pressEnter("Press Enter for instructions.")
}
func printInstructions() {
clearScreen()
fmt.Println("You are in the H&M casino, in front of one of our")
fmt.Printf(
"one-arm bandits. Bet from %v to %v USD (or 0 to quit).\n\n",
minBet, maxBet)
pressEnter("Press Enter to start.")
}
func won(prize int, bet int) int {
switch prize {
case 2:
fmt.Println("DOUBLE!")
case 5:
fmt.Println("*DOUBLE BAR*")
case 10:
fmt.Println("**TOP DOLLAR**")
case 100:
fmt.Println("***JACKPOT***")
}
fmt.Println("You won!")
return (prize + 1) * bet
}
func showStandings(usd int) {
fmt.Printf("Your standings are %v USD.\n", usd)
}
func printReels() {
moveCursorHome()
for _, r := range reel {
setColor(color[r])
fmt.Printf("[%v] ", image[r])
}
setColor(normalAttributes)
fmt.Println("")
}
func initReels() {
var images = len(image)
for i := range reel {
reel[i] = rand.Intn(images)
}
}
func spinReels() {
const seconds time.Duration = 2
var firstSecond = time.Now()
var lastSecond = firstSecond.Add(time.Second * seconds)
hideCursor()
for time.Now().Before(lastSecond) {
initReels()
printReels()
}
showCursor()
}
// Return the number of equals and bars in the given `reel` array.
func prize() (int, int) {
var equals = 0
for first := 0; first < len(reel); first++ {
for second := first + 1; second < len(reel); second++ {
if reel[first] == reel[second] {
equals++
}
}
}
if equals > 0 && equals < len(reel) {
equals++
}
var bars = 0
for i := 0; i < len(reel); i++ {
if reel[i] == bar {
bars++
}
}
return equals, bars
}
// Print the given prompt and wait until the user enters a string.
func inputString(prompt string) string {
setColor(inputInk)
defer setColor(defaultInk)
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Print the given prompt and wait until the user presses Enter.
func pressEnter(prompt string) {
inputString(prompt)
}
// Print the given prompt and wait until the user enters an integer.
func inputInt(prompt string) int {
var number int64
var err error
setColor(inputInk)
defer setColor(defaultInk)
for {
number, err = strconv.ParseInt(inputString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
func play() {
rand.Seed(time.Now().UTC().UnixNano())
var standings = 0
var bet = 0
var equals = 0
var bars = 0
initReels()
playLoop:
for {
betLoop:
for {
clearScreen()
printReels()
bet = inputInt("Your bet (or 0 to quit): ")
switch {
case bet > maxBet:
fmt.Printf("House limits are %v USD.\n", maxBet)
pressEnter("Press Enter to try again.")
case bet < minBet:
if confirm := inputString("Type \"q\" to confirm you want to quit. "); confirm == "q" {
break playLoop
}
default:
break betLoop
} // bet check
} // bet loop
clearScreen()
spinReels()
equals, bars = prize()
switch equals {
case 3:
if bars == 3 {
standings += won(100, bet)
} else {
standings += won(10, bet)
}
case 2:
if bars == 2 {
standings += won(5, bet)
} else {
standings += won(2, bet)
}
default:
fmt.Println("You lost.")
standings -= bet
} // prize check
showStandings(standings)
pressEnter("Press Enter to continue.")
} // play loop
showStandings(standings)
switch {
case standings < 0:
fmt.Println("Pay up! Please leave your money on the terminal.")
case standings == 0:
fmt.Println("Hey, you broke even.")
case standings > 0:
fmt.Println("Collect your winnings from the H&M cashier.")
}
}
func main() {
printCredits()
printInstructions()
play()
}
Stars
/*
Stars
Original version in BASIC:
Example included in Vintage BASIC 1.0.3.
http://www.vintage-basic.net
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-31.
Last modified: 20241231T0125+0100.
*/
package main
import "fmt"
import "strconv"
import "strings"
func inputString(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
func inputInt(prompt string) int {
var number int64
var err error
for {
number, err = strconv.ParseInt(inputString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
func main() {
var name string = inputString("What is your name? ")
fmt.Printf("Hello, %v.\n", name)
loop:
for {
var number int = inputInt("How many stars do you want? ")
var stars = strings.Repeat("*", number)
fmt.Println(stars)
var answer string = inputString("Do you want more stars? ")
switch strings.ToLower(strings.TrimSpace(answer)) {
case "ok", "yeah", "yes", "y":
default:
break loop
}
}
}
Strings
/*
Strings
Original version in BASIC:
Example included in Vintage BASIC 1.0.3.
http://www.vintage-basic.net
This version in Go:
Copyright (c) 2024, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2024-12-29/30.
Last modified: 20250105T1151+0100.
*/
package main
import "fmt"
import "strconv"
import "strings"
func inputString(prompt string) string {
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
func inputInt(prompt string) int {
var number int64
var err error
for {
number, err = strconv.ParseInt(inputString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
func main() {
var s = inputString("Enter a string: ")
var n = inputInt("Enter an integer: ")
fmt.Println()
fmt.Printf("ASC(\"%s\") --> ", s)
fmt.Printf("int([]rune(\"%s\")[0]) --> %v\n", s, int([]rune(s)[0]))
fmt.Printf("CHR$(%v) --> ", n)
fmt.Printf("rune(%v) --> '%v'\n", n, rune(n))
fmt.Printf("LEFT$(\"%s\", %v) --> ", s, n)
//fmt.Printf("\"%s\"[:%v] --> \"%s\"\n", s, n, s[:n]) // byte slices, ASCII only
fmt.Printf("string([]rune(\"%s\")[:%v])) --> \"%s\"\n", s, n, string([]rune(s)[:n]))
fmt.Printf("MID$(\"%s\", %v) --> ", s, n)
// XXX FIXME When n is greater than string length, the result is not that of the BASIC original:
// fmt.Printf("\"%s\"[%v-1:] --> \"%s\"\n", s, n, s[n-1:]) // byte slices, ASCII only
fmt.Printf("string([]rune(\"%s\")[%v-1:])) --> \"%s\"\n", s, n, string([]rune(s)[n-1:]))
fmt.Printf("MID$(\"%s\", %v, 3) --> ", s, n)
// fmt.Printf("\"%s\"[%v-1:%v-1+3] --> \"%s\"\n", s, n, n, s[n-1:n-1+3]) // byte slices, ASCII only
fmt.Printf("string([]rune(\"%s\")[%v-1:%v-1+3])) --> \"%s\"\n", s, n, n, string([]rune(s)[n-1:n-1+3]))
fmt.Printf("RIGHT$(\"%s\", %v) --> ", s, n)
// fmt.Printf("\"%s\"[len(\"%s\")-%v:] --> \"%s\"\n", s, s, n, s[len(s)-n:]) // byte slices, ASCII only
fmt.Printf("string([]rune(\"%s\")[len([]rune(\"%s\"))-%v:])) --> \"%s\"\n", s, s, n, string([]rune(s)[len([]rune(s))-n:]))
fmt.Printf("LEN(\"%s\") --> ", s)
//fmt.Printf("len(\"%s\") --> %v\n", s, len(s)) // byte slices, ASCII only
fmt.Printf("len([]rune(\"%s\")) --> %v\n", s, len([]rune(s))) // byte slices, ASCII only
fmt.Printf("VAL(\"%s\") --> ", s)
var val, _ = strconv.ParseInt(s, 10, 0)
fmt.Printf("var val, err = strconv.ParseInt(\"%s\", 10, 0) /* result in `val`, error in `err` */ --> %v\n", s, val)
fmt.Printf("STR$(%v) --> ", n)
fmt.Printf("fmt.Sprint(%v) --> \"%s\"\n", n, fmt.Sprint(n))
fmt.Printf("SPC(%v) --> ", n)
fmt.Printf("strings.Repeat(\" \", %v) --> \"%s\"\n", n, strings.Repeat(" ", n))
}
Xchange
/*
Xchange
Original version in BASIC:
Written by Thomas C. McIntire, 1979.
Published in "The A to Z Book of Computer Games", 1979.
https://archive.org/details/The_A_to_Z_Book_of_Computer_Games/page/n269/mode/2up
https://github.com/chaosotter/basic-games
This improved remake in Go:
Copyright (c) 2025, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written on 2025-01-12.
Last modified: 20250112T0212+0100.
*/
package main
import "fmt"
import "math/rand"
import "strconv"
import "strings"
import "time"
// Console {{{1
// =============================================================
const blackColor = 0
const redColor = 1
const greenColor = 2
const yellowColor = 3
const blueColor = 4
const magentaColor = 5
const cyanColor = 6
const whiteColor = 7
const defaultColor = 9
const normalAttributes = 0
const foreground = +30
const background = +40
const bright = +60
func clearScreen() {
fmt.Print("\x1B[0;0H\x1B[2J")
}
func moveCursorHome() {
fmt.Print("\x1B[H")
}
func eraseLineRight() {
fmt.Print("\x1B[K")
}
func eraseScreenDown() {
fmt.Print("\x1B[J")
}
func hideCursor() {
fmt.Print("\x1B[?25l")
}
func showCursor() {
fmt.Print("\x1B[?25h")
}
func setColor(color int) {
fmt.Printf("\x1B[%vm", color)
}
func setCursorPosition(line, column int) {
fmt.Printf("\x1B[%v;%vH", line, column)
}
// Globals {{{1
// =============================================================
const boardInk int = foreground + bright + cyanColor
const defaultInk int = foreground + whiteColor
const inputInk int = foreground + bright + greenColor
const instructionsInk int = foreground + yellowColor
const titleInk int = foreground + bright + redColor
const blank string = "*"
const gridHeight int = 3 // cell rows
const gridWidth int = 3 // cell columns
const cells int = gridWidth * gridHeight
var pristineGrid = [cells]string{}
const gridsRow int = 3 // screen row where the grids are printed
const gridsColumn int = 5 // screen column where the left grid is printed
const cellsGap int = 2 // distance between the grid cells, in screen rows or columns
const gridsGap int = 16 // screen columns between equivalent cells of the grids
const maxPlayers int = 4
var grid = [maxPlayers][cells]string{}
var isPlaying = [maxPlayers]bool{}
var players int
const quitCommand string = "X"
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters a string.
func inputString(prompt string) string {
setColor(inputInk)
defer setColor(defaultInk)
fmt.Print(prompt)
var s = ""
fmt.Scanf("%s", &s)
return s
}
// Print the given prompt and wait until the user enters an integer.
func inputInt(prompt string) int {
var number int64
var err error
for {
number, err = strconv.ParseInt(inputString(prompt), 10, 0)
if err == nil {
break
} else {
fmt.Println("Integer expected.")
}
}
return int(number)
}
// Print the given prompt and wait until the user presses Enter.
func pressEnter(prompt string) {
inputString(prompt)
}
// Return `true` if the given string is "yes" or a synonym.
func isYes(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "ok", "yeah", "yes", "y":
return true
default:
return false
}
}
// Return `true` if the given string is "no" or a synonym.
func isNo(s string) bool {
switch strings.ToLower(strings.TrimSpace(s)) {
case "n", "no", "nope":
return true
default:
return false
}
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
func yes(prompt string) bool {
for {
var answer = inputString(prompt)
if isYes(answer) {
return true
}
if isNo(answer) {
return false
}
}
}
// Title, instructions and credits {{{1
// =============================================================
// Print the title at the current cursor position.
func printTitle() {
setColor(titleInk)
fmt.Println("Xchange")
setColor(defaultInk)
}
// Print the credits at the current cursor position.
func printCredits() {
printTitle()
fmt.Println("\nOriginal version in BASIC:")
fmt.Println(" Written by Thomas C. McIntire, 1979.")
fmt.Println(" Published in \"The A to Z Book of Computer Games\", 1979.")
fmt.Println(" https://archive.org/details/The_A_to_Z_Book_of_Computer_Games/page/n269/mode/2up")
fmt.Println(" https://github.com/chaosotter/basic-games")
fmt.Println("This improved remake in Go:")
fmt.Println(" Copyright (c) 2025, Marcos Cruz (programandala.net)")
fmt.Println(" SPDX-License-Identifier: Fair")
}
// Print the instructions at the current cursor position.
func printInstructions() {
printTitle()
setColor(instructionsInk)
defer setColor(defaultInk)
fmt.Println("\nOne or two may play. If two, you take turns. A grid looks like this:\n")
setColor(boardInk)
fmt.Println(" F G D")
fmt.Printf(" A H %s\n", blank)
fmt.Println(" E B C\n")
setColor(instructionsInk)
fmt.Println("But it should look like this:\n")
setColor(boardInk)
fmt.Println(" A B C")
fmt.Println(" D E F")
fmt.Printf(" G H %s\n\n", blank)
setColor(instructionsInk)
fmt.Printf("You may exchange any one letter with the '%s', but only one that's adjacent:\n", blank)
fmt.Println("above, below, left, or right. Not all puzzles are possible, and you may enter")
fmt.Printf("'%s' to give up.\n\n", quitCommand)
fmt.Println("Here we go...")
}
// Grids {{{1
// =============================================================
// Print the given player's grid title.
func printGridTitle(player int) {
setCursorPosition(gridsRow, gridsColumn+(player*gridsGap))
fmt.Printf("Player %v", player+1)
}
// Return the cursor position of the given player's grid cell.
func cellPosition(player, cell int) (row, column int) {
var gridRow int = cell / gridHeight
var gridColumn int = cell % gridWidth
var titleMargin int = 0
if players > 1 {
titleMargin = 2
}
return gridsRow + titleMargin + gridRow,
gridsColumn + (gridColumn * cellsGap) + (player * gridsGap)
}
// Return the cursor position of the given player's grid prompt.
func gridPromptPosition(player int) (row, column int) {
var gridRow int = cells / gridHeight
var gridColumn int = cells % gridWidth
var titleMargin = 0
if players > 1 {
titleMargin = 2
}
return gridsRow + titleMargin + gridRow + 1,
gridsColumn + (gridColumn * cellsGap) + (player * gridsGap)
}
// Print the given player's grid, in the given or default color.
func printGrid(player int, color int) {
if players > 1 {
printGridTitle(player)
}
setColor(color)
defer setColor(defaultInk)
for cell := 0; cell < cells; cell++ {
setCursorPosition(cellPosition(player, cell))
fmt.Print(grid[player][cell])
}
}
// Print the current players' grids.
func printGrids() {
for player := 0; player < players; player++ {
if isPlaying[player] {
printGrid(player, boardInk)
}
}
fmt.Println()
eraseScreenDown()
}
// Scramble the grid of the given player.
func scrambleGrid(player int) {
for cell := 0; cell < cells; cell++ {
var randomCell = rand.Intn(cells)
// Exchange the contents of the current cell with that of the random one.
grid[player][cell], grid[player][randomCell] =
grid[player][randomCell], grid[player][cell]
}
}
// Init the grids.
func initGrids() {
grid[0] = pristineGrid
scrambleGrid(0)
for player := 0 + 1; player < players; player++ {
grid[player] = grid[0]
}
}
// Messages {{{1
// =============================================================
// Return a message prefix for the given player.
func playerPrefix(player int) string {
if players > 1 {
return fmt.Sprintf("Player %i: ", player+1)
} else {
return ""
}
}
// Return the cursor position of the given player's messages, adding the given
// row increment, which defaults to zero.
func messagePosition(player int, rowInc int) (row, column int) {
promptRow, _ := gridPromptPosition(player)
return promptRow + 2 + rowInc, 1
}
// Print the given message about the given player, adding the given row
// increment, which defaults to zero, to the default cursor coordinates.
func printMessage(message string, player int, rowInc int) {
setCursorPosition(messagePosition(player, rowInc))
fmt.Printf("%s%s", playerPrefix(player), message)
eraseLineRight()
fmt.Println()
}
// Erase the last message about the given player.
func eraseMessage(player int) {
setCursorPosition(messagePosition(player, 0))
eraseLineRight()
}
// Game loop {{{1
// =============================================================
// Return a message with the players range.
func playersRangeMessage() string {
if maxPlayers == 2 {
return "1 or 2"
} else {
return fmt.Sprintf("from 1 to %v", maxPlayers)
}
}
// Return the number of players, asking the user if needed.
func numberOfPlayers() int {
var players = 0
printTitle()
fmt.Println()
if maxPlayers == 1 {
players = 1
} else {
for players < 1 || players > maxPlayers {
players = inputInt(fmt.Sprintf("Number of players (%s): ", playersRangeMessage()))
}
}
return players
}
// Is the given cell the first one on a grid row?
func isFirstCellOfA_gridRow(cell int) bool {
return cell%gridWidth == 0
}
// Is the given cell the last one on a grid row?
func isLastCellOfA_gridRow(cell int) bool {
return (cell+1)%gridWidth == 0
}
// Are the given cells adjacent?
func areCellsAdjacent(cell_1, cell_2 int) bool {
switch {
case cell_2 == cell_1+1 && !isFirstCellOfA_gridRow(cell_2):
return true
case cell_2 == cell_1+gridWidth:
return true
case cell_2 == cell_1-1 && !isLastCellOfA_gridRow(cell_2):
return true
case cell_2 == cell_1-gridWidth:
return true
}
return false
}
// Is the given player's character cell a valid move, i.E. is it adjacent to
// the blank cell? If so, return the blank cell of the grid and `true`;
// otherwise return a fake cell and `false`.
func isLegalMove(player, charCell int) (blankCell int, ok bool) {
const nowhere = -1
for blankCell, cellContent := range grid[player] {
if cellContent == blank {
if areCellsAdjacent(charCell, blankCell) {
return blankCell, true
}
break
}
}
printMessage(fmt.Sprintf("Illegal move \"%s\".", grid[player][charCell]), player, 0)
return nowhere, false
}
// Is the given player's command valid, i.E. a grid character? If so, return
// its position in the grid and `true`; otherwise print an error message and
// return a fake position and `false`.
func isValidCommand(player int, command string) (position int, ok bool) {
const nowhere = -1
if command != blank {
for position, cellContent := range grid[player] {
if cellContent == command {
return position, true
}
}
}
printMessage(fmt.Sprintf("Invalid character \"%s\".", command), player, 0)
return nowhere, false
}
// Forget the given player, who quitted.
func forgetPlayer(player int) {
isPlaying[player] = false
printGrid(player, defaultInk)
}
// Play the turn of the given player.
func playTurn(player int) {
var blankPosition int
var characterPosition int
if isPlaying[player] {
for {
var ok bool
var command string
for ok = false; !ok; characterPosition, ok = isValidCommand(player, command) {
row, column := gridPromptPosition(player)
setCursorPosition(row, column)
eraseLineRight()
setCursorPosition(row, column)
command = strings.ToUpper(strings.TrimSpace(inputString("Move: ")))
if command == quitCommand {
forgetPlayer(player)
return
}
}
blankPosition, ok = isLegalMove(player, characterPosition)
if ok {
break
}
}
eraseMessage(player)
grid[player][blankPosition] = grid[player][characterPosition]
grid[player][characterPosition] = blank
}
}
// Play the turns of all players.
func playTurns() {
for player := 0; player < players; player++ {
playTurn(player)
}
}
// Is someone playing?
func isSomeonePlaying() bool {
for player := 0; player < players; player++ {
if isPlaying[player] {
return true
}
}
return false
}
// Has someone won? If so, print a message for every winner and return `true`;
// otherwise just return `false`.
func hasSomeoneWon() bool {
var winners = 0
for player := 0; player < players; player++ {
if isPlaying[player] {
if grid[player] == pristineGrid {
winners += 1
if winners > 0 {
var winner = "winner"
if winners > 1 {
winner = "winner, too"
}
printMessage(
fmt.Sprintf("You're the %s!", winner),
player,
winners-1)
}
}
}
}
return winners > 0
}
// Init the game.
func initGame() {
clearScreen()
players = numberOfPlayers()
for player := 0; player < players; player++ {
isPlaying[player] = true
}
clearScreen()
printTitle()
initGrids()
printGrids()
}
// Play the game.
func play() {
initGame()
for isSomeonePlaying() {
playTurns()
printGrids()
if hasSomeoneWon() {
break
}
}
}
// Main {{{1
// =============================================================
// Init the program, i.E. just once before the first game.
func initOnce() {
rand.Seed(time.Now().UTC().UnixNano())
// Init the pristine grid.
const firstCharCode = int('A')
for cell := 0; cell < cells-1; cell++ {
pristineGrid[cell] = string(rune(firstCharCode + cell))
}
pristineGrid[cells-1] = blank
}
// Return `true` if the player does not want to play another game; otherwise
// return `false`.
func enough() bool {
setCursorPosition(gridPromptPosition(0))
return !yes("Another game? ")
}
func main() {
initOnce()
clearScreen()
printCredits()
pressEnter("\nPress the Enter key to read the instructions. ")
clearScreen()
printInstructions()
pressEnter("\nPress the Enter key to start. ")
for {
play()
if enough() {
break
}
}
fmt.Println("So long…")
}
