Programmering med ChatGPT

Fra HTX Arduino
Spring til navigation Spring til søgning

Det er ikke så svært at få ChatGPT[1] til at lave kode, der skal man blot beskrive hvad man gerne vil have programmet til at gøre, og så kopiere den kode, der kommer ud af det - det giver tit ret gode resultater.

Problemet med at anvende ChatGPT i forbindelse med skoleprojekter er, at man skal have forståelsen med i det, og at man ikke kan blive anklaget for plagiat.

Hvad må man ikke i et skoleprojekt

  • Man må ikke kopiere kode, som man ikke selv har skrevet, til et programmeringsprodukt uden at man angiver en kilde. Det er uanset hvor koden stammer fra, også selvom man har brugt ChatGPT til at "udvikle" koden.
  • Man må ikke aflevere et produkt hvor de primære dele af koden er kopieret, heller ikke selvom man kildehenviser.
  • Man må ikke få ChatGPT til at dokumentere sin kode, ved at man kopierer ChatGPT's beskrivelse ind i sin dokumentation af koden.

De beskrevne eksempler er at betragte som plagiat, og vil blive håndteret efter skolens ordensregler, hvilket i værste fald kan ende med at et fag annulleres eller man bliver bortvist fra uddannelsen.

Hvad er tilladt med ChatGPT

På samme måde som man må anvende kilder fra internettet, så må man i programmerings-faget i gymnasiet anvende ChatGPT i udviklingen af kode til et programmeringsprodukt. Dette er blevet tilladt via en revidering af vejledningen til faget[2].

I vejledningen står der som følger:

"brug af internettet er tilladt som fagligt hjælpemiddel ved de mundtlige prøver, inkluderer de tilladte hjælpemidler anvendelsen af chatbots og lignende på en måde, som afspejler, hvordan man arbejder i den daglige undervisning. Dette kunne fx være brug af ChatGPT til at debugge kode."

Det skal altså afspejle den normale brug, som man også har ved at bruge internettet. Som angivet under hvad man ikke må, så er den normale brug, at man anvender information til at lære at programmere, men at man ikke tager så meget af sit produkt fra nettet, så man ikke har en forståelse af hvordan produktet er sammensat.

Videre i vejledningen står der:

"I forhold til denne type hjælpemidler er det vigtigt at være opmærksom på, at den tilladte anvendelse er betinget af, at hjælpemidlerne kun benyttes i et omfang, hvor prøvebesvarelsen er selvstændig og udelukkende elevens egen."

Igen understreges at det der afleveres skal være elevens selvstændigt udviklede produkt og dokumentation, hvor man på normal vis anvender internettet som et værktøj til at bibringe forståelse.

Problemet med ChatGPT

Ligesom man ikke kan kilde-henvise til "google", for at angive at man har fundet informationer på nettet, så kan man heller ikke kildehenvise til ChatGPT direkte som en kilde.

Når man finder informationer med google', så skal man angive hvor på nettet man har fundet dem med så præcis en kildehenvisning, at man kan se den information der er blevet anvendt.

Denne type henvisning kan man ikke lave i forhold til ChatGPT, da sprogmodellen der ligger bag ChatGPT konstant udvikler sig, samt at de svar sprogmodellen giver har et randomiseret element indbygget, som er med til at give bedre svar[3].

Her er man altså nødt til at tage andre metoder i brug, for at kunne dokumentere at man har udviklet sit produkt ordentligt og redeligt.

Kildehenvisninger i dokumentation

Der kan være forskellige standarder for hvordan man kildehenviser i forskellige uddannelser, dette er skrevet med udgangspunkt i at man anvender Word bibliografi-værktøj og her vist med en Harvard-lignende standard[4].

Normal Internet-kilde til kode

I dette eksempel er der angivet hvordan man ville dokumentere at man har anvendt kode fra et bibliotek til noget hardware på en Arduino. I det tænkte eksempel er det en 4 kanals AD-konverter[5], der er købt som modul.

For at kunne anvende modulet i koden skal der kommunikeres med modulet på en måde, som er angivet i beskrivelsen af modulet - det er dog ikke nødvendigt at sætte sig ind i den grundlæggende kommunikation, hvis man blot ønsker at få værdier ind i sin software. Som en dokumentation for hvordan man anvender biblioteket kan man skrive følgende:

