Basics of Swift

Priskribo de la ĉi-paĝa enhavo

Konverto de malnovaj BASIC-programoj al Swift 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 Swift:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2023-11-02/03, 2023-11-17.

Last modified: 20231117T2222+0100.
*/

import Foundation // exp(), sqrt()

let SPACE = " "
let DOT = "*"
let WIDTH = 56

// Move the cursor to the top left position of the terminal.
func home() {
    print("\u{001B}[H", terminator: "")
}

// Clear the terminal and move the cursor to the top left position.
func clear() {
    print("\u{001B}[2J", terminator: "")
    home()
}

// Display the credits and wait for a keypress.
func printCredits() {
    print("3D Plot\n")
    print("Original version in BASIC:")
    print("    Creative computing (Morristown, New Jersey, USA), ca. 1980.\n")
    print("This version in Swift:")
    print("    Copyright (c) 2023, Marcos Cruz (programandala.net)")
    print("    SPDX-License-Identifier: Fair\n")
    print("Press Enter to start the program.")
    let _ = readLine()
}

func a(_ z: Double) -> Double {
    return 30.0 * exp(-z * z / 100.0)
}

func draw() {
    var l = 0
    var z = 0
    var y1 = 0
    var line = Array<String>(repeating: SPACE, count: WIDTH)
    var x = -30.0
    while x <= 30.0 {
        for pos in 0 ..< WIDTH {
            line[pos] = SPACE
        }
        l = 0
        y1 = 5 * Int(sqrt(900.0 - x * x) / 5.0)
        var y = y1
        while y >= -y1 {
            z = Int(25.0 + a(sqrt(x * x + Double(y * y))) - 0.7 * Double(y))
            if z > l {
                l = z
                line[z] = DOT
            }
            y += -5
        } // y loop
        for pos in 0 ..< WIDTH {
            print(line[pos], terminator: "")
        }
        print()
        x += 1.5
    } // x loop
}

clear()
printCredits()
clear()
draw()

Bagels

/*
Bagels

Original version in BASIC:
    D. Resek, P. Rowe, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Swift:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-04-24.

Last modified: 20250424T2222+0200.
*/

// Terminal {{{1
// =============================================================================

// Move the cursor to the home position.
func moveCursorHome() {
    print("\u{001B}[H", terminator: "")
}

// Clear the screen and move the cursor to the home position.
func clearScreen() {
    print("\u{001B}[2J", terminator: "")
    moveCursorHome()
}

// User input {{{1
// =============================================================================

func promptedString(with prompt: String) -> String {
    while true {
        print(prompt, terminator: "")
        if let input = readLine() {
            return input
        }
    }
}

func promptedInteger(with prompt: String) -> Int {
    while true {
        print(prompt, terminator: "")
        if let input = readLine(), let number = Int(input) {
            return number
        } else {
            print("Integer expected.")
        }
    }
}


func pressEnter(prompt: String) {
    print(prompt, terminator: "")
    let _ = readLine()
}

// Return `true` if the given string is "yes" or a synonym.
//
func isYes(_ s: String) -> Bool {
    return ["ok", "y", "yeah", "yes"].contains(s.lowercased())
}

// Return `true` if the given string is "no" or a synonym.
//
func isNo(_ s: String) -> Bool {
    return ["n", "no", "nope"].contains(s.lowercased())
}

// 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 {
    while true {
        let answer = promptedString(with: prompt)
        if isYes(answer) {
            return true
        }
        if isNo(answer) {
            return false
        }
    }
}

// Credits and instructions {{{1
// =============================================================================

func printCredits() {
    clearScreen()
    print("Bagels")
    print("Number guessing game\n")
    print("Original source unknown but suspected to be:")
    print("    Lawrence Hall of Science, U.C. Berkely.\n")
    print("Original version in BASIC:")
    print("    D. Resek, P. Rowe, 1978.")
    print("    Creative computing (Morristown, New Jersey, USA), 1978.\n")
    print("This version in Swift:")
    print("    Copyright (c) 2025, Marcos Cruz (programandala.net)")
    print("    SPDX-License-Identifier: Fair\n")
    pressEnter(prompt: "Press Enter to read the instructions. ")
}

