Blog

  • Sci-Fi World

    Sci-Fi World

    Det är ännu inte för sent att besöka Sci-Fi World i Stockholm, som har öppet även imorgon söndag. Jag spenderade lördagen där, och nördade loss rejält bland Magic-kort och D&D-tärningar. Väldigt mycket kretsade kring Stjärnornas Krig. Dragplåstren var Robert Patrick, Ray Park, Samantha Fox, Joe Cantillo och Sam Jones.

    Sam J. Jones spelade Flash Gordon 1980 (som Queen gjorde soundtracket till) och sig själv i filmen Ted.

    WP_20150425_008

    Detta är bilen Kit från tv-serien Knight Rider med David Hasselhoff:

    WP_20150425_027

    Har tar min dotter kort på mig och Samantha Fox (som sjöng in en låt till Terror på Elm Street 5):

    WP_20150425_011

    Detta är Robert Patrick som spelade T-1000 i filmen Terminator 2 och John Doggett i Arkiv X:

    WP_20150425_019

  • Enkel trådning i .NET med Akka

    Enkel trådning i .NET med Akka

    Historiskt sett har det varit ganska komplicerat att bygga multitrådade applikationer i .NET. Rent tekniskt var det enkelt att sjösätta en tråd med hjälp av klassen System.Threading.Thread, men om mycket skulle göras började koden ganska snabbt likna spaghetti. För att minska ner koden kan man använda multitrådade delegater istället för objekt av typen Thread, men det löste inte problemet med spaghettikod. Nyckelorden asynk och await löste både komplexiteten med hopp och returvärde, men var ganska hårt knutet till modellen TAP.

    Om jag väljer att jobba med Akka .NET blir trådningen ännu mer hanterad, fast kring en mycket friare modell än TAP, nämligen Actor Model. (I alla nedanstående exempel jobbar jag i Visual Studio 2013.) Tänk dig att jobbar i Windows Forms och att du vill utföra något som kräver ungefär fem sekunders arbete och sedan visar ett meddelande när arbetet är utfört. Min erfarenhet är att Akka .NET fortfarande kan utsättas för förändringar i sitt publika API, så den exakta kodens utseende kan variera – jag kör Akka 1.0.0. För att visa detta skapar jag ett helt nytt Windows Forms-projekt (C# med .NET Framework 4.5.1) och installerar Akka .NET från Package Manager Console genom att skriva:

    Install-Package Akka

    Om du inte ser Package Manager Console, Klicka Tools, NuGet Package Manager och Package Manager Console.

    Akka .NET använder meddelanden för att kommunicera. Meddelanden är kort och gott godtyckliga objekt som initieras och skickas i samband med att ett asynkront anrop görs. Meddelandet bör vara immutable, alltså meddelandet ska inte kunna modifieras, vilket kan implementeras genom att objektets data inte har några publika setters.

    class AkkaMessage
    {
       public string MyValue { get; private set; }
    
       public AkkaMessage(string myValue)
       {
          this.MyValue = myValue;
       }
    }

    En actor (aktör) är den t.ex. det objekt som kan ta emot ett meddelande och agera på det. Det som utmärker en sådan aktör är basklassen Akka.Actor.ReceiveActor samt konstruktorn som tar emot meddelandet som skickas till den. För att ta emot meddelandet används den generiska metoden Receive vars argument är det som ska göras med meddelandet. Den generiska metodens typparameter är meddelandeklassen. Så i följande kod anropar vi Receive och skickar med kod jobbar i fem sekunder för att sedan visa en meddelanderuta.

    class AkkaActor : Akka.Actor.ReceiveActor
    {
       public AkkaActor()
       {
          this.Receive(x =>
             {
                System.Threading.Thread.Sleep(5000);
                System.Windows.Forms.MessageBox.Show(x.MyValue);
             }
          );
       }
    }

    Nu återstår bara initieringen och själva anropet. Följande kod har jag skrivit i mitt programs huvudformulär. Först har jag ett privat fält av typen Akka.Actor.ActorSystem som representerar motorn i Akka – det s.k. actorsystemet. I Load-eventet för formuläret initierar jag systemet med funktionen Create. Create vill ha ett namn på systemet i sin kostruktor. Slutligen, under Click-eventet på en knapp på formuläret så gör jag anropet som ska hanteras av aktören ovan.

    Den första raden anropar funktionen ActorOf för att erhålla en referens till en aktör. Den andra raden utför utför anropet med hjälp av funktionen Tell, och låter ett meddelande skickas med som argument. Meddelandet måste vara av typen AkkaMessage, eftersom jag hårdkodade aktören att hantera den typen.

    public partial class Form1 : Form
    {
       private Akka.Actor.ActorSystem actorSystem;
    
       public Form1()
       {
          InitializeComponent();
       }
    
       private void Form1_Load(object sender, EventArgs e)
       {
          this.actorSystem =
             Akka.Actor.ActorSystem.Create("ActorSystem");
       }
    
       private void button1_Click(object sender, EventArgs e)
       {
          var m = this.actorSystem.ActorOf(new
             Akka.Actor.Props(typeof(AkkaActor)));
          m.Tell(new AkkaMessage("Hello!"), m);
       }
    }

    När du kör programmet, notera att programmets fönster är svarsbenäget under den tid programmet pausar pausar (anropet på Sleep), vilket säger oss att aktören och formuläret körs på olika trådar.

    Om du istället hade velat implementera detta i Visual Basic, skulle ditt meddelande kunna se ut så här:

    Public Class AkkaMessage
       Public Property MyValue() As String
       Public Sub New(MyValue As String)
          Me.MyValue = MyValue
       End Sub
    End Class

    Aktören skulle kunna se ut så här:

    Public Class AkkaActor
       Inherits Akka.Actor.ReceiveActor
    
       Public Sub New()
          Me.Receive(Of AkkaMessage)(AddressOf MyMessageHandler)
       End Sub
    
       Private Sub MyMessageHandler(X As AkkaMessage)
          System.Threading.Thread.Sleep(5000)
          MessageBox.Show(X.MyValue)
       End Sub
    
    End Class

    Och slutligen, formuläret skulle kunna se ut så här:

    Public Class Form1
    
       Private ActorSystem As Akka.Actor.ActorSystem
    
       Private Sub Form1_Load(sender As Object, _
          e As EventArgs) Handles MyBase.Load
          Me.ActorSystem = Akka.Actor.ActorSystem.Create("ActorSystem")
       End Sub
    
       Private Sub Button1_Click(sender As Object, _
          e As EventArgs) Handles Button1.Click
          Dim M = Me.ActorSystem.ActorOf( _
             New Akka.Actor.Props(GetType(AkkaActor)))
          M.Tell(New AkkaMessage("Hello!"), M)
       End Sub
    
    End Class
  • Betydelsen av ordet “nörd” är justerat

    Betydelsen av ordet “nörd” är justerat

    Mattias Boström twittrade (följ!) detta igår:

    “Nörd” har fått ändrad definition i nya SAOL-upplagan (upptill på bilden).

    Han bifogar två bilder föreställande den tidigare och den reviderade definitionen från Svenska Akademiens ordlista, där den tidigare lyder “enkelspårig och löjeväckande person, tönt” och den nya lyder “person som har ett stort specialintresse och som därför kan verka något enkelspårig“.

    Eventuellt kan min syn på ordet vara färgad av hur vi använde ordet på 80-talet eftersom jag knappt använt ordet sedan dess, men då var “nörd” svengelska för “nerd” som fortfarande är en nedsättande beskrivning av någon som är lite töntig. Men de som beskriver sig själva som nördar idag, väljer alltså inte ett negativt ord för att påvisa självdistans, utan ett ord som betraktas som relativt neutralt.

    Den som vill ta sig an kulturskatten av college- och high school-filmer från 80-talet kommer fortfarande komma i kontakt med “nördar” i ordets gamla bemärkelse, men dessa kanske vi ska benämna som “nerdar” idag för att visa kopplingen till det engelska ordet “nerd”.

    Vad sägs om höjdarfilmen “Nördarna kommer!” (Revenge of the nerds) från 1985:

    Eller klassikern “Drömtjejen” (Weird Science), också från 1985:

    Mycket nöje!

  • Commoflage 54

    Commoflage 54

    Lyssna gärna på Commoflage avsnitt 54 när Henrik Andersson och jag diskuterar bl.a. lagringshastigheter på Commodore 64. Mycket nöje!

    11083657_1436862563277750_203473098906490422_n

  • Färger i datorer

    Färger i datorer

    Under knappt hälften av den tid vi haft datorer, har dessa varit oförmögna att visa färggrafik. De har presenterat sitt utdata med tända eller släckta bildelement. De första datorerna hade ingen bildskärm alls. Datamaskinen ENIAC (1946) levererade sitt utdata i form av hål i pappskivor, och en av de första hemdatorerna, Altair 8800 (1974), hade några röda lysdioder. Rätt snabbt ansåg man att den mest effektiva metoden att presentera utdata var att visa “tecken” på en tv-skärm, som t.ex. Commodore PET (1977) gjorde. En svart skärm visade 40 x 25 gröna tecken som byggdes av 8 x 8 bildelement (pixlar), alltså 64 punkter per tecken och 320 x 200 punkter totalt. Datorer med bildskärm var alltså som regel monokroma på 70-talet, vilket betyder att de skiljer på tända eller släckta bildelement utan att lägga någon annan aspekt i bildelementens färg. Grafiken kostade bara en bit per pixel, alltså 8 kilobytes totalt (320 x 200 / 8 = 8000).

    År 1980 fick vi uppleva den första hemdatorn som kunde visa grafik i färg – TRS-80 Color Computer 1. Just färgen var så pass viktig att själva operativsystemet, en BASIC-interpretator, fick namnet Color BASIC 1.0. Om det är tillåtet att uttrycka sig i moderna mått: Datorn var utrustad med en processor på knappt 0,0009 gigahertz, vilket är klent jämfört med dagens mobiltelefoner som klarar kring 2,0 gigahertz (iPhone 6 har en processor på 1,6 GHz och Nokia Lumia 930 har en processor på 2,2 GHz). TRS-80 var utrustad med ett minne på 0,000004 GB (vilket kan jämföras med dagens standard på 8 GB) och den var helt utan sekundär lagring, vilket idag kan handla om hårddiskar på terabytes.

    Personligen stannade jag vid monokroma datorer ganska länge. Jag gick från Sinclair ZX81 till Luxor ABC80. När mina vänner började använda Vic 20, Sinclair Spectrum och Commodore 64, så harvade jag vidare med min ZX81 ända fram till år 1986 då jag fick tillgång till en Commodore 128 med en 1571:a som sekundär lagringsenhet, vilket både innebar att jag kunde avbryta mitt arbete på kvällen, och återuppta gårdagens arbete dagen efter, genom att återkalla det från sekundärminnet.

    På den tiden använde man alltså en bit per bildelement. Varje bildelement (eller pixel) utgör en punkt i den matris som utgör skärmbilden, och varje bildelement beskrevs av en bit. Commodore 64 hade en palett av på 16 olika färger, men en bild på 200 x 320 pixlar kunde bara visa två av dessa färger: En förgrundsfärg och en bakgrundsfärg. Varje bildelement kunde fortfarande bara vara tänt eller släckt. Men C64:an hade dessutom ett flerfärgsläge.

    Genom att para ihop två pixlar, fick man helt plötsligt två bitar per pixel. Förvisso var dessa pixlar större, vilket gjorde att grafiken blev kantigare, men nu kunde varje pixel (totalt 160 x 200 avlånga pixlar) beskriva ett värde av fyra tillgängliga (00, 01, 10 eller 11) istället för bara 0 eller 1. Därmed kunde C64 visa fyra av sina 16 färger på skärmen, om än på bekostnad av antalet pixlar. De fyra färgerna valdes från listan av 16 tillgängliga färger.

    Sedan har datorerna succesivt utrustats med mer grafikminne och större färgpalett. På 90-talet fanns persondatorer som med nästan fem gånger så hög upplösning (640 x 480 pixlar) kunde 256 färger ur en palett på 16,8 miljoner färger, vilket kräver 1 megabyte grafikminne. Varje pixel beskrevs av en byte istället för en bit, och varje byte var en referens till en palett.

    Idag har man dels betydligt fler bildelement på skärmen, och dessutom kostar man på sig hela tre bytes per bildelement. Detta ger oss 24 bitar per pixel, vilket ger oss möjlighet att ha 16,8 miljoner kombinationer av tända och släckta bitar per pixel. Därmed har vi eliminerat behovet av att ha en palett att hänvisa till – varje pixel kan beskriva en färg istället för bara en position i en palett, där de första 8 bitarna av 28 beskriver färgens röda del, de nästa 8 beskriver färgens gröna del och de sista 8 beskriver färgens blå del. Utifrån dessa tre färger kan vi få fram alla tänkbara färger genom additiv färgblandning.

    Att förstå additiv färgblandning är enkelt om man tänker sig att man befinner sig i ett vitt rum utan fönster eller belysning. Trots att rummet är vitt så ser vi bara svart, eftersom inget ljus kommer in i våra ögon. Men om du lyser med en röd ficklampa på den vita väggen, kommer åtminstone rött ljus att komma in i dina ögon. Den vita väggen reflekterar alla frekvenser, och eftersom endast rött ljus strålar mot väggen, är det endast rött ljus som kommer in i våra ögon. Men om vi samtidigt lyser med en grön ficklampa på samma punkt på väggen, kommer både röda och gröna strålar att reflekteras från väggen, vilket vi uppfattar som gul färg.

    Om vi låter varje pixel beskriva sin egen färg, men tänker oss att varje pixel endast består av tre bitar där den första representerar röd, den andra grön och den tredje blå, kan vi visa åtta olika färger.

    000 skulle vara svart, eftersom ingen av färgerna lyser.

    100 skulle ge röd färg, eftersom den första representerar röd. Av samma skäl skulle 010 vara grön och 001 vara blå.

    bild1

    Om vi istället för tre bitar (en per grundfärg) skulle ha sex bitar per pixel (två per grundfärg), behöver inte röd bara vara tänd eller släkt (0 eller 1). Nu kan vi säga att 00 betyder släckt, 01 betyder 33% röd, 10 betyder 67% röd och 11 betyder 100% röd.

    bild2

    Detta öppnar som synes för möjligheten att visa mörkröd, du kan nu skapa ljusröd (eller rosa) genom att visa mycket röd och lite grön och blå. Vi kan visa lite av alla färger och få grå, vi kan visa en färg som är på väg att bli lila utan att bara fullt lila. Vi kan även visa lite av varje färg, med övervikt på grön för att få en grågrön nyans.

    bild3

    Ju fler bitar vi avsätter för varje pixel, desto fler färger kan vi visa, men minnesåtgången blir såklart större. Och någonstans vid åtta bitar per färg, alltså 24 bitar, finns en bra kompromiss mellan minnesåtgång och bra återgivning. Det kan finnas anledningar att avsätta ännu fler bitar per pixel för den som vill göra avancerade manipulationer som drar ner bildens kvalité, så att man har en bild som tål att tappa kvalité utan att betraktaren märker något, men då drar man oftast ner bilden till 24 bitar när bilden är färdigmanipulerad och ska betraktas.

  • Nokia och SJ? Windows Phone och SJ?

    Nokia och SJ? Windows Phone och SJ?

    Vissa som kör Windows Phone, särskilt de som kör Nokia, får erfara att det är omständligt för SJ att läsa SMS-biljetten automatiskt, om det ens går. SJ’s personal brukar spekulera om orsaken, mest i form av artighetskonversation, men ingen verkar ha koll. Är Nokias skärm för blank? Är bakgrundsfärgen fel? Svaret är valet av typsnitt.

    SJ’s app letar efter en rektangel som består av fyra sifferrader. Beroende på vilka dessa siffror är, är inte marginalen alltid jämn, vilket syns på denna bild:

    wp_ss_20150319_0002

    En lösning på problemet skulle kunna vara att hålla ner fingret över SMS:biljetten så att context-menyn visas, välja att kopiera, och klistra in biljetten i OneNote, som av en händelse har ett typsnitt som visar alla siffror i samma bredd:

    wp_ss_20150319_0001

    Därför kan SJ läsa av biljetten på ett kick, om du först lägger den i OneNote.

  • Kom igång med C128 Assembler

    Kom igång med C128 Assembler

    Idag finns en hel del produkter för att utveckla program till Commodore 64 eller 128 på en modern PC, vilket är betydligt enklare än att göra det direkt på målmaskinen. Följ dessa steg för att komma igång med Assembler i Commodore 128.

    1. Ladda hem Vice. Vice emulerar de flesta Commodore-maskinerna, vilket förutom C64/C128 även innefattar Pet, Vic 20 och Commodore +4.

    2. Ladda hem CBM .prg Studio. Detta är en fullfjädrad integrerad utvecklingsmiljö för valfri Commodore-maskin.

    3. Starta CBM .prg Studio. Välj Tools -> Options. Klicka på fliken Emulator Control. Bocka för C128 och peka C128-emulatorn från Vice (filen x128.exe).

    4. Välj File -> New Project. Kryssa i C128, klicka Next. Vid Project Location, klicka Browse och välj var du vill spara din källkod. Därefter, klicka Next och Create.

    5. I fönstret Project Explorer, högerklicka på Assembly Files och välj Add New File. Tillhandahåll ett filnamn med filändelsen .asm.

    6. Det sista du måste göra för att komma igång, är att ange ditt programs startadress. Först i din .asm-fil, skriv:

    *=$2000

    Symbolen * representerar programmets start och $2000 är den hexadecimala representationen av 8192. Detta betyder alltså att ditt program startas med SYS 8192. När du väl börjar koda ditt program måste du tänka på att CBM .prg Studio antar att en indragning betyder att du vill göra en programsats. Låt säga att du vill förändra borderfärgen vars minnesadress är 53280 (D020 i hex), kan du skriva följande program (och notera indragningarna):

    *=$2000
            inc $D020
            brk

    Instruktionen inc ökar värdet med 1 på angiven minnesadress och brk avbryter programmet. Nu har du ett fungerande program. Öppna programmet genom att trycka på F5. Stäng fönstret som visar disassemblyn (Assembly Dump) och notera hur Vice visar en C128 med ditt program inläst i minnet. Om du vill kontrollera värdet av $D020, skriv:

    PRINT PEEK(53280)

    Notera värdet som står där, t.ex. 253. Starta ditt program genom att skriva:

    SYS 8192

    Och direkt därefter, tryck X följt av Enter för att lämna Commodore 128:ans maskinkodsmonitor. Eftersom programmet ökar värdet av 53280 med 1, borde borderfärgen nu vara ljusblå, eftersom ljusblå ligger som nästa färg efter ljusgrön. Och mycket riktigt, upprepa PRINT PEEK(53280) och konstatera att värdet har ökat med 1, till t.ex. 254.

    bild1

    Kör programmet igen genom att skriva SYS 8192 igen. Nu har vi ökat 53280 med 1 igen, vilket ger ljusgrå borderfärg. Om du vill kan du titta på disassemblyn innan du lämnar monitorn genom att skriva D2000 och sedan X.

    bild2

    När du är färdig kan du stänga emulatorn (eller emulatorerna) – en ny öppnas varje gång du kör programmet.

  • Lorenz-attraktionen

    Lorenz-attraktionen

    Läs gärna min text om Loernz-attraktionen, med tillhörande PowerShell-implementation, på Humanistbloggen!

    Uppdatering 2015-01-02: Jonas Elfström har översatt PowerShell-koden till Swift.

  • Färgpaletten i Acorn Electron

    Acorn Electron kan förvisso visa 16 olika färger, men kan bara visa 8 olika nyanser. Dessa unika nyanser ligger på position 0 till 7 på färgpaletten.

    0 – Svart
    1 – Röd
    2 – Grön
    3 – Gul
    4 – Blå
    5 – Lila
    6 – Turkos
    7 – Vit

    De resterande färgerna återanvänder dessa nyanser i blinkande form.

    8 – Blinkande svart/vit
    9 – Blinkande röd/turkos
    10 – Blinkande grön/lila
    11 – Blinkande gul/blå
    12 – Blinkande blå/gul
    13 – Blinkande lila/grön
    14 – Blinkande turkos/röd
    15 – Blinkande vit/svart

    För att se hur blinkande svart/vit ser ut bredvid blinkande vit/svart, testa gärna detta program:

    10 MODE 1
    20 VDU 19, 0, 0, 0, 0, 0
    30 VDU 19, 1, 8, 0, 0, 0
    40 VDU 19, 2, 15, 0, 0, 0
    50 GCOL 0, 1
    60 MOVE 50, 50
    70 DRAW 100, 50
    80 GCOL 0, 2
    90 MOVE 60, 60
    100 DRAW 110, 60

    Rad 10 anger 320 * 256 pixlar med fyra färger. Rad 20 anger att den virtuella färgen 0 betyder svart. Rad 30 anger att den virtuella färgen 1 betyder blinkande svart/vit. Rad 40 anger att den virtuella färgen 2 betyder blinkande vit/svart. Därefter (rad 50-70) ritas en kort horisontell linje i undre vänstra hörnet med färg 1 (blinkande svart/vit). Sist (rad 80-100) ritas en till kort horisontell linje strax ovanför till höger. Detta ger illusionen att linjen hoppar fram och tillbaka, eftersom när den ena är vit är den andra svart.

    Den första nollan i GCOL (rad 50 eller rad 80) betyder att pixlarna ska ritas, men genom att sätta andra värden där, kan man använda logiska operationer på pixlarna, som t.ex. OR, AND, EOR eller INVERT. Den andra siffran i GCOL är val av logisk färg.

    För att få bort de blinkande linjerna, rensa grafikminnet med kommandot CLG.

  • Grafiklägen i Acorn Electron

    Vissa av er kanske minns Acorn Electron från 1983. På den tiden var PC-datorer både relativt dyra och oförmögna att visa grafik. Företaget Acorn hade en billigare maskin i sortementet, Electron, som kunde avsätta 20 kb för grafik, vilket kunde innebära monokrom grafik 640 * 256 pixlar, eller så mycket som 16 färger i lågupplöst läge (160 * 256 pixlar).

    Acorn Electron  - Foto: Oscar Orallo
    Acorn Electron – Foto: Oscar Orallo

    Den som vill programmera sin Electron kan använda den inbyggda BBC Basic-tolken, där kommandot MODE 0 ger monokrom högupplöst grafik (första bilden) och MODE 2 ger lågupplöst grafik med 16 färger (andra bilden). Den som förväntar sig sexton olika nyanser kommer att bli besviken, och bör välja ett läge med färre färger – Electron kan bara visa 8 nyanser.

    bild1

    bild2

    För den som inte är beredd offra tjugotusen bytes för grafik kan använda sin Electron i text only-läge (40 * 25 tecken) för endast 8 k genom att skriva MODE 6. Givet att du väljer läge 2 så kan du använda alla 16 färger, vilket tyvärr handlar om svart (0) röd (1), grön, gul, blå, lila, turkos och vit (7), samt blinkande svart/vit (8), röd/cyan (9), grön/lila, gul/blå och deras omvända (12-15). Om du inte tror mig, bör du köra detta program som använder 320 * 256 pixlar med 4 färger (läge 1):

    10 MODE 1
    20 VDU 19, 0, 9
    30 PRINT "NU BLINKAR ROD OCH CYAN"
    40 PRINT "TRYCK ENTER."
    50 INPUT A$
    60 VDU 19, 0, 14
    70 PRINT "NU BLINKAR CYAN OCH ROD."

    Inte nog med att detta program illustrerar det bisarra med att avsätta minne för bit-kombinationer som egentligen inte är speciellt unika, programmet belyser dessutom något som ser ut som en bugg.

    Kommandot PRINT “TJOHEJ” ger utdatat TJOHEJ, såvida det inte föregås av VDU 19 – då tappar Electron bort tre bytes och ger HEJ, vilket illustreras av följande program, som förutom en massa vackra färger ger TJOHEJ och HEJ som utdata:

    10 PRINT "TJOHEJ"
    20 VDU 19, 0, 2
    30 PRINT "TJOHEJ"

    bild3

    Detta beror på att även om du inte behöver fler parametrar, lyssnar VDU 19 efter ytterligare tre bytes, och tar vad den kan få. Man löser problemet t.ex. genom att fylla upp med nollor.

    10 PRINT "TJOHEJ"
    20 VDU 19, 0, 2, 0, 0, 0
    30 PRINT "TJOHEJ"

    Ovanstående kod ger verkligen TJOHEJ följt av TJOHEJ.

  • Word 5.5 under DosBox

    DosBox är en DOS-emulator som fungerar under bl.a. Windows 8.1 som ger utmärkt stöd för att köra DOS-program i fönster eller i helskärmsläge (växla med Alt+Enter). Jag tror att det primära syftet är att kunna spela de gamla favoritspelen från 90-talet på sin moderna PC, men emulatorn öppnar även för andra tillämpningar.

    Jag laddade hem Microsoft Word 5.5 för DOS, och placerade den packade filen i en undermapp till mappen jag använder som hårdisk i DOS, i mitt fall C:\DosBox\Word\word.exe. Tänk på att DosBox ska vara startad när du ändrar i filsystemet.

    Med filen på plats kan man starta DosBox och göra mappen till sin C-disk genom följande kommando:

    MOUNT C C:\DosBox

    Sedan är det bara att skriva C: för att komma dit, och påbörja installationen. Installationen sker i två steg. Först ska Wd55_ben.exe köras, så att filerna packas upp. Därefter ska installationsprogrammet köras. Låt inte installationsprogrammet ändra i några inställningar eftersom DosBox redan styr upp allt som behöver styras upp.

    Efter installationen märkte jag att muspekaren inte reagerade som den skulle, men efter att ha avslutat Word och DosBox, och sedan startat dem igen, har jag inte märkt av det felet. Sen är det bara att flyga in i inställningarna och göra önskvärda inställningar, och börja skriva. Mycket nöje!

    För att hitta länken till installationsfilen Wd55_ben.exe, besök denna sida och sök reda på textstycket “If you just want to get your free Word for DOS, click this link and the almighty Microsoft download deity will cause it to appear on your PC”.

  • EaseUS Partition Master

    If a hard disk partition suddenly disappears and shows up in Disk Manager as unallocated space, the program EaseUS Partition Master solve the problem. The program can repair the partition and assign a drive letter to it, so that your files become available again.

    The program is free to use, but the installer would like to put a lot of junk in your computer. Also, the program will remain running after use, so remember to uninstall from the control panel when the program is no longer needed.

  • Installera GitHub på Windows

    Dessa steg installerar GitHub på Windows 8.1, och sjösätter ett projekt.

    1. Ladda hem GitHub för Windows.

    2. Installera genom att köra GitHubSetup.exe.

    3. Under tiden GitHub installerar, passa på att skaffa ett konto på github.com/join. När kontot är registrerat, måste det aktiveras.

    4. När installationen är klar, logga in i klientapplikationen.

    5. Ett repository motsvarar ett projekt. För att skapa ett repository, klicka på symbolen + i klientapplikationens dashboard, högst uppe till vänster. Ge projektet ett lämpligt namn, och välj var du vill ha källkodsfilerna lagrade. Därefter, klicka Create repository.

    6. För att börja arbeta mot GitHub, skapa ett nytt Visual Studio-projekt inuti mappen som utgör ditt repository, alternativt flytta in ett existerande Visual Studio-projekt i mappen som utgör ditt repository.

    7. GitHub reagerar genom att presentera texten Uncommitted changes. För att skicka filerna till GitHub, klicka Show bredvid texten Uncommited changes, fyll i fältet Summary och om så önskas även Description, och klicka Commit to master.

    8. Publicera projektet genom att klicka Publish Repository längst uppe till höger, och sedan Publish [namn på repository].

    9. Slutligen ska de övriga programmerarna anges. Öppna projektets hemsida genom att högerklicka på repositoryts namn till vänster, och välj View on GitHub.

    10. På projektets hemsida klicka + uppe till höger och välj New collaborator. Fyll en annan programmerares användarnamn och klicka Add collaborator. Upprepa så många gånger som behövs.

    Projektet är nu tillgängligt för de som pekats ut som collaborators.

  • SQL 2012 Dependency Browser

    SQL 2012 Dependency Browser is a simple application for viewing what database objects that depends on a specific object, for example what tables that are used by a view.

    Setup program for version 1.0 with build date 2014-11-24 can be downloaded using this link. Click here to download the executable only.

    SQL 2012 Dependency Browser

  • Anropa en web service från PowerShell

    För att sätta en servicereferens inne i Visual Studio måste man (numera) följa dessa steg:

    1. Högerklicka på References i Solution Explorer, välj Add Service Reference.

    2. Klicka på knappen Advanced.

    3. Klicka på knappen Add Web Reference.

    4. Ange sin URL, typiskt en asmx-fil i Microsoft-världen, och klicka Add Reference.

    Som exempel finns en publik web service som konverterar mellan Celsius och Fahrenheit (och vise versa) på följande URL: http://www.w3schools.com/webservices/tempconvert.asmx

    Därefter genereras en proxy, som kan användas inifrån projektet, t.ex. så här (i C#):

    using (var x = new com.w3schools.www.TempConvert())
    {
       Console.WriteLine(x.CelsiusToFahrenheit("20"));
    }

    I PowerShell har vi inte samma möjlighet att generera en proxy via att använda ett grafiskt användargränssnitt, men vi kan antingen utveckla en DLL-fil i C# och använda den från PowerShell, eller använda en CmdLet som heter New-WebServiceProxy. Detta skapar en proxy, och lagrar resultatet i $FtoC:

    $FtoC = New-WebServiceProxy -Uri http://www.w3schools.com/webservices/tempconvert.asmx

    Sedan är det bara att anropa:

    $FtoC.CelsiusToFahrenheit("20")

    Förhoppningsvis borde både PowerShell och C# vara överens om att 20 grader C är lika med 68 grader F.

  • Simple full screen support in Windows Forms

    These simple steps lets you create a Windows Forms application with full screen support. I have changed the DoubleBuffered property to true for flicker free redrawing, StartPosition to Manual to be able to manage startup position in code and KeyPreview to true.

    This code in the Load event handler will position the window and restore the fullscreen state.

    var s = this.GetScreen();
    this.Top = s.WorkingArea.Top + 40; this.Left = s.WorkingArea.Left + 40;
    this.Width = s.WorkingArea.Width - 80; this.Height =
                                                 s.WorkingArea.Height - 80;
    if ((Application.UserAppDataRegistry.GetValue("Fullscreen",
                                             "false") as string) == "true")
       this.ToggleFullscreen();

    This is the KeyDown event handler that will enable the user to press F11 or Alt+Enter to toggle fullscreen.

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
       if(((e.KeyCode == Keys.Enter) && e.Alt) || (e.KeyCode == Keys.F11))
          this.ToggleFullscreen();
    }

    The FormClosing event handler stores the screen state:

    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
       if (this.FormBorderStyle ==
                            System.Windows.Forms.FormBorderStyle.Sizable)
          Application.UserAppDataRegistry.SetValue("Fullscreen", "false");
       else
          Application.UserAppDataRegistry.SetValue("Fullscreen", "true");
    }

    And here are the GetScreen and ToggleFullscreen functions:

    private Screen GetScreen()
    {
       var s = Screen.FromPoint(new Point((int)(this.Left + (this.Width / 2)),
                                        (int)(this.Top + (this.Height / 2))));
       return s ?? Screen.PrimaryScreen;
    }
    
    private void ToggleFullscreen()
    {
       var s = this.GetScreen();
       if (this.FormBorderStyle ==
           System.Windows.Forms.FormBorderStyle.Sizable)
       {
          this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
          this.Top = s.Bounds.Top;
          this.Left = s.Bounds.Left;
          this.Width = s.Bounds.Width;
          this.Height = s.Bounds.Height;
       }
       else
       {
          this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
          this.Top = s.WorkingArea.Top + 40; this.Left =
             s.WorkingArea.Left + 40;
          this.Width = s.WorkingArea.Width - 80; this.Height =
             s.WorkingArea.Height - 80;
       }
     }

    Finally, the Resize event handler should invalidate the form, and the Paint event handler should contain the drawing instructions.

  • Vinylbekännelser 2012

    Mellan januari och mars 2012 släppte Björn Johansson och jag tio avsnitt av musikpodden Vinylbekännelser. Ljudet är aningen överstyrt, och om vi återkommer med en ny säsong (vilket inte är omöjligt), så lovar vi att hålla en jämn och bra ljudkvalité. Jag hoppas att du upptäcker något nytt! Här finns en spellista med alla avsnitt (i omvänd ordning). Och här följer en lista över avsnitten från första säsongen:

    Avsnitt 1 med Patsy Cline, Crossroad Jam, Alcazar och Björn Afzelius.

    Avsnitt 2 med Karin Glenmark, Electric Banana Band, Roger Waters och George Michael.

    Avsnitt 3 med Leo Sayer, Slade, Bros och Mireille Mathieu.

    Avsnitt 4 med Anne-Marie David, Evan, Nova och Tose Proeski.

    Avsnitt 5 med Pat Benatar, Patsy Cline, Boyzone och Pet Shop Boys.

    Avsnitt 6 med Matt Monro, Ann Blyth & Vic Damone, Vaya Con Dios och Jellyfish.

    Avsnitt 7 med Björn Skifs, Breathe, Janne Schaffer och Adolphson & Falk.

    Avsnitt 8 med Dive, Tomas Dileva, Martika och Double Fantasy.

    Avsnitt 9 med Kikki Danielsson, Mia Martini & Roberto Murolo, Planet P Project och The Knife.

    Avsnitt 10 med Kim Wilde, Karin Glenmark, The Rescuers och Louis Armstrong.

  • Tre podcasts!

    Det är ju dags för den årliga omröstningen till Podradiopriset.  Själv har jag givetvis lagt en röst på Radio Houdi som jag gör tillsammans med John Houdi. Jag har även röstat på Kvack, en skeptisk podcast som bl.a. görs av min vän David Björnfot. Men så måste jag nämna kategorin musik, där Commoflage är nominerad. Commoflage är en fenomenal podcast av Henrik Andersson som främst spelar remixad C64-musik.

  • 10 excellent fifties / sixties covers

    1. Phil Collins – A Groovy Kind of Love

    This song was originally performed by Diane & Annita in the mid sixties, but Collins turned it to a huge hit in the eighties. I did not appreciate this back in the days, but now this song really takes me back to my teens.

    2. Freddie Mercury – The Great Pretender

    A shameless exploitation of this mid fifties song by The Platters, recorded just to show off Freddie Mercury´s excellent voice.

    3. David Lee Roth – Just a gigolo

    Extra points for all the references to other eighties videos. The song origins from a early 1900 piece by Roger A. Graham and Spencer Williams (“I Ain’t Got Nobody”) and were made famous by Louis Prima in the 1950’s.

    4. Whitesnake – Day Tripper

    The only thing you can depend on with Whitesnake, David Coverdale’s excellent vocals. At the beginning of their careers they did a cover of The Beatles Day Tripper (1966).

    5. Fine Young Cannibals -Suspicious Minds

    I really love the Fine Young Cannibals, but the original version (Elvis Presley 1969) gives tough competition.

    6. Motley Crue – Helter Skelter

    Originally performed by The Beatles in 1968, recorded by Motley Crue on the Shout at the Devil album from 1983.

    7. Yes – Every Little Thing

    Original by The Beatles (1964), covered by Yes in 1969. Great musical efforts!

    8. Twisted Sister – Leader of the Pack

    An excellent song by the very beautiful and talented Ellie Greenwich, first performed by The Shangri-Las in 1964.

    9. Phil Collins – You Can’t Hurry Love

    Excellent cover version of a classic Supremes song. Collins provides a very loving kind of satire in this version, quite similar to what you find in the Genesis song Jesus He Knows Me.

    10. Michael Jackson – Come Together

    This is the only Michael Jackson song that I genuinely like, probably because of my relation to the original song by The Beatles from 1969.

  • Sprite collision detection

    The Commodore 128 has built in sprite collision detection, but since Commodore Basic 7.0 is so slow, it must be used with some care. To demonstrate this, I have used SPRDEF to create two masterpieces. This is sprite 1:

    s1

    And this is sprite 2, this is excellent:

    s2

    I want to demonstrate the level of detail of the collision detection by turning both sprites on at an overlapping position, but not and colliding position, and then read out the collision flags. If you are doing this on your own Commodore 128, you might want to adjust the positions if your sprites are not identical to mine.

    s3

    The image shows that the sprites share an overlapping position, but they do not collide. There are no overlapping pixels. Yet, the BUMP(1) function will return 3 in this situation. The value 3 means that flag 1 and 2 are turned on, indicating that sprite 1 and  2 are colliding, even though they just share position. However, the size of the sprite is determined by the smallest rectangle that include all pixels of the sprite, which is second best after pixel perfect collision detection.

    Game programming in Commodore Basic 7 is futile. Despite this cheating, the sprites moves faster than the Basic interpreter can handle. To see this, you can make the sprites move towards each other, and their positions if they collide. You should see that not all collisions are acted upon.

    s4

  • I will vote for me

    We are about a month away from the election to the Swedish parliament, and as a liberal I was planning to go with Sweden’s only liberal party, The Pirate Party. However, since their current support is way under the required 4% and because of their resent support for the socialist idea of “citizen salary”, I am voting for myself. Anyone who gets two votes or more, must according to Swedish law be counted and accounted. So if my name shows up on the official election site, there is another one out there who has voted for me! 🙂

  • 5 pre-femenist videos

    Some good songs with videos made before the feminist revolution.

    5. Scorpions – Rhythm Of Love

    4. Kiss – Take it off

    3. Twinkle – Hello Hello

    2. Alice Cooper – Poison

    1. Günther – Ding Dong Song

  • Max objektorienterar

    Se så enkelt det kan bli! Läs detta av Christian Engvall!

    Max objektorienterar

  • Linjär interpolering

    Jag använder min Commodore 128 för detta exempel, eftersom jag vill komma åt den höga upplösningen i 80-kolumnsläget, men koden är körbar även på C64, Vic 20 och Pet, med reservation för radbryten och annat. Jag använder alltså en skärm som rymmer 80 tecken per rad, och kan representera talen 0 till och med 80 grafiskt med ett tecken. Denna kod kan representera talen 1 till 80 och här får 70 minustecken representera talet 70:

    När helst ett värde måste skalas om, kan linjär interpolering användas. Låt säga att du vill presentera värden mellan, säg 50 och 550 under förutsättningen att du bara får plats med 80 tecken på en rad. Här laddar jag tio tal mellan 50 och 550 i vektorn A:

    Algoritmen för att interpolera ett värde måste veta vad som ska interpoleras, vilken skala värdet ligger på (min och max) och vilken skala som värdet ska presenteras i (min och max). Själva formeln ser ut så här: NyttVärde = NyMin + (Värde – GammalMin) * (NyMax – NyMin) / (GammalMax – GammalMin).

    Om vi sätter ihop nummerserien med presentationen får vi detta:

    Vilket ger:

    Om vi inte skriver ut det nya numret i klartext, utan använder iterationen från första bilden, enligt följande:

    …så får vi detta resultat:

  • The song is better than the video (top 10)

    1. Bowie/Jagger are being spontaneous and not too HBTQ: “Dancing in the street

    2. Judas Priest is singing about committing a crime while committing a crime: “Breaking the law

    3. Europe walks into a bar, the bar plays Europe: “Rock the night

    4. Laura Branigan is dancing to one song and singing another, beat that! “Gloria

    5. Ignore the monks and everything else you see, and the song is excellent. Pet Shop Boys: “It’s a sin

    6. Male porn star (?) is digging Barbi Benton. “Ain’t that just the way

    7. What. Is. This. Shit??? Queen: “Crazy little thing called love

    8. In and out of different shades of pink. An aluminum foil. Twinkle: “Hello

    9. Journey without instruments, jump-cut to Journey with instruments, followed by various mimics. “Separate ways

    10. An excellent song, illustrated with unexpected camera mounts.  Huey Lewis and the News: “Hip to be square

  • Actual fullscreen text-console on Windows 7/8

    You can’t put the old CMD.EXE in fullscreen anymore, but there is replacement called Console2 that has some cool features. Do not set Console2 as your standard console in Windows you use shortcuts like the Visual Studio Command Prompt, because the different behaviors will set Console2 in an infinite loop.

    When installed, you can open the Settings window to tweak appearance, like hide the scrollbars and such. When done, you toggle fullscreen mode using the F11 key. This is what you get:

    fullscreen

    Update 2014-08-23: The latest version with the fullscreen option is 1.8.0. I am hosting it here.

  • Pekare

    En pekare håller reda på ett objekts plats i minnet. Otypade pekare deklareras med typen Pointer, medan typade pekare deklareras med inledande caret (^) vid typnamnet. I detta exempel är a en integer och b en integer-pekare.

    var
      a : integer;
      b : ^integer;

    Värdet som lagras i en pekare är minnesadressen till ett objekt. Variabeln b kan alltså hålla minnesadressen till en integer. För att få minnesadressen till ett objekt, används @. Detta exempel tilldelar adressen av a till b.

    a := 10;
    b := @a;

    Om man försöker läsa av b, får man adressen till a. Syftet med att läsa av b kan t.ex. vara att låta en ny pekare peka på a. Om man istället vill läsa av värdet som finns i variabeln som b pekar på, används caret som postfix. Detta ger svaret 10, eftersom b hänvisar till a som innehåller värdet 10.

    writeln(b^);

    Om ett värde tilldelas till b^ lagras det i a, eftersom b pekar på a.

    b^ := 50;
    writeln(a);

    Funktionspekare benämns procedural types. Variabeln måste vara deklarerad med exakt samma signatur (parametrar och retur) som funktionen den pekar på. Följande exempel använder funktionen sum i en unit som heter Unit1.

    unit Unit1;
    
    interface
    
      function sum(a, b: integer): integer;
    
    implementation
    
    function sum(a, b: integer): integer;
    begin
      result := a + b;
    end;
    
    end.

    En pekare som kan peka på funktionen sum måste ta två integers och ge en integer. Detta exempel deklarerar en kompatibel pekare (s), samt anropar funktionen sum direkt, och via pekaren.

    var
      s : function(x, y : integer) : integer;
    begin
      writeln(sum(10, 20));
      s := sum;
      writeln(s(20, 30));

    För tydlighetens skull, kan man uttryckligen skriva att man läser av adressen till sum, genom att använda prefixet @.

    s := @sum;

    Om funktionen sum istället vore en medlemsfunktion, behöver pekaren både hålla adressen till objektet och till medlemsfunktionen. Denna förändring i Unit1 placerar funktionen sum i klassen test.

    unit Unit1;
    
    interface
    
    type
      test = class
      public
        function sum(a, b: integer): integer;
      end;
    
    implementation
    
    { test }
    
    function test.sum(a, b: integer): integer;
    begin
      result := a + b;
    end;
    
    end.

    Nu deklareras pekaren med tillägget of object.

    var
      t : test;
      s : function(x, y : integer) : integer of object;
    begin
      writeln(t.sum(10, 20));
      s := t.sum;
      writeln(s(20, 30));

    För att skicka en funktion som parameter, måste funktionen deklareras som en typ. Så här ser en typ som beskriver funktionen sum ut:

    type
      sumtype = function(a, b : integer): integer of object;

    Med denna typ på plats, kan deklarationen av pekaren kortas ner från detta:

    s : function(x, y : integer) : integer of object;

    …till detta:

    s : sumtype;

    Typen sumtype kan användas som parametertyp, så att funktionen sum kan skickas som parameter.

    function call_sum(x: sumtype): integer;
    begin
      result := x(10, 20) + x(30, 40);
    end;

    Ett anrop kan se ut så här:

    var
      t : test;
      s : sumtype;
    begin
      s := t.sum;
      writeln(call_sum(s));
  • Hur producerar man en podcast?

    Med anledning av frågor jag har fått, tänkte jag skriva lite om hur man sätter ihop en podcast. Jag började podda 2010, och har utvecklat en viss kännedom om vad som krävs för att sätta ihop en podcast, hur man går tillväga. Det ställs lite olika krav på utrustning beroende på om man spelar in på distans eller om man faktiskt rent fysiskt åker till varandra och spelar in. Jag har varit med om båda, men att spela in på distans är enklare att arrangera och kräver mindre teknisk utrustning. För att spela in på distans, krävs att varje deltagare har en dator med internetanslutning, en mikrofon, mjukvara för inspelning och mjukvara för kommunikation. Själva förfarandet går ut på att deltagarna pratar med varandra över Internet, och spelar in sig själv. Jag själv använder Windows, men det finns motsvarande lösningar som fungerar på Macintosh.

    Mjukvara för inspelning
    Först och främst ska mikrofonen kopplas in i datorn och aktiveras. Väl inkopplad, visas den i listan över inspelningsenheter (högerklicka på ljudsymbolen nere i högra hörnet och välj inspelningsenheter). I listan över inspelningsenheter, högerklicka på din mikrofon och välj att ange den som standardenhet för inspelning. Det finns ett stort utbud av programvara för att spela in sin röst, och valet är inte speciellt viktigt, eftersom inspelningsfunktionen egentligen sitter i Windows, inte i inspelningsprogrammet. Det gäller bara att ha ett program som exponerar den funktionen. Det är viktigt att göra en testinspelning för att se att inspelningsnivån är korrekt inställd. Om man spelar in med för låg signal blir brusnivån hög, om man spelar in med för hög signal blir ljudet förvrängt (distortion). Lite mer avancerade program kan ha en VU-mätare, så att du kan se hur stark signalen är, vilket underlättar enormt när nivån ska sättas. För att sätta inspelningsvolymen, ta fram listan över inspelningsenheter, högerklicka på mikrofonen och välj egenskaper, växla till fliken Nivåer och använd reglaget för att ställa in önskad nivå. Klicka på Verkställ för att förändringen ska slå igenom.

    Mikrofon
    Det finns digitala och analoga mikrofoner. De digitala är aningen dyrare och ansluts via USB. De analoga är aningen billigare och ansluts i datorns anslutning för mikrofon. De analoga kan behöva adapteras till 3,5 mm telejack. De digitala mikrofonerna är att föredra, eftersom de ställer mycket mindre krav på din utrustning. De sköter AD-omvandlingen internt och ger ifrån sig en digital signal som varken påverkas till det bättre eller det sämre av att ljudkortet är dåligt. Om man har en analog mikrofon ställs mycket större krav på att ljudkortet ska vara bra och att man inte har något jordfel.

    Inspelning
    När alla deltagare har anslutit sig till varandra (t.ex. via Skype) kan inspelningen börja. Det är väldigt viktigt att alla startar inspelningen ungefär samtidigt och att man spelar in en synkroniseringssignal (t.ex. kan alla säga piiiiiiip samtidigt), annars blir det svårt att mixa ner det färdiga resultatet. Det är bra att spela in med låg inspelningsnivå och att prata nära mikrofonen, eftersom det minskar risken för att ljudet man har omkring sig ska fångas upp. Dessutom minskar det risken att studsat ljud, rumseko, fångas upp. Men man bör verkligen inte andas in i mikrofonen, och om möjligt bör man använda ett puffskydd.

    Skulle man spela in alla tillsammans, måste alla ha var sin analog mikrofon som är kopplad till en mixer, som antingen digitaliserar signalen och skickar den till datorns USB-ingång, eller skickar den analoga signalen vidare till datorns ljudingång på ljudkortet. Detta ställer höga krav på inspelningsnivåer och miljö. Att sitta i ett rum och prata i samma mikrofon går också, men det ger ett mycket sämre resultat eftersom mikrofonen då kommer att plocka upp alla biljud.

    Mastering och redigering
    När väl inspelningen är klar, har vi lika många ljudfiler som deltagare i podden. Dessa ska levereras till personen som ska redigera. På varje fil ska allt som spelades in före synkroniseringskanalen klippas bort, så att synkroniseringen blir korrekt vid nedmixning. Varje fil kan behöva putsas lite på, t.ex. brusreduceras, nivåutjämnas eller annan justering. Nivåerna i varje fil bör justeras så att alla pratar lika starkt. När filerna är mastrade, mixar man ner dem till ett enda ljudspår (eftersom mono är lämpligt att använda i podcasts), nivåutjämnar och klipper. Nivåutjämningen syftar till att få utrop och stillsamt prat att återges i ungefär samma ljudstyrka.

    Det finns en del fällor att man kan fastna i, och vilka dessa är beror på vilken mjukvara man använder sig av. Vissa program, t.ex. Skype, vill automatiskt justera inspelningsvolymen på mikrofonen. Denna inställning är lämplig för telefonsamtal, men helt olämplig för inspelning.

    För min del mastrar och redigerar jag i Sony SoundForge för Windows. Den har stöd för kompressor, nivåutjämning, och har bra funktioner för redigering. Det är ett ganska dyrt program, och det finns alternativ som är mycket billigare eller helt gratis, men när man hållit på ett tag är man tacksam för tillgången till en del kraftfulla funktioner. Oavsett vilket program man väljer, så har både mastering och redigering en inlärningskurva. Man måste träna.

    Publicering
    När redigeringen är färdig ska filen sparas i ett snålkodat format, t.ex. MP3 96 kbit, och laddas upp till webhotellet, och sedan marknadsföras. T.ex. kanske man vill publicera på iTunes och på sin blogg. Det finns en del gratisalternativ till att använda webbhotell, men de har sina nackdelar. Om filen publiceras på YouTube kan inte alla mobila enheter spela upp filen och den kan inte göras tillgänglig via iTunes.

  • Tester och iterationer

    If

    If-satser innehåller endast ett semikolon efter att hela satsen är slut. Detta exempel visar en test som alltid utvärderar till false:

      if false then
        writeln('Sant');

    Semikolon ska förekomma efter sista satsen. Detta exempel visar en if-sats med en else-sats:

      if false then
        writeln('Sant')
      else
        writeln('Falskt');

    Och detta visar två stycken else if-satser:

      if false then
        writeln('Sant')
      else if false then
        writeln('Annars falskt')
      else if true then
        writeln('Annars sant')
      else
        writeln('Falskt');

    Om fler än en rad ska exekvera när ett uttryck utvärderas till true, ska dessa kapslas in mellan begin och end.

      if false then
        writeln('Sant')
      else if true then
        begin
          writeln('Rad 1');
          writeln('Rad 2');
        end
      else if false then
        writeln('...')
      else
        writeln('Falskt');

    Om begin … end-blocket hade legat sist, hade end avslutats med semikolon.

    Case

    I ett case-block används alltid semikolon efter satserna, och således även semikolon efter end om man vill skapa ett block. Det finns ingen fall through, så delarna i case-blocket behöver inget kommando för att avbryta exekvering.

      i := 3;
      case i of
        1:
          writeln('1');
        2:
          writeln('2');
        3:
          begin
            writeln('3 a');
            writeln('3 b');
          end;
      end;

    For

    Den grundläggande syntaxen för for påverkar endast en rad:

      for i := 0 to 9 do
        writeln(i);

    För att flera rader ska ingå i iterationen används begin … end med semikolon.

      for i := 0 to 9 do
      begin
        writeln(i);
        writeln(i);
      end;

    Efter att uppräkningen gjorts har i värdet 10. Det går även att använda t.ex. enumereringar i for, vilket illustreras här:

    uses
      System.SysUtils, TypInfo;
    type
      Räkneord = (Första, Andra, Tredje, Fjärde);
    var
      i : Räkneord;
    begin
      for i := Första to Fjärde do
        writeln(GetEnumName(TypeInfo(Räkneord), integer(i)));

    Repeat

    För 1-till-flera används repeat. Iterationen pågår tills uttrycket efter until utvärderas till true.

      i := 1;
      repeat
        writeln(i);
        inc(i);
      until i >= 10;

    While

    För 0-till-flera används while. Till skillnad från repeat så gäller while bara för efterföljande rad såvida inte ett begin … end-block skapas.

      i := 1;
      while i < 10 do
      begin
        writeln(i);
        inc(i);
      end;

    Både for, repeat och while kan avbrytas med break.

  • Introduktion till Delphi XE5

    Språket Delphi är inte skiftlägeskänsligt för nyckelord eller namn. Kompilatorn kontrollerar att konstanta värden får plats i variabeln de tilldelas till, men under körning kontrolleras inte överflöden.

    Viktiga snabbtangenter:

    • F5: Breakpoint
    • F9: Kompilera och starta (Shift+Ctrl+F9 startar utan debugging)
    • Ctrl+F9: Kompilera
    • Ctrl+Shit+C: Implementera alla funktioner

    Unit

    En källkodsfil har filändelsen .pas och benämns som en unit (enhet). En enhet inleds med nyckelordet unit och avslutas med end. (inklusive punkten). Den minimala strukturen ser ut så här:

    unit MinUnit;
    interface
    implementation
    end.

    I sektionen interface anges vilka andra enheter som enheten använder.

    interface
    uses
      MinAndraUnit;

    Dessutom deklareras typer (nyckelordet type) och globala variabler i interface. Sektionen implementation används för att implementera funktioner.

    Datatyper

    Numeriska datatyper:

    • Byte: 0 – 255
    • ShortInt: -127 – 127
    • Word: 16 bit osignerad (0 – 65535)
    • SmallInt: 16 bit signerad (-32768 – 32767)
    • LongWord (eller Cardinal): 32 bit osignerad
    • LongInt (eller Integer): 32 bit signerad
    • Int64: 64 bit signerad
    • Currency: 50 tecken, fyra decimaler

    Flyttal:

    • Single: 7 tecken
    • Double: 15 tecken
    • Extended: 19 tecken

    Konstanter skapas utan att någon datatyp behöver anges. Notera att = vid deklaration av konstanter. Följande exempel skapar en ShortInt (A), en SmallInt (B) och en Integer (C):

    const
      A = 30;
      B = 300;
      C = 300000;

    Text:

    • Char (eller AnsiChar): Ett enkelt tecken
    • WideChar: Ett internationellt tecken
    • ShortString: Upp till 255 Char
    • String (eller AnsiString): Fritt antal Char
    • WideString: Fritt antal WideChar

    Som strängavgränsare används apostrof (‘).

    var
       s : string;
    begin
       s := 'Conny Karlsson';
       writeln(copy(s, 1, 3)); //Första tecknet och tre framåt ger 'Con'
       insert('Helmer ', s, 7); //Infoga 'Helmer ' efter 'Conny '
       writeln(s); //'Conny Helmer Karlsson'
       //Byt ut Helmer till Holger.
       writeln(stringreplace(s, 'Helmer', 'Holger', [rfReplaceAll, rfIgnoreCase]));

    Operatörer

    Aritmetiska operatörer:

    • + (addition eller strängkonkatenering)
    • – (subtraktion)
    • * (multiplikation)
    • / (division)
    • div (heltalsdivision)
    • mod (modulus)

    Logiska operatörer:

    • and (logiskt och)
    • or (logiskt eller)
    • xor (logiskt exklusivt eller)
    • not (enkel logiskt inte)

    Jämförelseoperatörer:

    • = (lika med)
    • < och > (mindre än, större än)
    • <= och >= (mindre än eller lika med, större än eller lika med)
    • <> (inte lika med)

    För tilldelning används := (kolon och lika med).

    Typomvandling

    För konvertering från tal till sträng används Str, CurrToStr, Format, IntToStr och IntToHex. Notera att Str tar en sträng som utparameter med resultatet istället för att ge resultatet i retur.

    var
      i : Integer;
      s : String;
      c : Currency;
    begin
      //Konvertera Integer till String med s som utparameter:
      i := 10;
      Str(i, s);
      //Konvertera Currentsy till String:
      c := 100;
      s := CurrToStr(c);

    För att konvertera från sträng till tal används StrToInt, StrToIntDef (feltolerant konvertering) och StrToFloat.

    var
      f : Extended;
      format : TFormatSettings;
    begin
      //Konvertera String till Integer (undantagsfel vid misslyckad konvertering):
      writeln(StrToInt('500'));
      //Konvertera String till Integer (0 vid misslyckad konvertering):
      writeln(StrToIntDef('1000', 0));
      //Konvertera String till Extended (undantagsfel vid misslyckad konvertering):
      format := TFormatSettings.create;
      format.DecimalSeparator := ',';
      writeln(StrToFloat('100,5', format));

    Typer

    Typer deklareras i interface-sektionen i en enhet. De inleds med type följt av namn och supertyp, och avslutas med end; (notera avslutande semikolon). Detta exempel visar två typer:

    type
      MinTyp = class(TObject)
      end;
    
    type
      MinAndraTyp = class(TObject)
      end;

    Supertypen TObject behöver inte anges, eftersom den antas gälla. Deklarationerna delas upp efter synlighet. Följande exempel utrustar typen Employee med två privata medlemsvariabler.

    type
      Employee = class
    private
      //Privata variabler
      _firstName : string;
      _lastName : string;
      end;

    Förutom synlighetsnivån private finns nivåerna protected, public och published. Nivån private anger att medlemmen är lokal för objektet, protected innebär att typer som ärver från typen har tillgång till medlemmen, public innebär att alla andra objekt i samma projekt har tillgång till medlemmen, och published innebär att medlemmen dessutom är tillgänglig för object inspectorn i utvecklingsmiljön.

    Funktioner

    Funktioner deklareras med nyckelordet function, följt av funktionens namn, parameterlista och returtyp enligt följande:

    function Namn(parameter: typ; parameter: typ) : returtyp;

    Om funktionen saknar retur, används istället nyckelordet procedure.

    procedure Namn(parameter: typ; parameter: typ)

    Parenteserna efter namnet är inte nödvändiga om funktionen eller proceduren inte tar några parametrar. Detta exempel visar funktionen getName:

    type
      Employee = class
      private
        //Privata variabler
        _firstName : string;
        _lastName : string;
        function getName : string;
      end;

    Implementationerna sker i sektionen implementation. Man använder typnamn.funktionsnamn för att ange vilken funktion som implementeras. Implementationen avgränsas med begin och end; (semikolon). Det är inte obligatoriskt att skicka tillbaka ett värde, trots att man deklarerat en returtyp. För att skicka tillbaka ett värde görs en tilldelning till result.

    function Employee.getName: string;
    begin
      result :=  (_firstName + ' ' + _lastName);
    end;

    Däremot är det inte tillåtet att läsa oinitierade strängar. Ett sätt att utrusta _firstName och _lastName med värden är att implementera en konstruktor. Dessa ska enligt standarden heta create, och kan överlagras för att erbjuda flera sätt att initiera objekten.

    public
      constructor create; overload;
      constructor create(firstName: string; lastName: string); overload;

    Konstruktorn måste anropas uttryckligen.

    e := Employee.create;

    Properties

    Properties är inte lika mångsidiga som i många andra språk. I Delphi anger man endast vilken medlemsvariabel som påverkas vid tilldelning till en property, och vilken som påverkas av avläsning. Syntaxen för en property är:

    property namn : typ read uttryck write uttryck;

    För att skapa en read only property utelämnas write uttryck, och för att skapa en read only property utelämnas write uttryck. Exempel:

    type
      Point = class
      private
        _x : integer;
        _y : integer;
      public
        property x : integer read _x write _x;
        property y : integer read _y write _y;
      end;

    Ett exempel

    Följande exempel visar deklarationen av en klass (sektionen interface) som representerar en person. Klassen innehåller privata variabler, en privat funktion, två konstruktorer, två read/write properties samt en read only property som använder en funktion som uttryck.

    type
      Employee = class
      private
        //Privata variabler
        _firstName : string;
        _lastName : string;
        function getName : string;
      public
        constructor create; overload;
        constructor create(firstName: string; lastName: string); overload;
        property firstName : string read _firstName write _firstName;
        property lastName : string read _lastName write _lastName;
        property name : string read getName;
      end;

    Så här ser implementationerna ut (sektionen implementation):

    constructor Employee.create;
    begin
      _firstName := '';
      _lastName := '';
    end;
    
    constructor Employee.create(firstName, lastName: string);
    begin
      _firstName := firstName;
      _lastName := lastName;
    end;
    
    function Employee.getName: string;
      begin
      result :=  (_firstName + ' ' + _lastName);
    end;

    Här följer ett exempel:

    var
      e : Employee;
    begin
      e := Employee.create('Sven', 'Svensson');
      writeln(e.name);
  • Commodore 128 bitmap graphics 2/2

    Part 1. Part 2.

    On the Commodore 128, the screen is divided into characters.

    1

    Each character consists of eight eight-pixel lines (bytes), which make a total of 64 pixels per character.

    2

    Each pixel pair represent one of four available colors in multicolor mode. This can be demonstrated using this simple code:

    10 GRAPHIC 3, 1 : REM CREATE CLEAN MULTICOLOR DISPLAY
    20 DRAW 1, 0, 0 : REM DRAW USING COLOR 1
    30 DRAW 2, 0, 1 : REM DRAW USING COLOR 2
    40 DRAW 3, 0, 2 : REM DRAW USING COLOR 3

    Now, this pattern is shown:

    3

    If you add a line of code to change to high resolution graphics without clearing, we can see that two turned off pixels represent the first color, one off and one on represent color 2, one on and one off represent color 3 and two turned on pixels represent color 4.

    50 GRAPHIC 1, 0 : REM CHANGE TO HIGH RESOLUTION WITHOUT CLEARING

    4

    The graphics memory is movable. This command places the graphics memory at 2000 (8192 in decimal):

    POKE 53272, (PEEK(53272) AND 240) OR 8

    The eight pixel pattern of each row in a character can be described using binary 00000000 for all off to 11111111 for all on (0 to 255 in decimal). Placing the number 00011011 (27) will draw one multicolor pixel of each color on the first line. 00 is color 0 (background), 01 is color 1, 10 is color 2 and 11 is color 3.

    10 GRAPHICS 3, 1
    20 POKE 53272, (PEEK(53272) AND 240) OR 8
    30 POKE 8192, 27

    5

  • Commodore 128 bitmap graphics 1/2

    Part 1. Part 2.

    When Commodore 128 vector graphics can’t provide the desired details, there is an option to do bitmap graphics. The 128 have a built in command for creating pixel perfect graphics called SPRSAV. You can create a pattern using the built-in editor (SPRDEF)…

    01

    …and transfer the data into a string variable, and then copy that data to the screen, like so:

    10 COLOR 0, 13
    20 COLOR 4, 13
    30 COLOR 1, 1
    40 COLOR 2, 8
    50 COLOR 3, 12
    60 GRAPHIC 3, 1
    70 SPRSAV 1, A$
    80 GSHAPE A$, 10, 10, 2 : REM DRAW SPRITE AT 10, 10
    90 GSHAPE A$, 20, 11, 2
    100 GSHAPE A$, 30, 12, 2

    02

    This should be enough for anyone, but if you like, you can also access the graphics memory directly.

  • Commodore 128 vector graphics

    The Commodore 128 have a rich API for making colorful vector graphics. The low resolution (160×200) color mode allows you to make the classic “Forrest Gump” smiley using these five lines of code:

    10 COLOR 0, 13 : REM GREY BACKGROUND
    20 COLOR 4, 13 : REM GREY BORDER
    30 COLOR 1, 1 : REM COLOR 1 IS BLACK
    40 COLOR 2, 8 : REM COLOR 2 IS YELLOW
    50 COLOR 3, 12 : REM COLOR 3 IS SHADOW (DARK GREY)
    60 GRAPHIC 3, 1 : REM LOWRES MULTICOLOR MODE
    70 CIRCLE 3, 80, 101, 49, 99 : REM SHADOW OUTLINE
    80 PAINT 3, 80, 101 : REM SHADOW FILL
    90 CIRCLE 2, 97, 99, 49, 99 : REM SMILEY SIZE
    100 PAINT 2, 79, 99 : REM SMILEY FILL
    110 CIRCLE 1, 79, 99, 49, 99 : REM SMILEY CONTOUR
    120 CIRCLE 1, 65, 70, 8, 22 : REM LEFT EYE
    130 PAINT 1, 65, 70 : REM LEFT EYE COLOR
    140 CIRCLE 1, 92, 70, 8, 22 : REM RIGHT EYE
    150 PAINT 1, 92, 70 : REM RIGHT EYE COLOR
    160 CIRCLE 1, 79, 99, 39, 85, 90, 280 :REM MOUTH

    smile

    To exit the program on a real 128, press Runstop + Restore. On the Vice emulator, press Caps Lock + Page Up. However, the Commodore 128 API is not very suitable for bitmap graphics. For that, other Commodore Basic strategies are more suitable.

  • Teatral rock, top 20

    Tjugo oförglömliga tillskott till den teatrala rocken:

    20. Helix – The kids are all shakin’ (1985)

    19. Freddie Mercury – The great pretender (1987)

    18. Van Halen – Jump (1983)

    17. The Sparks – When do I get to sing My Way (1984)

    16. Styx – Mr. Roboto (1982)

    15. Genesis – Land of confusion (1986)

    14. Slade – Far far away (1974)

    13. Pink Floyd – The trial (1979)

    12. Twisted Sister – Under the blade (1982)

    11. Kiss – Sure know something (1979)

    10. Jellyfish – The king is half-undressed (1990)

    9. Queen – I want to break free (1984)

    8. Black Sabbath – Headless cross (1989)

    7. Motley Crue – Live wire (1982)

    6. Rainbow – Tarrot woman (1976)

    5. David Lee Roth – Just like paradise (1988)

    4. Alice Cooper – Poison (1989)

    3. Kiss – Unholy (1992)

    2. T-Rex – Get it on (1971)

    1. The Sparks – This town ain’t big enough for both of us (1974)

  • Action Biker

    Nu har jag stulit så mycket jag bara kan: Rob Hubbards komposition, Trevor Horns sound (efter bästa förmåga). Detta är en cover på soundtracket till Action Biker från 1985.

  • En gameloop med input i GLBasic

    Att komma igång och skapa indiegames för t.ex. Windows är inte speciellt svårt. Har man minimal programmeringskunskap kan man komma igång, och det du gjorde på din gamla Commodore 64 räknas som meriterande programmeringserfarenhet i detta fall.

    GLBasic är en kommersiell produkt, men det finns en gratisversion som tillåter utveckling av 2D-spel för bl.a. Windows. Styrkan med GLBasic är att kompilatorn kompilerar till flera plattformar, däribland GPX2 Wiz som är min favoritenhet för casual gaming. Den stora svagheten delar GLBasic med andra enklare utvecklingsmiljöer för spel: Språket är vansinnigt begränsat, så ett komplicerat spel innebär en komplicerad kod.

    Tänk på att ett GLBasic-projekt består av flera filer, och att du därför bör skapa en mapp åt ditt projekt. Projektet utgörs av en GBAP-fil – det är denna som du öppnar för att återkomma till ditt projekt. Exe-filen som produceras hamnar i en undermapp till den mapp som GBAP-filen ligger i.

    Skärmupplösningen sätts med kommandot SETSCREEN. Förutom bredd och höjd ska 1 anges för att indikera att spelet ska köras i fullskärmsläge. Därför anger jag 0 medan jag testar min kod, eftersom 0 anger att spelet körs i ett fönster.

    SETSCREEN 640, 480, 0

    En egenskap som underlättar utvecklandet av en gameloop är automatisk synkronisering. Istället för att behöva klocka dina beräkningar och pausa, kan du helt enkelt be GLBasic om ett önskat antal uppdateringar per sekund (FPS) så sköts detta genom magi. Jag upplever att 30 räcker, men 50 ger aningen mjukare rörelser. I fullscreen-spel bör du välja 60.

    LIMITFPS 50

    För att figurer som är irreguljärt formade använder GLBasic en mask. Denna genereras automatiskt utifrån en färg i bilden som används. Kommandot SETTRANSPARENCY anger önskad färg och färger anges med funktionen RGB. Nedanstående väljer lila färg som mask.

    SETTRANSPARENCY RGB(255, 0, 255)

    Innan vi läser någon media, kan det vara bra att peka ut var programmet kan leta efter filerna. Jag tänkte lägga lite grafik i en mapp som heter “Media” som ligger under exe-filens mapp. Därför anger jag den relativa sökvägen till Media.

    SETCURRENTDIR("Media")

    Nu behöver jag ha en bild som ska agera spelets karaktär. Jag skapar en BMP-fil på 48×48 punkter med Microsoft Paint. Det är viktigt att bilden ligger på lila botten, eftersom vi angav lila som maskfärg. Bilden sparar jag som “player.bmp” i mappen Media.

    sprite
    Efter denna fenomenala insats för den grafiska konsten, är det dags att ladda in bilden som en sprite. Förutom filnamn tillhandahåller jag även ett index för spriten. Det är ganska smart att i förväg deklarera vilka index man tänker använda sig av, så att man slipper hålla reda på en massa siffror, men för denna gång anger jag 0 som index.

    LOADSPRITE "player.bmp", 0

    När spelaren är laddad, vill jag deklarera att spelaren har en position. Procenttecknet (%) indikerar att variablerna x och y är 32-bitars heltal.

    TYPE Player
      x%
      y%
    ENDTYPE

    Därefter kan jag skapa en variabel av typen Player och initiera den. Jag vill placera spelaren långt ner på skärmen, i mitten. Den horisontella mittpunkten är lika med halva skärmens bredd minus halva spelarens bredd, alltså 320 – 24 = 296. Den tvåhundranittiosjätte pixelns x-koordinat är 295.

    GLOBAL plr AS Player
    plr.x% = 295
    plr.y% = 440

    Själva iterationen (loopen) ska käras till dess att användaren trycker på Escape. Escape har kod 1 och funktionen KEY testar om en tangent är nertryckt, alltså ser loopen ut så här:

    WHILE NOT KEY(1)
       //Resten av koden skrivs här!
    WEND

    Resten av koden skrivs mellan WHILE och WEND. Det som återstår är att skriva kod som lyssnar på användarens tangentbordstryckningar, kod som renderar spelplanen och en anrop till GLBasic som talar om att vi är färdiga med en frame, så att GLBasic kan visa och synkronisera. Låt oss börja med koden som flyttar spelaren efter användarens tangentbordstryckningar.

    //Låt användaren styra höger och vänster.
    IF KEY(205) THEN plr.x% = plr.x% + 2
    IF KEY(203) THEN plr.x% = plr.x% - 2

    Just GLBasic har ett verktyg i Tools-menyn som låter dig ta reda på vilken kod som är kopplad till vilken tangent. 205 betyder höger och 203 betyder vänster. Vi vill även förhindra att spelaren simmar ur bild.

    //Man får inte simma ur bild.
    IF plr.x% < 0 THEN plr.x% = 0
    IF plr.x% > 591 THEN plr.x% = 591

    Sen renderar vi spelplanen. I vårt fall handlar det om ett enda anrop på DRAWSPRITE (som tar index, x-position och y-position), men ett riktigt spel kommer att ha många anrop på DRAWSPRITE här.

    //Rendera spelplanen.
    DRAWSPRITE 0, plr.x%, plr.y%

    Och sist ber vi GLBasic sköta om presentationen av det ritade, samt synkroniseringen.

    //Lämna över till GLBasic att sköta resten.
    SHOWSCREEN

    Och där har vi en färdig gameloop! Detta är den färdiga koden:

    SETSCREEN 640, 480, 0
    LIMITFPS 50
    SETTRANSPARENCY RGB(255, 0, 255)
    SETCURRENTDIR("Media")
    LOADSPRITE "player.bmp", 0
    
    TYPE Player
    	x%
    	y%
    ENDTYPE
    
    GLOBAL plr AS Player
    plr.x% = 295
    plr.y% = 420
    
    WHILE NOT KEY(1)
    
    	//Låt användaren styra höger och vänster.
    	IF KEY(205) THEN plr.x% = plr.x% + 2
    	IF KEY(203) THEN plr.x% = plr.x% - 2
    
    	//Man får inte simma ur bild.
    	IF plr.x% < 0 THEN plr.x% = 0
     	IF plr.x% > 591 THEN plr.x% = 591
    
    	//Rendera spelplanen.
    	DRAWSPRITE 0, plr.x%, plr.y%
    
    	//Lämna över till GLBasic att sköta resten.
    	SHOWSCREEN
    
    WEND
  • God jul önskar John och Anders!

    God jul önskar John och Anders!

    En hälsning den humanskeptiska podcasten Radio Houdi. God jul!

  • Byt dörr!

    I det amerikanska tv-programmet “Let’s make a deal” fick en gäst, spelaren, välja en dörr av tre tillgängliga i förhoppning att vinna en bil. Spelaren vet inte vilken dörr som innehåller bilen, men programlendaren Monty Hall vet precis vart bilen finns. Efter att spelaren gjort sitt val, öppnar Hall en dörr som spelaren inte valt, och visar att denna är en nit. Alltså, om spelaren väljer t.ex. dörr två, öppnar Hall t.ex. dörr tre och visar att dörr tre är en nitlott.

    Nu frågar Hall om spelaren vill byta dörr eller inte. Om spelaren vill vinna bilen, ska han acceptera erbjudandet?

    Ja, eftersom första valet är gjort med en vinstsannolikhet på 1/3, men bytet görs med en vinstsannolikhet på 1/2.

    Alltså, om vi använder C# för att undersöka spelarens sannolikhet att vinna bilen om han inte byter dörr, kommer vi se att vi vinner bilen ungefär 333 333 gånger av 1 000 000, eftersom vi helt uppenbart har en chans av tre att pricka rätt.

    var rnd = new Random();
    var correctGuesses = 0;
    for (var i = 0; i < 1000000; i++)
    {
       var correctPosition = rnd.Next(1, 4); //Ger ett tal mellan 1 och 3.
       var playersGuess = rnd.Next(1, 4); //Ger ett tal mellan 1 och 3.
       if (correctPosition == playersGuess)
          correctGuesses++;
    }
    Console.WriteLine(correctGuesses.ToString("n0"));

    Spontant kan man tycka att om spelaren inte byter dörr efter att han fått se att en annan dörr är en nit, borde det ändå vara 50% chans att han redan valt en vinst, men det stämmer inte. Valet gjordes när chansen var 33% att välja rätt. Låt oss se vad som händer när spelaren byter dörr efter att ha fått se en nit.

    Vi kan konstatera att valet var rätt redan från början med 33% sannolikhet. Med 33% sannolikhet väljer alltså spelaren bort en vinst, men när vi kör exemplet ser vi att antalet korrekta gissningar landar på ungefär 666 666 av 1 000 000.

    var rnd = new Random();
    var correctGuesses = 0;
    for (var i = 0; i < 1000000; i++)
    {
       var correctPosition = rnd.Next(1, 4); //Ger ett tal mellan 1 och 3.
       var playersGuess = rnd.Next(1, 4); //Ger ett tal mellan 1 och 3.
       //Bestämm vilken dörr som Monty Hall ska visa för spelaren.
       var shownByHall = rnd.Next(1, 4);
       while (shownByHall == correctPosition || shownByHall == playersGuess)
          shownByHall = rnd.Next(1, 4);
       //Låt spelare välja en ny dörr, inte den han redan valt
       //och inte den han valde tidigare.
       var newGuess = rnd.Next(1, 4);
       while (newGuess == playersGuess || newGuess == shownByHall)
          newGuess = rnd.Next(1, 4);
       if (correctPosition == newGuess)
          correctGuesses++;
    }
    Console.WriteLine(correctGuesses.ToString("n0"));

    Detta avslöjar alltså att bytet ger en bil med 66% sannolikhet.

  • Evolutionary sudoku

    This is a successful attempt to use an evolutionary algorithm to generate a sudoku board. A complete board is created after usually less than a million iterations, sometimes after a couple of hundred generations.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
       class Program
       {
          static void Main(string[] args)
          {
             var s = new Sudoku();
             s.Display();
             Console.Write("Done. Press enter to quit.");
             Console.ReadLine();
          }
       }
    
       class Sudoku
       {
          int[,] field = new int[9,9];
          const int mutation_speed = 3;
          const int dead_end_limit = 60000;
    
          public Sudoku()
          {
             var r = new Random();
             //Create a list of possible characters on the game field.
             var l = new List<int>();
             for (var i = 1; i < 10; i++)
                for (var j = 1; j < 10; j++)
                   l.Add(i);
             //Place them on the field randomly.
             for (var i = 0; i < 9; i++)
                for (var j = 0; j < 9; j++) {
                   var index = r.Next(0, l.Count);
                   var value = l[index]; l.RemoveAt(index);
                   this.field[j, i] = value;
                }
             //Prepare for score counting. Low is better.
             int score = int.MaxValue, iterations = 0, generations = 0,
             iterations_since_last_climb = 0;
             //Define how a mutation works - a random element swap.
             Action<int[,]> mutate = a => {
                var speed = (score > 2 ? mutation_speed : 1);
                for (var i = 0; i < speed; i++) {
                   int x1 = r.Next(0, 9), y1 = r.Next(0, 9),
                     x2 = r.Next(0, 9), y2 = r.Next(0, 9);
                   var v = a[x1, y1]; a[x1, y1] = a[x2, y2]; a[x2, y2] = v;
                }
             };
             //Show progress.
             Action status = () => {
                Console.Title = string
                   .Format("Score: {0:00} - Generations: {1:000} - " +
                   "Iterations: {2:000000}" +
                   " - Since last climb: {3:00000}",
                   score, generations, iterations, iterations_since_last_climb);
             };
             //Let evolution work.
             do {
                iterations++;
                //If adaptation has stopped, the parent must mutate.
                if (iterations_since_last_climb >= dead_end_limit) {
                   mutate(this.field);
                   score = this.GetScore(this.field);
                }
                //Breed two new sudokos and modify them slightly.
                int[,] child1 = (int[,])this.field.Clone();
                int[,] child2 = (int[,])this.field.Clone();
                mutate(child1); mutate(child2);
                //Check new scores.
                var child1score = this.GetScore(child1);
                var child2score = this.GetScore(child2);
                //Keep the best one, if any.
                Action<int[,], int> keep = (a, s) => {
                   this.field = (int[,])a.Clone(); score = s; generations++;
                   iterations_since_last_climb = 0; };
                if (child1score < score)
                   keep(child1, child1score);
                else if (child2score < score)
                   keep(child2, child2score);
                else
                   iterations_since_last_climb++;
                System.Threading.Thread.Yield();
                if ((iterations % 1000) == 0)
                   status();
    #if DEBUG
                this.Display();
                Console.CursorTop = 0;
    #endif
             } while (score > 0);
             status();
          }
    
          private int GetScore(int[,] p)
          {
             //Define how fitness is determined. A better solution is wanted.
             Func<int[,], int, int, bool> checkrow = (arr, x, y) => {
                var val = arr[x, y]; var count = 0;
                for (var i = 0; i < 9; i++)
                   if (arr[i, y] == val) count++;
                return (count == 1);
             };
             Func<int[,], int, int, bool> checkcol = (arr, x, y) => {
                var val = arr[x, y]; var count = 0;
                for (var i = 0; i < 9; i++)
                   if (arr[x, i] == val) count++;
                return (count == 1);
             };
             Func<int[,], int, int, bool> checksection = (arr, x, y) => {
                var section_x = x; while (!((section_x % 3) == 0)) section_x--;
                var section_y = y; while (!((section_y % 3) == 0)) section_y--;
                var val = arr[x, y]; var count = 0;
                for (var row = section_x; row < section_x + 3; row++)
                   for (var col = section_y; col < section_y + 3; col++)
                      if (arr[col, row] == val) count++;
                return (count == 1);
             };
             var score = 0;
             for (var i = 0; i < 9; i++)
                for (var j = 0; j < 9; j++) {
                   if (!(checkrow(p, j, i))) score++;
                   if (!(checkcol(p, j, i))) score++;
                   if (!(checksection(p, j, i))) score++;
                }
             return score;
          }
    
          public void Display()
          {
             for (var y = 0; y < 9; y++) {
                for (var x = 0; x < 9; x++)
                   Console.Write(field[x, y]);
                Console.WriteLine();
             }
          }
    
       }
    }

    Sudoku

    Update 2023-02-09: .NET 7.0 source code

  • Different assumptions?

    A philosophy professor who relates to the real world, like Luciano Floridi, makes completely different assumptions than a philosophy professor who relates to his personal superstitions, like William Lane Craig. Check if you can spot any of them.

  • Vic 20-labyrinten

    Vissa är säkert bekanta labyrintalgoritmen för Commodore 64, känd för de mer eller mindre slumpmässiga förutsättningarna som gör att en labyrint kan genereras med minimal kod. Koden jag tänker på är givetvis denna (Commodore Basic 2.0):

    10 PRINT CHR$(205.5+RND(1));:GOTO 10

    Koden är faktiskt inte klockren på C64. Med en hög upplösning (320*200 pixlar) i åtanke, satsade man på teckengrafik som alltid hade minst två pixlars bredd, eftersom detta gjorde grafiken mer visuell. Det ledde till att grafiken såg “trasig” ut när man körde program som inte tog hänsyn till detta.

    Vic 20 däremot, med sin låga upplösning (352*184), behövde inte använda dubbla pixlar på vertikala linjer. Och eftersom Vic 20 och C64 delade Basic (Commodore Basic 2.0) är labyrintkoden i fråga kompatibel med Vic 20. Om vi bryter ner koden, har vi följande:

    1. RND(1) är ett slumptal mellan 0 och strax under 1.

    2. 205,5 + RND(1) är antingen 205 eller 206 (\ eller / – inte backslash respektive slash – snarare diagonal åt höger och diagonal åt vänster) beroende på vad som tilldelas 205,5 eftersom CHR$ konverterar sin parameter till ett heltal genom att avlägsna decimaldelen. Om RND(1) är t.ex. 0,1 eller 0,4 är 205,5 + 0,4 = 205, men om 205,5   tilldelas 0,6 blir resultatet 206. Vi har alltså att göra med en implisiv typomvandling från realtal till heltal.

    3. PRINT CHR$(205) ger en diagonal linje från vänster till höger, och CHR$(306) ger en diagonal linje från höger till vänster. Koden i fråga ger alltså antingen \ eller / i en följd, vilket ger en labyrint.

    Detta är resultatet av koden på en Commodore 64 eller 128:

    c64

    Detta är resultatet av samma kod på en Vic 20:

    vic20

  • Cold Fact

    This is something I wasn’t aware of until I saw the movie “Searching for Sugar Man”. The Rodriguez album Cold Fact from 1971. Just listen! Incredible!

    1. “Sugar Man
    2. “Only Good for Conversation”
    3. “Crucify Your Mind
    4. “This Is Not a Song, It’s an Outburst: Or, the Establishment Blues”
    5. “Hate Street Dialogue”
    6. “Forget It”
    7. “Inner City Blues”
    8. “I Wonder
    9. “Like Janis”
    10. “Gommorah (A Nursery Rhyme)”
    11. “Rich Folks Hoax
    12. “Jane S. Piddy”

  • 16K RAM till ZX81

    Detta är mitt expansionskort som ger 16K RAM till Sinclair ZX81.