Kann ChatGPT programmieren? Das Experiment
Im zweiten Teil meiner Artikelserie stelle ich mir die Frage: Kann ChatGPT programmieren? Um das herauszufinden werde ich in einem Experiment die KI mit meinen Anforderungen konfrontieren. Kann die künstliche Intelligenz den Beruf Software Entwickler ersetzen?
Kann ChatGPT programmieren? Das Experiment
Ich bin ja gemein. Ich könnte Bing mit einem einfach „Hello World“ Programm konfrontieren, aber ich gehe einfach davon aus, dass diese KI mehr kann. Ein „Hello World“ Programm findet man vollständig für jede erdenkliche Programmiersprache zig Mal mit einer beliebigen Suchmaschine. Die Herausforderung ist eher folgende: kann ChatGPT bzw. das trainierte Modell dahinter bereits Anforderungen verstehen und mir eine konkrete Antwort als Source Code präsentieren? Nichts anderes macht ein Entwickler täglich. Deshalb frage ich etwas unübliches, aber passend für meinen Blog:
Ich: Ich benötige für meinen C64 ein Assemblerprogramm, welches mir ein buntes Sprite in Form eines Vogels auf dem Bildschirm ausgibt und im Kreis bewegt.
Bing: Es gibt viele Assemblerprogramme für den C64, die ein Sprite in Form eines Vogels auf dem Bildschirm ausgeben und im Kreis bewegen können1. Eine Anleitung zur Sprite-Programmierung findest du im C64 Benutzerhandbuch2. Wenn du weitere Assembler-Beispielprogramme benötigst, kannst du diese auf der C64-Wiki Seite finden1. Es gibt viele Assemblerprogramme für den C64, die ein Sprite in Form eines Vogels auf dem Bildschirm ausgeben und im Kreis bewegen können. Eine Anleitung zur Sprite-Programmierung findest du im C64 Benutzerhandbuch. Wenn du weitere Assembler-Beispielprogramme benötigst, kannst du diese auf der C64-Wiki Seite finden.
Analyse 1
Auf der einen Seite bin ich schwer enttäuscht mit der Antwort. Gute Recherche, keine Frage, aber kein Source Code. Mein Job scheint auch weiterhin gesichert. Eventuell bekomme ich mit einer anderen einfacheren Fragestellung MOS 6510 Steuercodes für ein Programm.
Gib mir doch Assembler Code!
Also doch Hello World. Aber damit die Aufgabe nicht zu trivial wird bestehe ich auf meinen C64 Code in Assembler Sprache.
Ich: Schreibe mir ein Programm in Assembler Code für den C64 welches den Text „Hallo Bing“ ausgibt.
Bing: Ich kann Ihnen dabei helfen, ein Assemblerprogramm für den C64 zu schreiben, das den Text “Hallo Bing” ausgibt. Hier ist ein Beispielprogramm:
; Hallo Bing Programm
; von Bing
lda #$0e ; Zeichen „H“ laden
jsr $ffd2 ; Zeichen ausgeben
lda #$01 ; Zeichen „a“ laden
jsr $ffd2 ; Zeichen ausgeben
lda #$0b ; Zeichen „l“ laden
jsr $ffd2 ; Zeichen ausgeben
jsr $ffd2 ; Zeichen ausgeben
lda #$16 ; Zeichen „o“ laden
jsr $ffd2 ; Zeichen ausgeben
lda #$00 ; Leerzeichen laden
jsr $ffd2 ; Leerzeichen ausgeben
lda #$0c ; Zeichen „B“ laden
jsr $ffd2 ; Zeichen ausgeben
lda #$0d ; Zeichen „i“ laden
jsr $ffd2 ; Zeichen ausgeben
lda #$0e ; Zeichen „n“ laden
jsr $ffd2 ; Zeichen ausgeben
ende jmp ende ; Endlosschleife
Analyse 2
Obwohl es eine triviale Anforderung ist, hat mich die zweite Antwort nun doch überrascht. Ich habe Source Code bekommen und nicht nur das, ChatGPT hat den Code auch gleich schön kommentiert und mit einem Header versehen in dem es sich selbst (Bing) als Autor nennt. Ich habe den Code kopiert und im CBM Studio getestet…zuerst ohne Erfolg.
Manuelle Änderungen
Es wird Zeit für Details. Grundsätzlich gehe ich davon aus, dass der Code korrekt ist. Da ich eine IDE verwende muss ich auch dessen Regeln befolgen. Ich füge also automatisch generierten Code für den Sys() Call ein (damit das Programm automatisch startet) und definiere noch die Startadresse (jene Adresse ab wo im RAM Speicher der Programmcode steht). Der folgende Test ist leider wieder ernüchternd, das Programm läuft zwar, es gibt aber nichts aus.
Nach einiger Recherche habe ich den Code von Bing! doch noch zum Laufen gebracht. Die KI hat den „Fehler“ gemacht die Zeichen falsch zu laden. Die Speicheradressen zeigten auf Steuerzeichen, weshalb sich die Schriftart der Konsole geändert hat, jedoch keine Zeichen ausgegeben worden sind. Anhand der PETSCII Tabelle habe ich die entsprechenden Zeichen angepasst und Bing somit korrigiert.
Analyse 3
Die Petscii Zeichen sind fixe Werte im Speicher. Der Code von Bing ruft die Zeichen offenbar aus einer anderen Zeichentabelle auf. Mir ist nicht bekannt, welche Anforderungen es dafür gibt, ein 0815 C64 gibt mit dem Code jedenfalls keinen Text aus. Des weiteren ist mir erst bei der Ausgabe der korrigierten Version aufgefallen, das ChatGPT einfach mal den Buchstaben G am Ende vergessen hat. Ich habe das mehrfach geprüft, aber das kam tatsächlich so zurück
Falls ihr euch fragt was der Code genau macht. Es wird pro Zeichen ein Byte von der Petscii Tabelle ins A Register geladen und die Kernal Funktion FFD2 oder auch CHROUT aufgerufen. Diese Funktion aus dem ROM gibt das Zeichen aus dem Register A auf dem Standard Output aus. Das Ergebnis schaut bei mir wie folgt aus:
C# ist einfacher?
Zugegeben, Assemblercode für einen Prozessor aus dem Jahre 1982 ist doch eine Anwendung für eine kleine Nische. Deshalb möchte ich für diesen Artikel auch eine moderne Sprache testen. C# eignet sich dabei vermutlich am besten, kommt die doch auch (OpenAI gehört ja nun mehrheitlich Microsoft) aus dem gleichen Hause. Verbinden möchte ich die Anfrage mit einem der Aufgaben aus meinem Studium, Convay’s Game of Life.
Ich: „Schreibe mir ein C# Programm, welches Convay’s Game of Life implementiert und auf der Konsole eine Simulation für ein Grid mit 100 mal 100 Zellen für 100 Generationen ausgibt.“
Bing: „Here’s a C# implementation of Conway’s Game of Life that you can run on the console.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | using System; using System.Threading; class Program { static void Main(string[] args) { int[,] grid = new int[100, 100]; int[,] newGrid = new int[100, 100]; // Initialize grid with random values Random random = new Random(); for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { grid[i, j] = random.Next(2); } } // Run simulation for 100 generations for (int generation = 0; generation < 100; generation++) { Console.Clear(); Console.WriteLine("Generation: " + generation); // Print current grid for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { Console.Write(grid[i, j] == 1 ? "X" : " "); } Console.WriteLine(); } // Calculate new grid for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { int neighbors = CountNeighbors(grid, i, j); if (grid[i, j] == 1 && neighbors < 2) { newGrid[i, j] = 0; } else if (grid[i, j] == 1 && (neighbors == 2 || neighbors == 3)) { newGrid[i, j] = 1; } else if (grid[i, j] == 1 && neighbors > 3) { newGrid[i, j] = 0; } else if (grid[i, j] == 0 && neighbors == 3) { newGrid[i, j] = 1; } } } // Swap grids int[,] tempGrid = grid; grid = newGrid; newGrid = tempGrid; // Wait for a short time before next generation Thread.Sleep(50); } } static int CountNeighbors(int[,] grid, int x, int y) { int count = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (!(i == 0 && j == 0) && x + i >= 0 && x + i < grid.GetLength(0) && y + j >= 0 && y + j < grid.GetLength(1)) { count += grid[x + i, y + j]; } } } return count; } } |
This implementation initializes a grid with random values and runs the simulation for **100 generations**. It prints out each generation on the console and waits for a short time before moving on to the next generation.
You can copy this code into a C# file and run it on your console to see the simulation in action.
Let me know if you have any questions!
Analyse 4
Die Erwartungen waren hoch, der Code schaut auf den ersten Blick gut aus. Deshalb habe ich mit Visual Studio gleich ein neues Konsolenprojekt gestartet und den Quellcode in der Programm.cs Datei ersetzt, kompiliert und gestartet.
Das Ergebnis ist wieder nicht das erwartete Programm. Es wird ein zufällig erstelltes Grid ausgegeben. Obwohl die Logik im Code gut aussieht stimmt die Ausgabe nicht. Bei den berechneten 100 Generationen änderte das Grid immer zwischen den exakt 2 gleichen Konstellationen. Diese werden zwar mit jedem neuen Programmaufruf zufällig erstellt, zeigten aber nur 2 offenbar statische Zustände ohne den bekannten Game of Life Mustern. Was war passiert?
Nach einigem Aufwand Fehlersuche habe ich den Fehler ausgemacht. Beim Ändern der zwei Grids (das neu Berechnete und der alte Zustand) wird ein 3-eckstausch gemacht. Dabei wird aber kein neues Grid initialisiert sondern nur der alte Zustand übernommen, weshalb nur noch zwischen 2 Zuständen gewechselt wird. Ändert man diese Zeile und legt stattdessen ein neues leeres Grid an, dann stimmt der Code:
Ein interessanter Test, Werner!
Deckt sich mit anderen Programmier – Versuchen im Netz. Gilt auch bei Mikrokontroller-Programmierung.
MfG Fritz
Danke Fritz!