func printInstructions() {
    clearScreen()
    print("Bagels")
    print("Number guessing game\n")
    print("I am thinking of a three-digit number that has no two digits the same.")
    print("Try to guess it and I will give you clues as follows:\n")
    print("   PICO   - one digit correct but in the wrong position")
    print("   FERMI  - one digit correct and in the right position")
    print("   BAGELS - no digits correct")
    pressEnter(prompt: "\nPress Enter to start. ")
}

// Main {{{1
// =============================================================================

let DIGITS = 3

func randomNumber() -> [Int] {
    var randomDigits = [Int]()
    for _ in 0 ..< DIGITS {
        var digit: Int
        repeat {
            digit = Int.random(in: 0 ..< 10)
        } while randomDigits.contains(digit)
        randomDigits.append(digit)
    }
    return randomDigits
}

func userInput(prompt: String) -> [Int] {
    var number = [Int]()
    askForNumber: while true {
        let input = promptedString(with: prompt)
        if (input).count != DIGITS {
            print("Remember it's a \(DIGITS)-digit number.")
            continue askForNumber
        }
        for digitPosition in 0 ..< DIGITS {
            let digitIndex = input.index(input.startIndex, offsetBy: digitPosition)
            let digit = input[digitIndex]
            if "0" ... "9" ~= digit {
                let numericDigit = Int(String(digit))!
                if !number.contains(numericDigit) {
                    number.append(numericDigit)
                } else {
                    print("Remember my number has no two digits the same.")
                    continue askForNumber
                }
            } else {
                print("What?")
                continue askForNumber
            }
        }
        break
    }
    return number
}

// Game {{{1
// =============================================================================

// Init and run the game loop.
//
func play() {
    let TRIES = 20
    var score = 0
    var fermi = 0 // counter
    var pico = 0 // counter
    var userNumber: [Int]
    while true {
        clearScreen()
        let computerNumber = randomNumber()
        print("O.K.  I have a number in mind.")
        for guess in 1 ... TRIES {
            // print("My number: \(computerNumber)") // XXX TMP
            userNumber = userInput(prompt: "Guess #" + String(guess) + ": ")
            fermi = 0
            pico = 0
            for i in 0 ..< DIGITS {
                for j in 0 ..< DIGITS {
                    if userNumber[i] == computerNumber[j] {
                        if i == j {
                            fermi += 1
                        } else {
                            pico += 1
                        }
                    }
                }
            }
            if pico + fermi == 0 {
                print("BAGELS")
            } else {
                print(String(repeating: "PICO ", count: pico), terminator: "")
                print(String(repeating: "FERMI ", count: fermi))
            }
            if fermi == DIGITS {
                break
            }
        }
        if fermi == DIGITS {
            print("You got it!!!")
            score += 1
        } else {
            print("Oh well.")
            print("That's \(TRIES) guesses.  My number was ",terminator: "")
            for i in 0 ..< DIGITS {
                print(computerNumber[i], terminator: "")
            }
            print(".")
        }
        if !yes(prompt: "Play again? ") {
            break
        }
    }
    if score != 0 {
        print("A \(score)-point bagels, buff!!")
    }
    print("Hope you had fun.  Bye.")
}

printCredits()
printInstructions()
play()

Bug

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Swift:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written in 2023-11-04/05.

Last modified: 20231105T0109+0100.
*/


struct Bug {
    var body: Bool = false
    var neck: Bool = false
    var head: Bool = false
    var feelers: Int = 0
    var feelerType: Character = " "
    var tail: Bool = false
    var legs: Int = 0
}

struct Player {
    var pronoun: String = ""
    var possessive: String = ""
    var bug: Bug = Bug()
}

// Players.
var computer = Player(pronoun: "I", possessive: "My")
computer.bug.feelerType = "F"
var human = Player(pronoun: "you", possessive: "Your")
human.bug.feelerType = "A"