Eksempel på dokumentation

Til at hente værdierne fra den 4 kanals AD-konverter anvendes et bibliotek fundet på GitHub (Singh, 2018) som kan indlæse de analoge spændinger. Til at sætte AD-konverteren rigtigt op, til at kunne læse 4 spændinger, så anvendes eksemplet ”ADS1115_SingleEnded.ino” og i setup skrives følgende kode:

    ads.getAddr_ADS1115(ADS1115_DEFAULT_ADDRESS);   // 0x48, 1001 000 (ADDR = GND)
    ads.setGain(GAIN_TWO);          // 2x gain   +/- 2.048V  1 bit = 0.0625mV (default)
    ads.setMode(MODE_CONTIN);       // Continuous conversion mode
    ads.setRate(RATE_128);          // 128SPS (default)
    ads.setOSMode(OSMODE_SINGLE);   // Set to start a single-conversion
    ads.begin();

I loop() testes i første omgang, om det kan lade sig gøre at læse de 4 kanaler. Dette gøres ved at afvikle følgende testkode:

    address = ads.ads_i2cAddress;
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0)
    {
        int16_t adc0, adc1, adc2, adc3;

        adc0 = ads.Measure_SingleEnded(0);
        Serial.print("Digital Value of Analog Input at Channel 1: ");
        Serial.println(adc0);
        adc1 = ads.Measure_SingleEnded(1);
        Serial.print("Digital Value of Analog Input at Channel 2: ");
        Serial.println(adc1);
        adc2 = ads.Measure_SingleEnded(2);
        Serial.print("Digital Value of Analog Input at Channel 3: ");
        Serial.println(adc2);
        adc3 = ads.Measure_SingleEnded(3);
        Serial.print("Digital Value of Analog Input at Channel 4: ");
        Serial.println(adc3);
    }

Denne test giver det ønskede resultat.

Ud fra denne beskrivelse viser man, at man tager andres kode, og anvender ind i sit eget projekt, og man angiver præcist i sin beskrivelse hvad man har taget fra andres kode.

I kildelisten vil kilden optræde på følgende måde:

Singh, Y., 2018. Arduino Library ADS1115 16 bit 4 channel ADC. [Online]
Available at: https://github.com/ncdcommunity/Arduino_Library_ADS1115_16Bit_4Channel_ADC
[Senest hentet eller vist den 15 marts 2024].

ChatGPT som kilde til kode

Det følgende er skrevet med udgangspunkt i det der er beskrevet på viden.ai[6]. hvor der angives hvordan man kildehenviser, men også lidt om hvordan man håndterer det i tekst - i korte træk, lav et metodeafsnit, beskriv hvad man gør og vær meget redelig, ved at man angiver hvad man har fået af ChatGPT ved at lægge det ved som et bilag.

Hvis man forstår at prompte fornuftigt, så er ChatGPT faktisk ret god til at foreslå kode, der kan anvendes, men ligesom det er plagiat hvis man kopierer fra nettet, så er det plagiat, hvis man lader ChatGPT skrive store dele af ens program for sig.

Som det er formuleret i vejledningen til både programmering C og B, så kan man anvende ChatGPT til hjælp i fejlfinding af kode. For at kunne kildehenvise korrekt til hjælp med fejlfinding, så skal man angive hvad man har promptet til ChatGPT og hvad den har svaret - det kan placeres i bilag.

En anden ting, som også er OK at få hjælp med er, at komme i gang med programmet - det er denne problematik der her gives et eksempel på. I eksemplet beskrives hvad man skal skrive i sin dokumentation, når man har anvendt denne hjælp, og der er forslag til hvordan man kommer videre i projektet.

Konkret er det starten på udviklingen af et lille spil med bolde i et vindue, udviklet i Processing, der tages udgangspunkt i.

Metodeafsnit med omtale af ChatGPT

Når man anvender CharGPT, så skal man dokumentere hvordan man har gjort det, og hvilke overvejelser der ligger bag det. Det betegnes i rapportskrivning som et metodeafsnit. Det kunne i dette eksempel lyde:

