May 04, 2024, 02:48:24 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


iLife v2 - Final

Started by WayneA, June 14, 2010, 02:10:10 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

WayneA

/* iLife v2 6/13/2010
Hopefully these changes will put my obsession over this program, which was originally written in late 2005/early 2006, to a happy end.
I've made the following optimizations resulting in a slightly smaller(1.5kb) and noticably faster EXE.
Also a few elegance upgrades were done because pretty code is important ;)
-Replaced byte by byte copying of newCells->cells in runGen with RtlMoveMemory.
-Replaced byte by byte clearing of cells in Set with RtlFillMemory.
-Converted life/death 1-line if statements in runGen to assignments based on a boolean equation.
-Removed superfluous Rect statement in Paint (this basically doubled the drawing speed itself).
Painting could be sped up using a buffered drawing system, but I don't think the difference would be appreciable
unless the number of cells were to be significantly increased.
-Extracted the grid initiation code from Paint and placed it in its own sub that is now called only once
rather than every single time the window is painted.
-Merged the handling of Right and Left click messages.
-Converted all RGB statements to integer literals.
-Converted all Asc statements to integer literals.
-Merged the handling of the random fill and clear buttons.
-Moved Control statements, SetFonts and SetClientSize calls to @IDCreate. The SetFonts are now in a loop
they take up 1 less line this way. I'm not sure if the loop is any faster or slower than 4 seperate calls.
EXE size is not impacted for sure, though.
-Encapsulated all subs as far as I could. There are now only 3 global variables from 6 or so.
-Renamed all 'ii' counters to j. Its prettier this way.
-Compiled as a project instead of single file and found that 1kb (1024 bytes exactly) is magically removed from exe size...(?!)
-Got rid of all the Return statements now that they aren't required for subs with no return value.
-Renamed various variables to make their purpose more apparent. Added a few sparse comments.
-Converted asm labels in Inc and Dec from global to local (put a dot in front).
-Added IIf to simplify color decision points. (white for dead, blue for alive)
This would be faster with just an 2-element array with both colors, but its a good chance to demonstrate IIf usage.
Note: If you want to change the size of the cells, initgrid and paint have optional parameters that can be changed
but you will also need to change the size in the Right/Left mouse click handler because the size is hardcoded there.
*/
AutoDefine "Off"
Const GWL_STYLE=-16
Const GWL_EXSTYLE=-20
Const WS_OVERLAPPED=0
Const SWP_NOMOVE=2
Declare Import,SetWindowPos(hwnd As Int,hWndInsertAfter As Int,x As Int,y As Int,cx As Int,cy As Int,wFlags As Int),Int
Declare Import,AdjustWindowRectEx(rec As Pointer,style As Int,mflag As Int,ExStyle As Int),Int
Declare Import,GetWindowLongA(hwin As Int,nIndex As Int),Int
Declare Import,SetRect(pRect As Pointer,x1 As Int,y1 As Int,x2 As Int,y2 As Int),Int
Declare Import,PtInRect(pRect As WINRECT,x As Int,y As Int),Int
Declare Import,RtlFillMemory(Destination As Pointer,Length As UInt,Fill As Char)
Declare Import,RtlMoveMemory(Destination As Pointer,Source As Pointer,Length As UInt)
Dim wndMain As Window
Dim grid[960]As WINRECT
Dim cells[48,20] As Char
InitGrid()
OpenWindow wndMain,0,0,0,0,@MinBox|@NoAutoDraw,0,"iLife",&wndMainProc
WaitUntil wndMain=0
End
Sub wndMainProc
Dim i,t As Int
Select @Message
Case @IDCreate
Control wndMain,@Button,"Q U I T",0,0,18,18*6,@CTLBTNMulti|@CTLBTNFlat,1
Control wndMain,@Button,"A B O U T",0,18*6,18,18*6,@CTLBTNMulti|@CTLBTNFlat,2
Control wndMain,@Button,"C L E A R",0,18*12,18,18*6,@CTLBTNMulti|@CTLBTNFlat,3
Control wndMain,@Button,"R A N D",0,18*18,18,18*4+3,@CTLBTNMulti|@CTLBTNFlat,4
For i=1 to 4
SetFont wndMain,"FixedSys",0,0,0,i
Next i
SetClientSize(wndMain,980,400)
Case @IDPaint
Paint(wndMain)
Case @IDLButtonDN
Case& @IDRButtonDN
For i=0 To 959
If PtInRect(grid[i],@MouseX,@MouseY)
'Color literals in format of 0x00BBGGRR
cells[i]=(@Message=@IDLButtonDN)
Rect wndMain,grid[i].left,grid[i].top,18,18,0x000000FF,IIf(cells[i],0x00FF0000,0x00FFFFFF)
BreakFor
EndIf
Next i
Case @IDControl
Select @ControlID
Case 1
SendMessage wndMain,@IDCloseWindow,0,0
Case 2
MessageBox wndMain,"Made by Wayne Anthony\nwith code by Bevets.","About."
Case 3
Case& 4
Set(cells,null,(@ControlID=4))
Paint(wndMain)
EndSelect
SetFocus wndMain
Case @IDChar
Select @wParam
Case 0x72  'Asc("r")
Case& 0x52 'Asc("R")
Case& 0x31 'Asc("1")
runGen(cells)
Paint(wndMain)
Case 0x71  'Asc("q")
Case& 0x51 'Asc("Q")
SendMessage wndMain,@IDCloseWindow,0,0
Case 0x32 'keys 2-9
Case& 0x33
Case& 0x34
Case& 0x35
Case& 0x36
Case& 0x37
Case& 0x38
Case& 0x39
t=@wParam-48
For i=1 to t
runGen(cells)
Next i
Paint(wndMain)
EndSelect
Case @IDCloseWindow
CloseWindow wndMain
EndSelect
EndSub
Sub runGen(cells[48,20] As Char)
Dim i,j,nAlive As Int
Dim newCells[48,20] As Char
For i=0 to 47
For j=0 to 19
nAlive=0
nAlive=cells[Dec(i,48),j]+cells[Inc(i,48),j]+cells[i,Dec(j,20)]+cells[i,Inc(j,20)]+_
cells[Dec(i,48),Dec(j,20)]+cells[Inc(i,48),Inc(j,20)]+cells[Dec(i,48),Inc(j,20)]+_
cells[Inc(i,48),Dec(j,20)]
Select cells[i,j]
Case 0
'If nAlive is 3 the cell is born
newCells[i,j]=(nAlive=3)
Case 1
'If nAlive is 2 or 3 the cell remains alive, otherwise its dead.
newCells[i,j]=(nAlive=2 Or nAlive=3)
EndSelect
Next j
Next i
RtlMoveMemory(cells,newCells,48*20)
EndSub
Sub Set(cells[48,20] As Char,value As Int,Opt random=False As Int)
Dim i,j As Int
If !random Then
RtlFillMemory(cells,48*20,value)
Else
For i=0 to 47
For j=0 to 19
cells[i,j]=Rand(0,1)
Next j
Next i
EndIf
EndSub
'Inc and Dec are used to allow the universe to wrap around. They are in asm just to squeeze a little extra speed because
'they are called (in total) 12 times on each cell, with 960(48*20) cells thats 11520 times for each generation.
'compared to the original IBasic Pro generated code for the If statements commented out in each sub, the inline asm
'was IIRC 30 bytes or so (total) less. I didn't time them but I suspect/hope this code is at least a microsecond or so faster..
Sub Inc(in As Int,max As Int),Int
Dim ret As Int
'If in=max-1 Then ret=0 Else ret=in+1
_asm
dec dword [ebp+12]
mov eax,[ebp+12]
cmp dword [ebp+8],eax
jne .diff
mov dword [ebp-4],0
jmp .done
.diff:
mov eax,[ebp+8]
mov dword [ebp-4],eax
inc dword [ebp-4]
.done:
_endasm
Return ret
EndSub
Sub Dec(in As Int,max As Int),Int
Dim ret As Int
'If in=0 Then ret=max-1 Else ret=in-1
_asm
dec dword [ebp+12]
cmp dword [ebp+8],0
jne .diff
mov dword eax,[ebp+12]
mov dword [ebp-4],eax
jmp .done
.diff:
mov eax,[ebp+8]
mov dword [ebp-4],eax
dec dword [ebp-4]
.done:
_endasm
Return ret
EndSub
Sub IIf(cond As Int,IfTrue As Int,IfFalse As Int),Int
Dim ret As Int
_asm
cmp dword [ebp+8],0
je .false
mov dword eax,[ebp+12]
mov dword [ebp-4],eax
jmp .ret
.false:
mov dword eax,[ebp+16]
mov dword [ebp-4],eax
.ret:
_endasm
Return ret
EndSub
Sub InitGrid(Opt cellSize=20 As Int,Opt spacing=2 As Int)
Dim i,col,row As Int
For i=0 To 959
If i%48=0 Then
'If we can divide i by 48 with no remainder then we re-calc the current row and reset the column counter
row=i/48
col=0
EndIf
grid[i].top=row*cellSize
col+=cellSize
'the column counter is incremented by the cell size because fewer instructions are required to determine the cell's
'left position. It is the same as simply incrementing col and multiplying the number of the column by size of each cell everytime.
grid[i].left=col
grid[i].right=grid[i].left+(cellSize-spacing)
grid[i].bottom=grid[i].top+(cellSize-spacing)
Next i
EndSub
Sub Paint(win As Window,Opt cellSize=20 As Int,Opt spacing=2 As Int)
'Color literals in format of 0x00BBGGRR
Dim i,size=cellSize-spacing As Int
For i=0 To 959
Rect win,grid[i].left,grid[i].top,size,size,0x000000FF,IIf(cells[i],0x00FF0000,0x00FFFFFF)
Next i
EndSub
Sub SetClientSize(win As Window,w As Int,h As Int,Opt fmenu=0 As Int),Int
WINRECT rec
Int style=0,exstyle=0,err=0
style=GetWindowLongA(win.hwnd,GWL_STYLE)
If style
style=style & not(WS_OVERLAPPED)
exstyle=GetWindowLongA(win.hwnd,GWL_EXSTYLE)
If style=0 | exstyle=0 Then Return 0
EndIf
err=SetRect(rec,0,0,w,h)
If err
err=AdjustWindowRectEx(rec,style,fmenu,exstyle)
If err
err=SetWindowPos(win.hwnd,0,0,0,rec.right+Int(ABS(rec.left)),rec.bottom+Int(ABS(rec.top)),SWP_NOMOVE)
EndIf
EndIf
Return err
EndSub
99 little bugs in the code,
99 bugs in the code,
Fix one bug,
Compile again,
104 little bugs in the code...

All code I post is in the public domain.