enum Part: Int, CaseIterable {
    case Body = 1, Neck, Head, Feeler, Tail, Leg
    func quantity() -> Int {
        switch self {
            case .Body: return 1
            case .Neck: return 1
            case .Head: return 1
            case .Feeler: return 2
            case .Tail: return 1
            case .Leg: return 6
        }
    }
}

// Bug body attributes.
let BODY_HEIGHT = 2
let FEELER_LENGTH = 4
let LEG_LENGTH = 2
let MAX_FEELERS = 2
let MAX_LEGS = 6
let NECK_LENGTH = 2

// Move the cursor to the home position.
func home() {
    print("\u{001B}[H", terminator: "")
}

// Clear the screen and move the cursor to the home position.
func clear() {
    print("\u{001B}[2J", terminator: "")
    home()
}

// Move the cursor up by the given number of rows (defaults to 1), without
// changing the column position.
func cursorUp(rows: UInt8 = 1) {
    print("\u{001B}[\(rows)A", terminator: "")
}

// Erase the current line, without moving the cursor position.
func eraseLine() {
    print("\u{001B}[2K", terminator: "")
}

// Move the cursor to the previous row, without changing the column position,
// and erase its line.
func erasePreviousLine() {
    cursorUp()
    eraseLine()
}

// Clear the screen, display the credits and wait for a keypress.
func printCredits() {
    clear()
    print("Bug\n")
    print("Original version in BASIC:")
    print("    Brian Leibowitz, 1978.")
    print("    Creative computing (Morristown, New Jersey, USA), 1978.\n")
    print("This version in Swift:")
    print("    Copyright (c) 2023, Marcos Cruz (programandala.net)")
    print("    SPDX-License-Identifier: Fair\n")
    print("Press Enter to read the instructions. ", terminator: "")
    let _ = readLine()
}

let instructions = """
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
"""

extension String {
    func capitalized() -> String {
        return self.prefix(1).uppercased() + self.dropFirst()
    }
}

func leftJustify(text: String, width: Int, pad: String) -> String {
    return String((text + String(repeating: pad, count: width)).prefix(width))
}

// Print a table with the bug parts' description.
func printPartsTable() {

    let COLUMNS = 3
    let COLUMN_WIDTH = 8
    let RULER = String(repeating: "-", count: COLUMN_WIDTH)
    let COLUMN_SEPARATION = 2
    let SEPARATOR = String(repeating: " ", count: COLUMN_SEPARATION)

    let headers = ["Number", "Part", "Quantity"]
    for header in headers {
        print(
            leftJustify(text: header, width: COLUMN_WIDTH, pad: " "),
            terminator: SEPARATOR
        )
    }
    print()

    // Rulers
    for _ in 0 ..< COLUMNS {
        print(RULER, terminator: SEPARATOR)
    }
    print()

    // Data
    for part in Part.allCases {
        print(
            leftJustify(text: String(part.rawValue), width: COLUMN_WIDTH, pad: " "),
            leftJustify(text: String(describing: part), width: COLUMN_WIDTH, pad: " "),
            part.quantity(),
            separator: SEPARATOR,
            terminator: ""
        )
        print()
    }

}

// Clear the screen, print the instructions and wait for a keypress.
func printInstructions() {
    clear()
    print("Bug\n")
    print(instructions)
    print()
    printPartsTable()
    print("\nPress Enter to start. ", terminator: "")
    let _ = readLine()
}

// Print a bug head.
func printHead() {
    print("        HHHHHHH")
    print("        H     H")
    print("        H O O H")
    print("        H     H")
    print("        H  V  H")
    print("        HHHHHHH")
}

// Print the given bug.
func printBug(bug: Bug) {
    if bug.feelers > 0 {
        for _ in 0 ..< FEELER_LENGTH {
            print("        ", terminator: "")
            for _ in 0 ..< bug.feelers {
                print(" ", bug.feelerType, terminator: "")
            }
            print()
        }
    }
    if bug.head {
        printHead()
    }
    if bug.neck {
        for _ in 0 ..< NECK_LENGTH {
            print("          N N")
        }
    }
    if bug.body {
        print("     BBBBBBBBBBBB")
        for _ in 0 ..< BODY_HEIGHT {
            print("     B          B")
        }
        if bug.tail {
            print("TTTTTB          B")
        }
        print("     BBBBBBBBBBBB")
    }
    if bug.legs > 0 {
        for _ in 0 ..< LEG_LENGTH {
            print("    ", terminator: "")
            for _ in 0 ..< bug.legs {
                print(" L", terminator: "")
            }
            print()
        }
    }
}