For at få en fornuftig start på programkoden er der brugt ChatGPT til at få rammerne på koden etableret, og til at få et system, så der kan optræde flere bolde i programvinduet. Det er ChatGPT 3.5 (OpenAI, 2023)[7] der er anvendt til denne hjælp. Til programstarten blev der promptet efter at få et programvindue på 800x600 hvor en bold bevæger sig rundt. Den foreslåede kode blev kopieret ind i processings IDE (Processing, 2024)[8] og testet – den opførte sig som forventet.

Derefter blev der promptet efter at få den bold der bevægede sig til at dele sig op i to, når man klikker på den med musen. Dette resulterede i en væsentligt anderledes kode, som igen blev kopieret ind i processing og testet, og igen med et acceptabelt resultat. Dialogen med ChatGPT (OpenAI, 2023) og herunder de to koder, kan ses i bilag 1.

Herefter er resten af koden udviklet på traditionel vis, hvor første trin var at dokumentere princippet i den foreslåede kode, specielt den Ball klasse der blev introduceret for at kunne arbejde med flere bolde i vinduet.

Det nævnte bilag 1 kan ses i denne PDF.

Når man opretter kilden i Word, så er det lidt forskelligt hvilke felter der er synlige, alt efter hvilken standard man har aktiv - men ved Harvard-Anglia standarden kan indtastningerne se således ud:

Indtastningerne ved kildehenvisning til ChatGPT
Indtastningerne ved kildehenvisning til ChatGPT

Mere om teknikken i at anvende kildelister kan ses på Holstebro HTX Wiki - Kildelister.

Den meget nemmere metode

Tingene udvikler sig hele tiden, og nu kan man faktisk få et link til den dialog man har haft med ChatGPT 3.5. Hvordan andre sprogmodeller fungerer må man selv eksperimentere sig frem til.

Måden man gør det på er, at man klikker på en ikon i øverste højre side af websiden med den dialog man gerne vil linke til, som vist herunder, hvor man kommer i en dialog, så man kan kopiere linket.
Link ikon til dialog i chatGPT
Link ikon til dialog i chatGPT

Selve linket ser ud som følger: https://chat.openai.com/share/90e9607f-bff9-40d1-a68c-9617051e9615.

Dokumentation af koden udviklet af ChatGPT

Da der skulle kunne dannes nye bolde så oprettes der en klasse, som ChatGPT skriver det i dialogen i Bilag 1:

"Dette kodeeksempel opretter en klasse kaldet Ball, som repræsenterer boldobjekterne. Når musen klikkes, kontrolleres det, om musens position er inden for en bold. Hvis det er tilfældet, oprettes to nye boldobjekter, der bevæger sig i forskellige retninger, og den originale bold fjernes fra arrayet."

Ved at teste koden kan man se at det ikke er det helt rigtige den formulerer - der oprettes godt nok to nye bold-objekter, men det er ikke den oprindelige bold der fjernes, det er den ene af de ny-oprettede bolde. Det kan ses ved at de to bolde kommer i samme position, og den ene fortsætter i samme retning (den oprindelige), mens den ny-oprettede går i en tilfældig retning. Dette rettes til senere i dokumentationen under "oprettelse af ny bold".

For at have en god struktur i koden, så placeres klassen Ball i en fil for sig kaldet Ball. Denne fil starter med at definere koden som en klasse med linjen:

class Ball {

Dernæst defineres klassens egenskaber, der er boldens position og hastighed samt en fast diameter. Ud over det er der en x- og y-retning defineret. Den anvendes til at sige om bolden bevæger sig i en positiv eller negativ retning, så de skifter mellem +1 og -1, når kanterne rammes.

  float x, y;
  float xspeed, yspeed;
  int diameter = 30;
  int xdirection = 1;
  int ydirection = 1;

Der defineres to forskellige constructors til bolden. Den første anvendes til at danne den første bold med, hvor position og hastighed sættes tilfældigt.

Her forudses et problem at der kan komme et randfænomen, hvis bolden dannes, så den ligger med noget uden for skærmen. Da det er tilfældigt; så er det sjældent problemet opstår. For at fremprovokere det oftere er det testet med en diameter på 200, hvor det kunne ses at bolden bliver "hængende" i kanten.

