Basics of Swift

Description of the page content

Conversion of old BASIC programs to Swift in order to learn the basics of this language.

Tags:

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))\"")

Related pages

Basics off
Metaproject about the "Basics of…" projects.
Basics of 8th
Conversion of old BASIC programs to 8th in order to learn the basics of this language.
Basics of Ada
Conversion of old BASIC programs to Ada in order to learn the basics of this language.
Basics of Arturo
Conversion of old BASIC programs to Arturo in order to learn the basics of this language.
Basics of C#
Conversion of old BASIC programs to C# in order to learn the basics of this language.
Basics of C3
Conversion of old BASIC programs to C3 in order to learn the basics of this language.
Basics of Chapel
Conversion of old BASIC programs to Chapel in order to learn the basics of this language.
Basics of Clojure
Conversion of old BASIC programs to Clojure in order to learn the basics of this language.
Basics of Crystal
Conversion of old BASIC programs to Crystal in order to learn the basics of this language.
Basics of D
Conversion of old BASIC programs to D in order to learn the basics of this language.
Basics of Elixir
Conversion of old BASIC programs to Elixir in order to learn the basics of this language.
Basics of F#
Conversion of old BASIC programs to F# in order to learn the basics of this language.
Basics of Factor
Conversion of old BASIC programs to Factor in order to learn the basics of this language.
Basics of FreeBASIC
Conversion of old BASIC programs to FreeBASIC in order to learn the basics of this language.
Basics of Gleam
Conversion of old BASIC programs to Gleam in order to learn the basics of this language.
Basics of Go
Conversion of old BASIC programs to Go in order to learn the basics of this language.
Basics of Hare
Conversion of old BASIC programs to Hare in order to learn the basics of this language.
Basics of Haxe
Conversion of old BASIC programs to Haxe in order to learn the basics of this language.
Basics of Icon
Conversion of old BASIC programs to Icon in order to learn the basics of this language.
Basics of Io
Conversion of old BASIC programs to Io in order to learn the basics of this language.
Basics of Janet
Conversion of old BASIC programs to Janet in order to learn the basics of this language.
Basics of Julia
Conversion of old BASIC programs to Julia in order to learn the basics of this language.
Basics of Kotlin
Conversion of old BASIC programs to Kotlin in order to learn the basics of this language.
Basics of Lobster
Conversion of old BASIC programs to Lobster in order to learn the basics of this language.
Basics of Lua
Conversion of old BASIC programs to Lua in order to learn the basics of this language.
Basics of Nature
Conversion of old BASIC programs to Nature in order to learn the basics of this language.
Basics of Neat
Conversion of old BASIC programs to Neat in order to learn the basics of this language.
Basics of Neko
Conversion of old BASIC programs to Neko in order to learn the basics of this language.
Basics of Nelua
Conversion of old BASIC programs to Nelua in order to learn the basics of this language.
Basics of Nim
Conversion of old BASIC programs to Nim in order to learn the basics of this language.
Basics of Nit
Conversion of old BASIC programs to Nit in order to learn the basics of this language.
Basics of Oberon-07
Conversion of old BASIC programs to Oberon-07 in order to learn the basics of this language.
Basics of OCaml
Conversion of old BASIC programs to OCaml in order to learn the basics of this language.
Basics of Odin
Conversion of old BASIC programs to Odin in order to learn the basics of this language.
Basics of Pike
Conversion of old BASIC programs to Pike in order to learn the basics of this language.
Basics of Pony
Conversion of old BASIC programs to Pony in order to learn the basics of this language.
Basics of Python
Conversion of old BASIC programs to Python in order to learn the basics of this language.
Basics of Racket
Conversion of old BASIC programs to Racket in order to learn the basics of this language.
Basics of Raku
Conversion of old BASIC programs to Raku in order to learn the basics of this language.
Basics of Retro
Conversion of old BASIC programs to Retro in order to learn the basics of this language.
Basics of Rexx
Conversion of old BASIC programs to Rexx in order to learn the basics of this language.
Basics of Ring
Conversion of old BASIC programs to Ring in order to learn the basics of this language.
Basics of Rust
Conversion of old BASIC programs to Rust in order to learn the basics of this language.
Basics of Scala
Conversion of old BASIC programs to Scala in order to learn the basics of this language.
Basics of Scheme
Conversion of old BASIC programs to Scheme in order to learn the basics of this language.
Basics of Styx
Conversion of old BASIC programs to Styx in order to learn the basics of this language.
Basics of V
Conversion of old BASIC programs to V in order to learn the basics of this language.
Basics of Vala
Conversion of old BASIC programs to Vala in order to learn the basics of this language.
Basics of Zig
Conversion of old BASIC programs to Zig in order to learn the basics of this language.

External related links