// Return `true` if the given bug is finished; otherwise return `false`.
func finished(bug: Bug) -> Bool {
    return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS
}

// Array to convert a number to its equilavent text.
let asText = [
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six" ] // MAX_LEGS

// Return a string containing the given number and noun in their proper form.
func plural(number: Int, noun: String) -> String {
    return "\(asText[number]) \(noun)\((number > 1) ? "s" : "")"
}

// Add the given part to the given player's bug.
func addPart(part: Part, player: inout Player) -> Bool {
    var changed = false
    switch part {
    case Part.Body:
        if player.bug.body {
            print(", but", player.pronoun, "already have a body.")
        } else {
            print(";", player.pronoun, "now have a body:")
            player.bug.body = true
            changed = true
        }
    case Part.Neck:
        if player.bug.neck {
            print(", but", player.pronoun, "you already have a neck.")
        } else if !player.bug.body {
            print(", but", player.pronoun, "need a body first.")
        } else {
            print(";", player.pronoun, "now have a neck:")
            player.bug.neck = true
            changed = true
        }
    case Part.Head:
        if player.bug.head {
            print(", but", player.pronoun, "already have a head.")
        } else if !player.bug.neck {
            print(", but", player.pronoun, "need a a neck first.")
        } else {
            print(";", player.pronoun, "now have a head:")
            player.bug.head = true
            changed = true
        }
    case Part.Feeler:
        if player.bug.feelers == MAX_FEELERS {
            print(", but", player.pronoun, "have two feelers already.")
        } else if !player.bug.head {
            print(", but", player.pronoun, "need a head first.")
        } else {
            player.bug.feelers += 1
            print("; \(player.pronoun) now have \(plural(number: player.bug.feelers, noun: "feeler")):")
            changed = true
        }
    case Part.Tail:
        if player.bug.tail {
            print(", but", player.pronoun, "already have a tail.")
        } else if !player.bug.body {
            print(", but", player.pronoun, "need a body first.")
        } else {
            print(";", player.pronoun, "now have a tail:")
            player.bug.tail = true
            changed = true
        }
    case Part.Leg:
        if player.bug.legs == MAX_LEGS {
            print(", but", player.pronoun, "have",
                asText[MAX_LEGS], "feet already.")
        } else if !player.bug.body {
            print(", but", player.pronoun, "need a body first.")
        } else {
            player.bug.legs += 1
            print("; \(player.pronoun) now have \(plural(number: player.bug.legs, noun: "leg")):")
            changed = true
        }
    }
    return changed
}

// Ask the user to press the Enter key, wait for the input, then erase the
// prompt text.
func prompt() {
    print("Press Enter to roll the dice. ", terminator: "")
    let _ = readLine()
    erasePreviousLine()
}

// Play one turn for the given player, rolling the dice and updating his bug.
func turn(_ player: inout Player) {
    prompt()
    let number = Int.random(in: 1...6)
    let part = Part(rawValue: number)!
    print("\(player.pronoun.capitalized()) rolled a \(number) (\(String(describing: part).lowercased()))", terminator: "")
    if addPart(part: part, player: &player) {
        print()
        printBug(bug: player.bug)
    }
    print()
}

// Print a message about the winner.
func printWinner() {
    if finished(bug: human.bug) && finished(bug: computer.bug) {
        print("Both of our bugs are finished in the same number of turns!")
    } else if finished(bug: human.bug) {
        print(human.possessive, "bug is finished.")
    } else if finished(bug: computer.bug) {
        print(computer.possessive, "bug is finished.")
    }
}

// Return `true` if either bug is finished, i.e. the game ending condition.
func gameOver() -> Bool {
    return finished(bug: human.bug) || finished(bug: computer.bug)
}

// Execute the game loop.
func play() {
    clear()
    while !gameOver() {
        turn(&human)
        turn(&computer)
    }
    printWinner()
}

printCredits()
printInstructions()
play()
print("I hope you enjoyed the game, play it again soon!!")

Bunny

/*
Bunny

Original version in BASIC:
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Swift:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2023-11-03.

Last modified 20231103T1711+0100.
*/


// Move the cursor to the top left position of the terminal.
func home() {
    print("\u{001B}[H", terminator: "")
}

// Clear the terminal and move the cursor to the top left position.
func clearScreen() {
    print("\u{001B}[2J", terminator: "")
    home()
}

// Print the credits and wait for a keypress.
func printCredits() {
    print("Bunny\n")
    print("Original version in BASIC:")
    print("    Creative Computing (Morristown, New Jersey, USA), 1978.\n")
    print("This version in Swift:")
    print("    Copyright (c) 2023, Marcos Cruz (programandala.net)")
    print("    SPDX-License-Identifier: Fair\n")
    print("Press Enter to start the program.")
    let _ = readLine()
}

let WIDTH = 53
let SPACE : Unicode.Scalar = " "
var line = Array<Unicode.Scalar>(repeating: SPACE, count: WIDTH)

// Clear the line buffer with spaces.
func clearLine() {
    for column in 0 ..< line.count {
        line[column] = SPACE
    }
}

let letter : [Unicode.Scalar] = ["B", "U", "N", "N", "Y"]

let EOL : Int = -1 // end of line identifier
let data : [Int] = [
    1, 2, EOL, 0, 2, 45, 50, EOL, 0, 5, 43, 52, EOL, 0, 7, 41, 52, EOL,
    1, 9, 37, 50, EOL, 2, 11, 36, 50, EOL, 3, 13, 34, 49, EOL, 4, 14,
    32, 48, EOL, 5, 15, 31, 47, EOL, 6, 16, 30, 45, EOL, 7, 17, 29, 44,
    EOL, 8, 19, 28, 43, EOL, 9, 20, 27, 41, EOL, 10, 21, 26, 40, EOL,
    11, 22, 25, 38, EOL, 12, 22, 24, 36, EOL, 13, 34, EOL, 14, 33, EOL,
    15, 31, EOL, 17, 29, EOL, 18, 27, EOL, 19, 26, EOL, 16, 28, EOL,
    13, 30, EOL, 11, 31, EOL, 10, 32, EOL, 8, 33, EOL, 7, 34, EOL, 6,
    13, 16, 34, EOL, 5, 12, 16, 35, EOL, 4, 12, 16, 35, EOL, 3, 12, 15,
    35, EOL, 2, 35, EOL, 1, 35, EOL, 2, 34, EOL, 3, 34, EOL, 4, 33,
    EOL, 6, 33, EOL, 10, 32, 34, 34, EOL, 14, 17, 19, 25, 28, 31, 35,
    35, EOL, 15, 19, 23, 30, 36, 36, EOL, 14, 18, 21, 21, 24, 30, 37, 37,
    EOL, 13, 18, 23, 29, 33, 38, EOL, 12, 29, 31, 33, EOL, 11, 13, 17,
    17, 19, 19, 22, 22, 24, 31, EOL, 10, 11, 17, 18, 22, 22, 24, 24, 29,
    29, EOL, 22, 23, 26, 29, EOL, 27, 29, EOL, 28, 29, EOL ]

var dataIndex = 0 // data pointer

func datum() -> Int {
    dataIndex += 1
    return data[dataIndex - 1]
}

func printLine() {
    for character in line {
        print(character, terminator: "")
    }
    print()
}

// Draw the graphic out of `data` and `letter`.
func draw() {
    clearLine()
    while dataIndex < data.count {
        let firstColumn = datum()
        if firstColumn == EOL {
            printLine()
            clearLine()
        } else {
            let lastColumn = datum()
            for column in firstColumn ... lastColumn {
                line[column] = letter[Int(column) % letter.count]
            }
        }
    }
}

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 Swift:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2023-11-02.

Last modified 20231103T0050+0100.
*/


let LINES = 17

for i in 1 ... LINES / 2 + 1 {
    for _ in 1 ... (LINES + 1) / 2 - i + 1 {
        print(" ", terminator: "")
    }
    for _ in 1 ... i * 2 - 1 {
        print("*", terminator: "")
    }
    print()
}
for i in 1 ... LINES / 2 {
    for _ in 1 ... i + 1 {
        print(" ", terminator: "")
    }
    for _ in 1 ... ((LINES + 1) / 2 - i) * 2 - 1 {
        print("*", terminator: "")
    }
    print()
}

Math

/*
Math

Original version in BASIC:
    Example included in Vintage BASIC 1.0.3.
    http://www.vintage-basic.net

This version in Swift:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-04-23.

Last modified: 20250423T1508+0200.
*/

import Foundation

let n: Double

while true {
    print("Enter a number: ", terminator: "")
    if let input = readLine(), let number = Double(input) {
        n = number
        break
    } else {
        print("Number expected.")
    }
}

func sign (_: Double) -> Int {
    switch true {
        case n < 0: return -1
        case n > 0: return 1
        default: return 0
    }
}

print("ABS(\(n)) --> abs(\(n)) --> \(abs(n))")
print("ATN(\(n)) --> atan(\(n)) --> \(atan(n))")
print("COS(\(n)) --> cos(\(n)) --> \(cos(n))")
print("EXP(\(n)) --> exp(\(n)) --> \(exp(n))")
print("INT(\(n)) --> Int(\(n)) --> \(Int(n))")
print("LOG(\(n)) --> log2(\(n)) --> \(log(n))")
print("SGN(\(n)) --> \(n).sign == .minus ? -1 : +1 --> \(n.sign == .minus ? -1 : 1)")
print("SGN(\(n)) --> sign(\(n)) --> \(sign(n)) // ad hoc function")
print("SQR(\(n)) --> sqrt(\(n)) --> \(sqrt(n))")
print("TAN(\(n)) --> tan(\(n)) --> \(tan(n))")

Name

/*
Name

Original version in BASIC:
    Example included in Vintage BASIC 1.0.3.
    http://www.vintage-basic.net

This version in Swift:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-04-23.

Last modified: 20250423T1045+0200.
*/

var name = ""
while name == "" {
    print("What is your name? ", terminator: "")
    name = readLine() ?? ""
}

var number = 0
while number <= 0 {
    print("Enter a number greater than 0: ", terminator: "")
    number = Int(readLine() ?? "0") ?? 0
}

for _ in 0 ..< number {
    print("Hello, \(name)!")
}

Sine Wave

/*
Sine Wave

Original version in BASIC:
    Creative Computing (Morristown, New Jersey, USA), ca. 1980.

This version in Swift:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2023-11-03.

Last modified: 20231103T0142+0100.
*/

import Foundation // sin()

// Move the cursor to the top left position of the terminal.
func home() {
    print("\u{001B}[H", terminator: "")
}

// Clear the terminal and move the cursor to the top left position.
func clear() {
    print("\u{001B}[2J", terminator: "")
    home()
}

var word = ["", ""]

// Ask the user to enter a word and return it.
func getWord(number: Int) -> String {
    let order = ["first", "second"]
    word[number] = ""
    while word[number] == "" {
        print("Enter the \(order[number]) word: ", terminator: "")
        word[number] = readLine() ?? ""
    }
    return word[number]
}

// Ask the user to enter two words and store them.
func getWords() {
    clear()
    for n in 0 ... 1 {
        word[n] = getWord(number: n)
    }
}

// Clear the screen, display the credits and wait for a keypress.
func printCredits() {
    clear()
    print("Sine Wave\n")
    print("Original version in BASIC:")
    print("    Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n")
    print("This version in Swift:")
    print("    Copyright (c) 2023, Marcos Cruz (programandala.net)")
    print("    SPDX-License-Identifier: Fair\n")
    print("Press Enter to start the program.")
    let _ = readLine()
}

func draw() {
    clear()
    var even = true
    var angle = 0.0
    while angle <= 40.0 {
        print(String(repeating: " ", count: Int(26 + 25 * sin(angle))), terminator: "")
        print(word[even ? 0 : 1])
        even = !even
        angle += 0.25
    }
}

printCredits()
getWords()
draw()

Stars

/*
Stars

Original version in BASIC:
    Example included in Vintage BASIC 1.0.3.
    http://www.vintage-basic.net

This version in Swift:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-04-23.

Last modified: 20250423T1940+0200.
*/

var name = ""
while name == "" {
    print("What is your name? ", terminator: "")
    name = readLine() ?? ""
}
print("Hello, \(name).")

var numberOfStars: Int

play: while true {

    while true {
        print("How many stars do you want? ", terminator: "")
        if let input = readLine(), let number = Int(input) {
            numberOfStars = number
            break
        } else {
            print("Integer expected.")
        }
    }

    print(String(repeating: "*", count: numberOfStars))

    print("Do you want more stars? ", terminator: "")
    let answer = (readLine() ?? "").lowercased()
    if !["ok", "yeah", "yes", "y"].contains(answer) {
        break play
    }

}

Strings

/*
Strings

Original version in BASIC:
    Example included in Vintage BASIC 1.0.3.
    http://www.vintage-basic.net

This version in Swift:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-04-24.

Last modified: 20250424T1551+0200.
*/

func promptedString(with prompt: String) -> String {
    while true {
        print(prompt, terminator: "")
        if let input = readLine() {
            return input
        }
    }
}

func promptedInteger(with prompt: String) -> Int {
    while true {
        print(prompt, terminator: "")
        if let input = readLine(), let number = Int(input) {
            return number
        } else {
            print("Integer expected.")
        }
    }
}

var s = promptedString(with: "Enter a string: ")
var n = promptedInteger(with: "Enter an integer: ")

var start: String.Index
var end: String.Index
var result: String.SubSequence

print()

print("ASC(\"\(s)\") --> ", terminator: "")
print("\"s\".unicodeScalars.map { $0.value } [0] --> \(s.unicodeScalars.map { $0.value } [0])")

print("CHR$(\(n)) --> ", terminator: "")
print("Character(UnicodeScalar(\(n))!) --> \"\(Character(UnicodeScalar(n)!))\"")

// XXX TODO check limits of substrings in order to mimic the behaviour of the
// original BASIC functions

print("LEFT$(\"\(s)\", \(n)) -->")
print("""
            let s: \"\(s)\"
            let n: \(n)
            let end: String.Index = s.index(s.startIndex, offsetBy: n)
            let result: StringSubSequence = s[..<end]
        """)
end = s.index(s.startIndex, offsetBy: n)
result = s[..<end]
print("--> \"\(result)\"")

print("MID$(\"\(s)\", \(n)) -->")
print("""
            let s: \"\(s)\"
            let n: \(n)
            let start: String.Index = s.index(s.startIndex, offsetBy: n - 1)
            let result: String.SubSequence = s[start...]
        """)
start = s.index(s.startIndex, offsetBy: n - 1)
result = s[start...]
print("--> \"\(result)\"")

print("MID$(\"\(s)\", \(n), 3) -->")
print("""
            let s: \"\(s)\"
            let n: \(n)
            let start: String.Index = s.index(s.startIndex, offsetBy: n - 1)
            let end: String.Index = s.index(start, offsetBy: 3)
            let result String.SubSequence = s[start..<end]
        """)
start = s.index(s.startIndex, offsetBy: n - 1)
end = s.index(start, offsetBy: 3)
result = s[start..<end]
print("--> \"\(result)\"")

print("RIGHT$(\"\(s)\", \(n)) --> ")
print("""
            let s: \"\(s)\"
            let n: \(n)
            let start: String.Index = s.index(s.endIndex, offsetBy: -n)
            let result: String.SubSequence = s[start...]
        """)
start = s.index(s.endIndex, offsetBy: -n)
result = s[start...]
print("--> \"\(result)\"")

print("LEN(\"\(s)\") --> ", terminator: "")
print("\"\(s)\".count --> \(s.count)")

print("VAL(\"\(s)\") --> ", terminator: "")
print("Double(\"\(s)\") ??  0 --> \(Double(s) ?? 0)")

print("STR$(\(n)) --> ", terminator: "")
print("String(\(n)) --> \"\(String(n))\"")

print("SPC(\(n)) --> ", terminator: "")
print("String(repeating: \" \", count: \(n)) --> \"\(String(repeating: " ", count: n))\"")

Rilataj paĝoj

Basics off
Metaprojekto pri la projektoj «Basics of…».
Basics of 8th
Konverto de malnovaj BASIC-programoj al 8th por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Ada
Konverto de malnovaj BASIC-programoj al Ada por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Arturo
Konverto de malnovaj BASIC-programoj al Arturo por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of C#
Konverto de malnovaj BASIC-programoj al C# por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of C3
Konverto de malnovaj BASIC-programoj al C3 por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Chapel
Konverto de malnovaj BASIC-programoj al Chapel por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Clojure
Konverto de malnovaj BASIC-programoj al Clojure por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Crystal
Konverto de malnovaj BASIC-programoj al Crystal por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of D
Konverto de malnovaj BASIC-programoj al D por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Elixir
Konverto de malnovaj BASIC-programoj al Elixir por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of F#
Konverto de malnovaj BASIC-programoj al F# por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Factor
Konverto de malnovaj BASIC-programoj al Factor por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of FreeBASIC
Konverto de malnovaj BASIC-programoj al FreeBASIC por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Gleam
Konverto de malnovaj BASIC-programoj al Gleam por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Go
Konverto de malnovaj BASIC-programoj al Go por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Hare
Konverto de malnovaj BASIC-programoj al Hare por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Haxe
Konverto de malnovaj BASIC-programoj al Haxe por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Icon
Konverto de malnovaj BASIC-programoj al Icon por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Io
Konverto de malnovaj BASIC-programoj al Io por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Janet
Konverto de malnovaj BASIC-programoj al Janet por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Julia
Konverto de malnovaj BASIC-programoj al Julia por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Kotlin
Konverto de malnovaj BASIC-programoj al Kotlin por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Lobster
Konverto de malnovaj BASIC-programoj al Lobster por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Lua
Konverto de malnovaj BASIC-programoj al Lua por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Nature
Konverto de malnovaj BASIC-programoj al Nature por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Neat
Konverto de malnovaj BASIC-programoj al Neat por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Neko
Konverto de malnovaj BASIC-programoj al Neko por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Nelua
Konverto de malnovaj BASIC-programoj al Nelua por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Nim
Konverto de malnovaj BASIC-programoj al Nim por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Nit
Konverto de malnovaj BASIC-programoj al Nit por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Oberon-07
Konverto de malnovaj BASIC-programoj al Oberon-07 por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of OCaml
Konverto de malnovaj BASIC-programoj al OCaml por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Odin
Konverto de malnovaj BASIC-programoj al Odin por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Pike
Konverto de malnovaj BASIC-programoj al Pike por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Pony
Konverto de malnovaj BASIC-programoj al Pony por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Python
Konverto de malnovaj BASIC-programoj al Python por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Racket
Konverto de malnovaj BASIC-programoj al Racket por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Raku
Konverto de malnovaj BASIC-programoj al Raku por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Retro
Konverto de malnovaj BASIC-programoj al Retro por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Rexx
Konverto de malnovaj BASIC-programoj al Rexx por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Ring
Konverto de malnovaj BASIC-programoj al Ring por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Rust
Konverto de malnovaj BASIC-programoj al Rust por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Scala
Konverto de malnovaj BASIC-programoj al Scala por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Scheme
Konverto de malnovaj BASIC-programoj al Scheme por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Styx
Konverto de malnovaj BASIC-programoj al Styx por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of V
Konverto de malnovaj BASIC-programoj al V por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Vala
Konverto de malnovaj BASIC-programoj al Vala por lerni la fundamentojn de ĉi-tiu lingvo.
Basics of Zig
Konverto de malnovaj BASIC-programoj al Zig por lerni la fundamentojn de ĉi-tiu lingvo.

Eksteraj rilataj ligiloj