  Ball() {
    x = random(width);
    y = random(height);
    xspeed = random(0.5, 2);
    yspeed = random(0.5, 2);
  }

Dette rettes ved at omskrive den foreslåede constructor til følgende kode, hvor bolden placeres mindst en halv diameter fra kanten:

  Ball() {
    x = random(diameter / 2, width - diameter / 2);
    y = random(diameter / 2, height - diameter / 2);
    xspeed = random(0.5, 2);
    yspeed = random(0.5, 2);
  }

Den anden constructor tager fire parametre, der er positionen og hastigheden. Denne constructor anvendes, når der skal dannes nye bolde, hvor koden netop skal starte den nye bold i den oprindelige bolds position, og med en ny retning.

  Ball(float x, float y, float xspeed, float yspeed) {
    this.x = x;
    this.y = y;
    this.xspeed = xspeed;
    this.yspeed = yspeed;
  }

Ud over disse standard ting til oprettelsen af objekter, så er der 3 metoder i klassen. Den første er en move() metode:

  void move() {
    x = x + (xspeed * xdirection);
    y = y + (yspeed * ydirection);

    if ((x + diameter/2 >= width) || (x - diameter/2 <= 0)) {
      xdirection *= -1;
    }
    if ((y + diameter/2 >= height) || (y - diameter/2 <= 0)) {
      ydirection *= -1;
    }
  }

Måden metoden virker på er at den lægger hastigheden til boldens position (både x og y), og hvis boldens position (både i x og y) er kommet uden for rammen, så ændres retningen for enten x eller y.

For at kunne vise bolden er der en display() metode:

  void display() {
    ellipse(x, y, diameter, diameter);
  }

Den sidste metode hedder contains(), og den tager en position som parameter til metoden. Der undersøges så om den position ligger inden for bolden ved at se om afstanden mellem boldens center og den testede position er mindre end den halve diameter. Hvis den er inden for bolden, så returneres true, ellers returneres false:

  boolean contains(float x, float y) {
    float d = dist(x, y, this.x, this.y);
    if (d < diameter/2) {
      return true;
    } else {
      return false;
    }
  }

Oprettelse af ny bold

I den oprindelige kode undersøges om der er klikket på en bold, ved at alle bolde løbes igennem i mousePressed() funktionen, og med metoden contains undersøges om der er klikket på en bold. Denne måde at undersøge alle bolde på beholdes.

Som omtalt er det ikke den smarteste måde til at dele bolden op i to, der anvendes. Som det ses i koden herunder, så appendes der to nye bolde på med den oprindelige bolds x- og y-position på samt tilfældige x- og y-hastigheder. Disse to bolde lægges i slutningen af arrayet. Derefter anvendes shorten[9] til at fjerne et element, men da det element der fjernes er det sidste element i arrayet, så fjernes der bare den sidst oprettede bold.

Hvis man var tilfreds med den opførsel, så kunne man blot fjerne kodelinjen med den anden append og kodelinjen med shorten. Så ville man have samme funktion.

    if (balls[i].contains(mouseX, mouseY)) {
      balls = (Ball[]) append(balls, new Ball(balls[i].x, balls[i].y, random(-5, 5), random(-5, 5)));
      balls = (Ball[]) append(balls, new Ball(balls[i].x, balls[i].y, random(-5, 5), random(-5, 5)));
      balls = (Ball[]) shorten(balls);
      break;
    }

Den nye bold der oprettes får bare en tilfældig retning, og den oprindelige bold fortsætter bare i den retning den var på vej i. Det er ikke den opførsel der ønskes i spillet. Der ønskes i stedet at det ser ud som om bolden "splittes op" i 2 bolde, og den opførsel laves ved at lade den oprindelige bold ændre 30 grader til den ene side, og den nye bold får 30 grader til den anden side.

For at gøre dette, så oprettes der en metode multiply() i klassen Ball. Denne metode returnerer en ny bold af typen Ball, så nu kan koden i museklikket skrives som følger:

    if (balls[i].contains(mouseX, mouseY)) {
      balls = (Ball[]) append(balls, balls[i].multiply());
      break;
    }

Som det kan ses, så appendes der en ny bold ind i balls-arrayet, ud fra metoden multiply().

Metoden multiply() oprettes inde i klassen med følgende kode:

  Ball multiply() {
    xspeed *= xdirection;
    yspeed *= ydirection;
    xdirection = 1;
    ydirection = 1;
    float speed = dist(0, 0, xspeed, yspeed);
    float angle = acos(xspeed / speed);
    if (yspeed < 0) angle = 0.0 - angle;
    speed *= 1.5;
    xspeed = speed * cos(angle + PI / 6);
    yspeed = speed * sin(angle + PI / 6);
    Ball b = new Ball(x, y, speed * cos(angle - PI / 6), speed * sin(angle - PI / 6));
    return b;
  }

Først regnes retningen over i xspeed og yspeed, så de kan være både positive og negative. På den måde indeholder de to variabler retningen.

Dernæst omregnes x- og y-hastigheden til en samlet hastighed speed og en vinkel angle, som kan være mellem +PI og -PI (+/- 180 grader).

Begge bolde skal bevæge sig hurtigere efter de er delt, så spillet gradvist bliver sværere, derfor ganges hastigheden med 1,5.

Den oprindelige bold får nu en ny retning, ved at udregne x- og y-hastigheden ud fra den nye hastighed og vinklen plus 30 grader.

Til sidst oprettes en ny bold med samme position som den oprindelige og en x- og y-hastighed udregnet fra den oprindelige bolds hastighed og vinklen minus 30 grader. Denne bold returneres fra metoden.

Refleksioner over anvendelsen

Der er selvfølgelig stadig lang vej til man har et færdigt spil, men ideen var også at tage fat i, hvordan man anvender ChatGPT på en ordentlig og redelig måde i et programmeringsprojekt. Dette kan selvfølgelig altid gradbøjes, men hvis man håndterer anvendelsen som vist i eksemplet, så vil man ikke gøre noget der kommer i nærheden af plagiat.

Referencer

Programmering
Programmeringsbegreber Initialisering - Sekvens - Algoritme - Hexadecimal - Det Binære Talsystem - HEX-fil - ASCII - Interrupt - Events - Styresystem - Autocomplete - Selvstudie Programmering - Hour Of Code - Stepwise Improvement - Syntaks - Prog-links - Microcontroller - ChatGPT
Grundlæggende C C float - C double - C-løkker - Datatyper - Konstanter - Regnearter - Funktioner - Return - Returværdi - Rekursion - Semikolon
Variabel Typer boolean - byte - int - unsigned int - word - long - unsigned long - short - float - double - char - unsigned char - string - char array - String - object - Array - 2-dimensionelt Array - void
Program-klassikere Polling - State-machine - Trykknap - Forkant - Bagkant - Prel
Arduino Arduino til Programmering - C til Arduino - Programmering Shield - Arduino PC-software - Arduino Udviklingsmiljø - Arduino Pin Library - Funktion - Arduino Seriel - Arduino String - Arduino String Split - Arduino StateChangeDetection - setup() - loop() - Compilerdirektiver - Asynkron kommunikation - millis() - micros() - Scratch for Arduino - Send fra Arduino til Excel - [[]] - [[]]
Processing Grafik i Processing‎ - Keyboard i Processing - Mus i Processing‎ - Tid i Processing‎ - Draw() - Setup() - Tal Input til Processing - Syntaksfarvning - Kommunikation fra Arduino til Processing - Kommunikation fra Processing til Arduino
javaScript Javascript input‎ - Javascript output‎ - Javascript strukturer‎ - Javascript syntaks‎ - Tid i javaScript - Objekt‎ - AJAX
Serverprogrammering PHP - MySQL - Task Scheduler - WeMOS
PIC JAL - [[]]
Scratch for Arduino S4A Installation - S4A programmering - S4A undervisningsforløb - S4A begrænsninger
Program Dokumentation Algoritme - Flowchart - Pseudokode - Datastruktur - Dataabstraktion - Pulsplaner - Program-kommentar - Teori - Test - UML