Created
October 2, 2020 21:20
-
-
Save gabrieloc/a14eaff9e2582cc27fdf5521b00d7f21 to your computer and use it in GitHub Desktop.
Revisions
-
gabrieloc created this gist
Oct 2, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,188 @@ import Foundation import simd enum Input: String, CaseIterable { case up = "▴" case right = "▸" case down = "▾" case left = "◂" var bytes: [UInt8] { switch self { case .up: return [27, 91, 65] case .right: return [27, 91, 67] case .down: return [27, 91, 66] case .left: return [27, 91, 68] } } init?(_ bytes: [UInt8]) { guard let match = Self.allCases.first(where: { $0.bytes == bytes }) else { return nil } self = match } } class InputHandler { static func initStruct<S>() -> S { let struct_pointer = UnsafeMutablePointer<S>.allocate(capacity: 1) let struct_memory = struct_pointer.pointee struct_pointer.deallocate() return struct_memory } static func enableRawMode(fileHandle: FileHandle) -> termios { var raw: termios = initStruct() tcgetattr(fileHandle.fileDescriptor, &raw) let original = raw raw.c_lflag &= ~(UInt(ECHO | ICANON)) tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &raw); return original } static func restoreRawMode(fileHandle: FileHandle, originalTerm: termios) { var term = originalTerm tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &term); } let originalTerm: termios init() { self.originalTerm = Self.enableRawMode( fileHandle: FileHandle.standardInput ) } deinit { Self.restoreRawMode( fileHandle: FileHandle.standardInput, originalTerm: originalTerm ) } var input: Input? } class Renderer { var buffer: [Character] var length: Int { buffer.count } let size: simd_int2 init(size: simd_int2) { self.size = size self.buffer = Array<Character>( repeating: "▓", count: Int(size.x * size.y) ) } func scale(_ position: simd_float2) -> simd_int2 { simd_int2( simd_int2.Scalar(position.x * Float(size.x)), simd_int2.Scalar(position.y * Float(size.y)) ) } func drawChar(_ char: Character, position: simd_float2) { let i = scale(position).index(rowLength: size.x) self.buffer[i % length] = char } func drawRect(frame: float2x2, char: Character) { let p1 = convertViewportPoint(frame.columns.0) let p2 = convertViewportPoint(frame.columns.0 + frame.columns.1) (p1.x..<p2.x).forEach { x in (p1.y..<p2.y).forEach { y in let i = Int(simd_int2(x: x, y: y).index(rowLength: size.x)) self.buffer[i % length] = char } } } func error() { buffer = Array<Character>(repeating: "▓", count: length) } func clear() { buffer = Array<Character>(repeating: "░", count: length) } func blit() { print("\u{001B}[2J") print( String ( stride(from: 0, to: length, by: Int(size.x)).map { i in String(buffer[i..<(i + Int(size.x))]) }.joined(separator: "\n") ) ) } func convertViewportPoint(_ point: simd_float2) -> simd_int2 { simd_int2( x: Int32(Float(size.x) * point.x), y: Int32(Float(size.y) * point.y) ) } } extension simd_int2 { func index(rowLength: Scalar) -> Int { Int((y * rowLength) + (x % rowLength)) } } class Game { let inputHandler = InputHandler() let renderer = Renderer( size: simd_int2(x: 40, y: 10) ) var isRunning = false var pointer = simd_float2(0.5, 0.5) let speed: Float = 0.1 let guy = Character("☃︎") func start() { var data: Data repeat { data = FileHandle.standardInput.availableData let bytes = [UInt8](data) let input = Input(bytes) renderer.clear() if let input = input { switch input { case .up: pointer -= simd_float2(0, speed) case .right: pointer += simd_float2(speed, 0) case .down: pointer += simd_float2(0, speed) case .left: pointer -= simd_float2(speed, 0) } pointer.clamp( lowerBound: simd_float2.zero, upperBound: simd_float2.one ) } renderer.drawChar( guy, position: pointer ) renderer.blit() } while (data.count > 0) } func stop() { isRunning = false } } let game = Game() game.start()