vbScript :: Encoded + Obfuscated

Over the years, I get used to automate all of those little processes that go beyond a simple copy & paste files, especially the casual ones. This way, they stay not only easily reproducibles, but also well documented.

The best way to document a process, is to create a well structured script able to reproduce it ;-)

This makes my life much easier, but from time to time, I need to execute one of these script on client’s computer. What to do then?

We could copy the script to his computer and simply launch it, but we run the risk of running into one of those “I like to make your life more interesting” user, for whom a so visible script is too much temptation.

In fact, related to those users, I’m not worried at all that they could browse in the script code, maybe they even could learn something, what really worries me is that they could believe that they have discovered the Pandora’s box of administrator secrets, and they could decide to happily modify something, generating the corresponding destruction – more information about this kind of users in any BOFH thematic blog.

As it is said, better safe than sorry, so to avoid temptation, we’d better apply some kind of obfuscation to the script. And as our only goal is to avoid the user to tinker with our code, we can use some of the several free simple techniques which can be easily found googling (see eg free-vbscript-obfuscator).

However, frequently happens that once our script has been obfuscated, the antivirus agent decides that our script is suspected to be a virus, proceeding to its inmediate elimination or simply stopping its execution. With all good intentions, the antivirus has detected the obfuscated code and puts us a little bump on our way towards the automating tasks on the client machine.

Luckily, Microsoft offer a tool for coding scripts (see eg Encode a Script), which once applied to our obfuscated code, creates a second layer of protection over the script. This way, the obfuscated-and-encoded script is no longer visible to antivirus agents.

And as all of these operations have gone beyond a simple copy & paste files, we’d better collect and sort them all in a single script, that automates the process of obfuscation and encoding of those vbs files, whose code it is wanted to stay away from prying eyes ;-)

In any case, you must be aware that these techniques do not provide real security to our script. In deed, some techniques to decode Windows Script Encoded files have been documented (see eg Breaking The Windows Script Encoder ), and once a file is decoded, it will be only more or less legible, depending on the complexity we had implemented in the obfuscation routine.

Option Explicit

Dim oEncoder
Set oEncoder = CreateObject("Scripting.Encoder")

Dim oFso
Set oFso = CreateObject("Scripting.FileSystemObject")

Dim oFiles
set oFiles = WScript.Arguments


Call EnObfuscate(oFiles)

Private function EnObfuscate(oFiles)
  Dim sFileName, oFile, sBody, i
  Dim sFileOut, sOutput

  For i = 0 to oFiles.Count - 1
    sFileName = oFiles(i)
    set oFile = oFso.GetFile(sFileName)
    sBody = ReadFile(oFile)

    sOutput = Encode(Obfuscate(sBody))

    sFileOut = Left(sFileName, Len(sFileName) - 3) & "vbe"
    WriteFile sFileOut, sOutput
End Function

Private Function ReadFile(oFile)
  Dim oStream


  Set oStream = oFile.OpenAsTextStream(IOMODE_FOR_READING)
  ReadFile = oStream.ReadAll
End Function

Private Sub WriteFile(sFileOut, sOutput)
  Dim oOutputFile

  Set oOutputFile = oFso.CreateTextFile(sFileOut)
  oOutputFile.Write sOutput
End Sub

Private Function Obfuscate(txt)
  Dim enc, i

  enc = ""
  for i = 1 to len(txt)
    enc = enc & "chr( " & form( asc(mid(txt,i,1)) ) & " ) & "

  Obfuscate = "Execute(" & enc & " vbcrlf " & " ) "
End Function

Private Function form(n)
  dim r, k, ret

  r = int(rnd * 10000)
  k = int(rnd * 3)
  if( k = 0) then ret = (r+n) & "-" & r
  if( k = 1) then ret = (n-r) & "+" & r
  if( k = 2) then ret = (n*r) & "/" & r

  form = ret
End Function

Private Function Encode(txt)
  Const Extension = ".vbs"
  Const Flags = 0
  Const Language = ""

  Encode = oEncoder.EncodeScriptFile(Extension, txt, Flags, Language)
End Function


  • Create a file with the above code and save it with your preferred name (eg: enobfuscator.vbs)
  • To encode a file, just drag & drop it over the “enobfuscator” script.
  • Or from command line, just pass in the vbs filename to the script as an argument

Tron 3.0

I have been spending a little more of time in CBM-Basic, so I am ready to present… Tron 3.0.
– Improved Graphics and speed up to the limits of CBM-Basic of unexpanded Vic-20.

The code is not as “academic” as before due to memory optimizations, but it is still fully commented.

Download source code and d64 image disc: Tron 3.0

Source code.

:rem tron 3.0      :::
:rem by f.g.huerta :::

:rem ms->screen memory (normally 7680)
:rem mc->color memory (normally 38400)
:rem cm->character memory (normally 32768) 
1 ms=peek(648):mc=37888+256*(ms and 2):ms=ms*256:cm=32768:poke36879,16

:rem music :::::::::::
:rem m1,m2,m3 music chanels
:rem n%(8) -> melodie
:rem max volumen
3 dim n%(8):m1=36874:m2=36875:m3=36876:ni=0:poke 36878,15

:rem players :::::::::
:rem p%(i, data) -> data player array
:rem i -> player id
:rem p%(i, 0)-> x pos
:rem p%(i, 1)-> y pos
:rem p%(i, 2)-> ix
:rem p%(i, 3)-> iy
:rem p%(i, 4)-> color
:rem p%(i, 5)-> status: 0->ok, 1->crashed
:rem p%(i, 6)-> current q
:rem p%(i, 7)-> last q 
:rem p%(i, 8)-> last ix
:rem p%(i, 9)-> last iy
:rem p%(i,10)-> num crashes
:rem v ->velocity
:rem b ->background sprite 46
:rem nc -> num cols
:rem nr -> num rows
4 dim p%(1,10):v=1:nc=22:nr=22:b=102

:rem sprites
:rem s(0) -> up down 93
:rem s(1) -> up right 112
:rem s(2) -> up left 110
:rem s(3) -> left right 64
:rem s(4) -> down right 109
:rem s(5) -> down left 125
5 s0=93:s1=112:s2=110:s3=64:s4=109:s5=125
:rem 52 s$=chr$(93)+chr$(112)+chr$(110)+chr$(64)+chr$(109)+chr$(125)
:rem chr$(158) -> yellow
:rem chr$(30)  -> green
:rem chr$(31)  -> blue

:rem new game entry point
:rem reset scores
10 p%(0,10)=0:p%(1,10)=0

:rem new match entry point
:rem init melodie
11 for i=0 to 8:read n%(i):next
:rem init players 
12 for i=0 to 1: for d=0 to 9: read p%(i,d): next:next
:rem clear screen cyan chr$(159): white 5: black 144
13 k$="":for i=1 to nc:k$=k$+chr$(166):next i
14 ?chr$(147)chr$(28):for i=0 to nr-1:?k$;:next
:rem paint scores
15 gosub 140

:::rem begin main loop
: rem debug - print main loop miliseconds
rem 20 ?chr$(19)+str$(timer-t)+"    ";:t=timer

: rem match finished?
20 if p%(0,5) or p%(1,5) then 100

: rem game finished?
22 if p%(0,10)=9 or p%(1,10)=9 then 120

: rem store old velocities
23 p%(0,8)=p%(0,2):p%(0,9)=p%(0,3):p%(1,8)=p%(1,2):p%(1,9)=p%(1,3)

: rem read keyboard in any key
24 if peek(198)=0 then 30

:::::::::::: rem exit? 'x'
25 get k$:if k$="x" then 120

:::::::::rem player ia
26 if k$="a" then p%(0,2)=-v:p%(0,3)=0:goto 30
27 if k$="d" then p%(0,2)=v:p%(0,3)=0:goto 30
28 if k$="w" then p%(0,3)=-v:p%(0,2)=0:goto 30
29 if k$="s" then p%(0,3)=v:p%(0,2)=0:goto 30

:::::::::::rem computer ia
: rem inminent crash? and little random factor
rem 30 q=(p%(1,1)+p%(1,3))*nc+p%(1,0)+p%(1,2):i=rnd(1)
30 d1=p%(1,1):d3=p%(1,3):d0=p%(1,0):d2=p%(1,2)
31 q=(d1+d3)*nc+d0+d2:i=rnd(1)
32 if peek(ms+q)=b and i<.95 then 40
: rem  change direction
33 d=v: if i<.5 then d=-d
34 if d2<>0 then q=(d1+d)*nc+d0:goto 36
35 q=d1*22+d0+d
: rem direction blocked? change direction again
36 if peek(ms+q)<>b then d=-d
: rem final decision
37 if d2<>0 then p%(1,2)=0:p%(1,3)=d: goto 40
38 p%(1,2)=d:p%(1,3)=0

:::::::::: rem phisics
40 for i=0to1
rem 40 i=0
 :::::::::::: rem move - memory optimized
rem 41 d0=p%(i,0):d2=p%(i,2):p%(i,0)=d0+d2+22*((d0=21andd2=1)-(d0=0andd2=-1))
rem 42 d1=p%(i,1):d3=p%(i,3):p%(i,1)=d1+d3+22*((d1=21andd3=1)-(d1=0andd3=-1))

::::::::: rem move - speed optimized
41  d0=p%(i,0)+p%(i,2):d1=p%(i,1)+p%(i,3):p%(i,0)=d0:p%(i,1)=d1
42  if d0<0  then p%(i,0)=21
43  if d0>21 then p%(i,0)=0
44  if d1<0  then p%(i,1)=21
45  if d1>21 then p%(i,1)=0
46 next
rem 46 if i=0 then i=1:goto 41

:::::rem check crashes
:rem draw?
50 if p%(0,0)=p%(1,0) and p%(0,1)=p%(1,1) then p%(0,5)=1:p%(1,5)=1:goto 100

:rem crash?
51 for i=0 to 1
 :rem if crashes ->
 :rem  - update status
 :rem  - increment crashes
52  p%(i,7)=p%(i,6):p%(i,6)=p%(i,1)*nc+p%(i,0)
53  if peek(ms+p%(i,6))<>b then p%(i,5)=1: p%(i,10)=p%(i,10)+1
54 next

:::::::::::::rem paint
60 for i=0 to 1
 :::::::::::::rem tail
 :: rem get tail char
 61 d2=p%(i,2):if d2 and d2=p%(i,8) then d=s3:goto 67
 62 d3=p%(i,3):if d3 and d3=p%(i,9) then d=s0:goto 67

 63 if (p%(i,9)=-v and p%(i,2)=-v) or (p%(i,8)= v and p%(i,3)= v) then d=s2:goto 67
 64 if (p%(i,9)=-v and p%(i,2)= v) or (p%(i,8)=-v and p%(i,3)= v) then d=s1:goto 67
 65 if (p%(i,9)= v and p%(i,2)=-v) or (p%(i,8)= v and p%(i,3)=-v) then d=s5:goto 67
 66 if (p%(i,9)= v and p%(i,2)= v) or (p%(i,8)=-v and p%(i,3)=-v) then d=s4:goto 67
 :: rem poke tail & head
 67 d7=p%(i,7):poke ms+d7,d:poke mc+d7,p%(i,4):d6=p%(i,6):poke ms+d6,90:poke mc+d6,0
68 next

: rem play music
69 gosub 85

:::::rem end main loop
70 goto 20

:rem music :::::::::::
:rem prepare next note
85 ni=ni+1:if ni>8 then ni=0:poke m1,0
:rem play a note :::::
86 poke m1,n%(ni):poke m2,n%(ni):poke m3,n%(ni)
87 return

:::::::: rem music off
88 poke m1,0:poke m2,0:poke m3,0:ni=0
89 return

:::::::::rem end match
::::::::rem select msg
100 if p%(0,5)=1 then msg$="human crash!":q=p%(0,1)*nc+p%(0,0):poke mc+q,2
101 if p%(1,5)=1 then msg$="computer crash!":q=p%(1,1)*nc+p%(1,0):poke mc+q,2
102 if p%(0,5)+p%(1,5)=2 then msg$="draw!"
:::::::::rem music off
103 gosub 88

::::::::rem earthquake
104 for n=1 to 10:for m=10 to 14
105  poke 36864,m:poke 36877,240-m
106 next m: next n
107 poke 36877,0

:::::::::rem show info
110 k$=chr$(17):?chr$(19)k$k$k$k$k$k$k$k$k$k$k$k$
111 ?chr$(28)"  "msg$:?"  any key continue   ";
::rem flush key buffer
112 get k$: if k$<>"" then 112
::::::::::::::rem wait
113 for i=1 to 1000: next
:rem read keyboard
115 get k$: if k$="" then 115
:::::::::::::rem exit?
116 if k$="x" then 120
::rem start new match
117 restore
118 goto 11

:::::: rem exit ::::::
: rem music off
120 gosub 88
: rem score screen
121 ?chr$(144)chr$(147):
122 ?"final score"
123 ?"----------------------"
124 ?"human    : ";p%(1,10)
125 ?"computer : ";p%(0,10)
126 if p%(0,10)=p%(1,10) then msg$="draw!":goto 129
127 if p%(0,10)<p%(1,10) then msg$="human wins!":goto 129
128 msg$="computer wins!"
129 ?msg$

:::::: new game? :::::
130 ?:?"play again (y/n)?"
: rem flush key buffer
131 get k$: if k$<>"" then 131
::::::::::::: rem wait
132 for i=1 to 1000: next
133 get k$: if k$="" then 133
134 if k$="y" then restore:goto 10
135 ?"bye.":end

:::::::rem paint score
140 ?chr$(19);:for i=1 to nc:?chr$(17);:next:d0=p%(0,10):d1=p%(1,10)
141 ?chr$(31)" human:"chr$(158)str$(d1)chr$(31)" computer:"chr$(30)str$(d0);
::::::: rem background score
142 for i=0 to 1
143 for ir=0 to 7:d=peek(cm+(48+p%(1-i,10))*8+ir)
144 for ic=7 to 0 step -1
145 d=d/2:d0=0:if d=int(d) then d0=p%(i,4)
146 d=int(d):poke mc+(ir+1)*nc+ic+2+10*i,d0
147 next:next:next
148 return

::::: rem data :::::::
: rem melody
200 data 215,201,187, 215,201,195, 215,201,187
: rem initial players values
210 data 5,10,1,0,7,0,225,224,0,1
220 data 16,10,-1,0,5,0,236,237,0,1

Tron 2.0

My first quick version of tron was extremely simple.

Here it is another more user friendly version, with improved computer I.A. and sound effects.

In game screen.

Download source code and d64 image disc: tron.rar

Source code.

:rem tron 2.0      :::
:rem by f.g.huerta :::

:: rem configure music
::::::::::: volume max
10 poke 36878,15

:rem music :::::::::::
:rem mm(2) -> instruments
:rem n%(3) -> melodie
20 dim mm(2):dim n%(3)
:rem init instruments
22 mm(0)=36874:mm(1)=36875:mm(2)=36876
:rem music pointers
24 mi=0:ni=0

:rem players :::::::::
:rem p(pn, data) -> data player array
:rem pn -> player id
:rem p(pn, 0)-> x pos
:rem p(pn, 1)-> y pos
:rem p(pn, 2)-> ix
:rem p(pn, 3)-> iy
:rem p(pn, 4)-> color
:rem p(pn, 5)-> status: 0->ok, 1->crashed
:rem p(pn, 6)-> num crashes
:rem p(pn, 7)-> last x pos
:rem p(pn, 8)-> last y pos
30 dim p(1,8)

:rem ms->screen memory
:rem mc->color memory
40 ms=7680: mc=38400

:rem v ->velocity
:rem s ->character sprite
:rem hc->head color
50 v=1:s=asc("*"): hc=0

:rem new game entry point
:rem reset scores
60 p(0,6)=0:p(1,6)=0

:rem new match entry point
:rem init melodie
70 for i=0 to 3:read n%(i):next i
:rem init players 
75 for pn=0 to 1: for i=0 to 5
80  read p(pn,i)
90 next i: next pn

:rem clear screen
100 print chr$(147)

:::rem begin main loop

: rem match finished?
110 if p(0,5)+p(1,5) > 0 then 1510

: rem game finished?
120 if p(0,6)=9 or p(1,6)=9 then 1810

: rem read keyboard
130 get k$

: rem exit?
140 if k$="x" then goto 1810

:::::::::rem player ia
210 if k$="a" then p(0,2)=-v:p(0,3)=0
220 if k$="d" then p(0,2)=v :p(0,3)=0
230 if k$="w" then p(0,3)=-v:p(0,2)=0
240 if k$="s" then p(0,3)=v :p(0,2)=0

:::::::::::rem computer ia
: inminent crash? and little random factor
310 q=(p(1,1)+p(1,3))*22+p(1,0)+p(1,2)
320 if peek(ms+q)<>s and rnd(1)<.95 then goto 410
: change direction
330 d=v: if rnd(1)<.5 then d=-d
340 if p(1,2)<>0 then q=(p(1,1)+d)*22+p(1,0):goto 360
350 q=p(1,1)*22+p(1,0)+d
: direction blocked? change direction again
360 if peek(ms+q)=s then d=-d
: final decision
370 if p(1,2)<>0 then p(1,2)=0:p(1,3)=d: goto 410
380 p(1,2)=d:p(1,3)=0

:::::::::: rem phisics
410 for pn=0 to 1
 :::: rem save old pos
420  p(pn,7)=p(pn,0):p(pn,8)=p(pn,1)
 :::::::::::: rem move
430  p(pn,0)=p(pn,0)+p(pn,2): p(pn,1)=p(pn,1)+p(pn,3)
 ::::::::: rem fix pos
440  if p(pn,0)<0  then p(pn,0)=21
450  if p(pn,0)>21 then p(pn,0)=0
460  if p(pn,1)<0  then p(pn,1)=22
470  if p(pn,1)>22 then p(pn,1)=0
480 next pn

:::::rem check crashes
:rem draw?
510 if p(0,0)=p(1,0) and p(0,1)=p(1,1) then p(0,5)=1:p(1,5)=1:goto 1510

:rem player crashes?
520 for pn=0 to 1
 :rem if crashes ->
 :rem  - update status
 :rem  - increment crashes
530  q=p(pn,1)*22+p(pn,0)
540  if peek(ms+q)=s then p(pn,5)=1: p(pn,6)=p(pn,6)+1
550 next

:::::::::::::rem paint
610 for pn=0 to 1
 :::::::::::::rem tail
630  q=p(pn,8)*22+p(pn,7)
640  poke mc+q,p(pn,4)
 :::::::::::::rem head
660  q=p(pn,1)*22+p(pn,0)
670  poke ms+q,s
680  poke mc+q,hc
690 next

:::::::rem paint score
720 q=2*22+2
730 poke mc+q,p(0,4)
740 poke ms+q,48+p(1,6)
750 q=2*22+19
760 poke mc+q,p(1,4)
770 poke ms+q,48+p(0,6)

: play music
780 gosub 1110

:::::rem end main loop
790 goto 110

::::: music ::::::::::
:rem prepare next note
1110 ni=ni+1:if ni>3 then ni=0:poke mm(mi),0:mi=mi+1
1120 if mi>2 then mi=0
:rem play a note :::::
1130 poke mm(mi),n%(ni)
1140 return

:::::::::::: music off
1210 poke mm(0),0
1220 poke mm(1),0
1230 poke mm(2),0
: reset music pointers
1240 mi=0:ni=0
1250 return

:::::::::rem end match
::::::::rem select msg
1510 if p(0,5)=1 then msg$="human crash!":q=p(0,1)*22+p(0,0):poke mc+q,2
1520 if p(1,5)=1 then msg$="computer crash!":q=p(1,1)*22+p(1,0):poke mc+q,2
1530 if p(0,5)+p(1,5)=2 then msg$="draw!"

:::::::::rem music off
1540 gosub 1210

::::::::rem earthquake
1550 for n=1 to 10:for m=10 to 14
1560  poke 36864,m:poke 36877,240-m
1570 next m: next n
1580 poke36877,0

:::::::::rem show info
1610 print msg$
1620 print "any key continue"
::rem flush key buffer
1630 get k$: if k$<>"" then 1630
::::::::::::::rem wait
1640 for i=1 to 1000: next
:rem read keyboard
1650 get k$: if k$="" then 1650
:::::::::::::rem exit?
1660 if k$="x" then 1810
::rem start new match
1670 restore
1680 goto 70

:::::: rem exit ::::::
: rem music off
1810 gosub 1210
: rem score screen
1815 print chr$(147)
1820 print "final score"
1830 print "----------------------"
1840 print "human    : ";p(1,6)
1850 print "computer : ";p(0,6)
1860 if p(0,6)=p(1,6) then msg$="draw!":goto 1890
1870 if p(0,6)<p(1,6) then msg$="human wins!": goto 1890
1880 msg$="computer wins!"
1890 print:print msg$

:::::: new game? :::::
1900 print:print"play again (y/n)?"
: rem flush key buffer
1910 get k$: if k$<>"" then 1890
::::::::::::: rem wait
1920 for i=1 to 1000: next
1930 get k$: if k$="" then 1930
1940 if k$="y" then restore:goto 60
1950 print "bye."
1960 end

::::: rem data :::::::
: melody
2010 data 195,201,207,209
: initial players values
2020 data 9, 11, 0, -1, 7, 0
2030 data 11, 11, 0, -1, 5, 0

Tron – Remembering old Commodore days

A couple of days ago, I found in Amazon the book “A commodore 64 Walkabout by Robinson Mason”, so I had no choice but buy it.

As many other nowadays developers, in the eighties I was the owner of a fantastic Commodore Vic-20, a real cool computer, the real programmer teacher for many of us.

After reading no more than twenty pages, I had to download the VICE emulator and as if it was an old ritual, I began to type the game of TRON, a clasic game that I have developed in every computer platform that I have owned, and in every programming language that I have learned.

Here is the code I have been able to develop in a couple of hours of remembering CBM Basic. The code works ok in the Commodore Vic-20 emulation of VICE. Enjoy it.

10 : rem TRON   :::::
12 : rem by     :::::
14 : rem F.G.Huerta :

20 rem ::::::init vars
25 rem n => (x, y, ix, iy, color)
30 dim p%(1,4)
40 for pn=0 to 1
50  for i=0 to 4
60   read a: p%(pn,i)=a
70  next i
80 next pn

90 :::::::rem velocity
100 v=1

110 rem ::::::::sprite
120 s=asc("*")

130 rem screen & color
140 ms=7680: mc=38400

150 :::rem init screen
160 print chr$(147)

170 :::::rem main loop
180 get k$
190 if k$="x" then msg$="bye":goto 1600: rem salir

200 :::::rem player ia
210 if k$="a" then p%(0,2)=-v:p%(0,3)=0
220 if k$="d" then p%(0,2)=v :p%(0,3)=0
230 if k$="w" then p%(0,3)=-v:p%(0,2)=0
240 if k$="s" then p%(0,3)=v :p%(0,2)=0

300 :::::::rem comp ia
310 x=p%(1,0)+p%(1,2):y=p%(1,1)+p%(1,3)
320 gosub 1000: rem get char
330 if z<>s then goto 400
340 if p%(1,2)<>0 then p%(1,2)=0:p%(1,3)=1: goto 400
350 p%(1,2)=1:p%(1,3)=0

400 :::::::rem phisics
410 for pn=0 to 1
420   gosub 1100: rem p to vars
430   gosub 1200: rem move
440   gosub 1300: rem vars to p
450 next pn

500 :::::::rem chk fin
510 if p%(0,0)=p%(1,0) and p%(0,1)=p%(1,1) then 1400: rem empate
520 for pn=0 to 1
530   gosub 1100: rem p to vars
540   gosub 1000: rem get char
550   if z=s then 1500: rem winner
560 next

600 ::::::::rem pintar
610 for pn=0 to 1
620   q=p%(pn,1)*22+p%(pn,0)
630   poke mc+q,p%(pn,4)
640   poke ms+q,s
650 next

700 ::::::rem end loop
710 goto 170

1000 ::::::rem get char
1010 q=y*22+x
1020 z=peek(ms+q)
1030 return

1100 :::::rem p to vars
1110 x = p%(pn,0): y = p%(pn,1)
1120 ix= p%(pn,2): iy= p%(pn,3)
1130 return

1200 ::::::::: rem move
1210 x=x+ix: y=y+iy
1220 if x<1  then x=21
1230 if x>21 then x=0
1240 if y<1  then y=21
1250 if y>21 then y=0
1260 return

1300 :::::rem vars to p
1310 p%(pn,0)=x
1320 p%(pn,1)=y
1330 return

1400 ::::::: rem empate
1410 msg$="draw"
1420 gosub 1700: rem efectos especiales
1420 goto 1600: rem salir

1500 :::::::rem winner
1510 msg$="human win"
1520 if pn=0 then msg$="computer win"
1530 gosub 1700: rem efectos especiales

1600 ::::::::rem salir
1610 print msg$
1620 end

1700 :::rem efecto fin
1710 for n=1 to 10
1720  for m=10 to 14
1730   poke36864,m
1740  next m
1750 next n
1760 return 

2000 :::::::::rem data
2010 data 9, 11, 0, 1, 7
2020 data 11, 11, 0, 1, 2

Unity first impressions

I am developping a simple mini-game with Unity.

My first impression is that Unity is as powered as easy to use. I am really impresed.
I only I had better 3D Studio skills!

Woodman progress: http://nanomuelle.com/woodman
Unity home page: http://unity3d.com/

Owner Credential & Propel Admin Generator

It would be great to be able to specify in generator.yml a per object owner credential, so the generator only showed that actions to the owner of each object. Lets see how we can achieve this behavior easily.

Setting up the project.

Install and Configure Symfony

Follow the instructions of the great jobeet tutorial to install symfony and create the frontend application. We will use the propel ORM.

Instaling sfGuardPlugin

Following the symfony philosophy, we do not need to reinvent the wheel.

$ symfony plugin:install sfGuardPlugin

Preparing schema and some sample data

# config/schema.yml
    title:    { type: varchar(255), required: true }
    content:  { type: longvarchar, required: false }
      type: integer
      foreignTable: sf_guard_user
      foreignReference: id
      required: true
# data/fixtures/fixtures.yml
    username: andrew
    password: andrew
    username: valentine
    password: valentine

    title: Andrew's Post
    content: This is the content of the first Andrew's post.
    owner_id: andrew
    title: Valentine's Post
    content: This is the content of the first Valentine's post.
    owner_id: valentine

Build models, load data and create the post module

$ symfony generate:app frontend
$ symfony propel:build --all --and-load
$ symfony propel:generate-admin frontend Post
$ symfony plugin:publish-assets
$ symfony clear cache

The funny work

At this point, if you go to the url ocag.localhost/post, we can see the list of posts, with two actions attached to each post: edit and delete.

What we want is that the admin-generator only shows the actions edit and/or delete to the owner of each post.

Enabling login module and securing post module

Add the following to settings.yml

# apps/frontend/settings.yml
# ...
    # ...
    # Modules
    enabled_modules: [default, sfGuardAuth]

    # ...
    login_module:   sfGuardAuth
    login_action:   signin
    secure_module:  sfGuardAuth
    secure_action:  secure

Secure the post module

# apps/frontend/modules/post/config/security.yml
  is_secure: true

Edit the generator.yml

First, we set the owner credential to an object action.

What we woult like the admin generator to do with this, is to hide that action to everybody but the creator of the object being procesed.

Saddly this is not way the default admin generator works, but we will soon see how to build our own customized admin theme with this behavior.

Don’t be afraid! It’s easyer than it appears.

# apps/modules/post/config/generator.yml
  class: sfPropelGenerator
    model_class:           Post
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              Post
    plural:                Posts
    route_prefix:          post
    with_propel_route:     1
    actions_base_class:    sfActions

      actions: ~
      fields:  ~
        # setting owner credential
            credentials: owner
            credentials: owner
      filter:  ~
      form:    ~
      edit:    ~
      new:     ~

Creating myadmin theme

Create the folder data/generator/sfPropelModule/myadmin and copy the admin theme files in order to customize them:

$ mkdir -p data/generator/sfPropelModule/myadmin
$ cp -r lib/vendor/symfony/lib/plugins/sfPropelPlugin/data/generator/sfPropelModule/admin/* \

Configure the generator.yml to use myadmin theme

# apps/modules/post/config/generator.yml
    theme:                 myadmin

The partial-generator responsible of the object_actions is data/generator/sfPropelModule/myadmin/template/templates/_list_td_actions.php. We are going to add a line that calls a user function that dinamically adds or revoques the owner credential depending on the object being processed.

<?php echo '<?php $sf_user->addOwnerCredentials($' . $this->getSingularName() . ') ?>' ?>
  <ul class="sf_admin_td_actions">
<?php foreach ($this->configuration->getValue('list.object_actions') as $name => $params): ?>
<?php if ('_delete' == $name): ?>
    <?php echo $this->addCredentialCondition('[?php echo $helper->linkToDelete($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>

<?php elseif ('_edit' == $name): ?>
    <?php echo $this->addCredentialCondition('[?php echo $helper->linkToEdit($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>

<?php else: ?>
    <li class="sf_admin_action_<?php echo $params['class_suffix'] ?>">
      <?php echo $this->addCredentialCondition($this->getLinkToAction($name, $params, true), $params) ?>

<?php endif; ?>
<?php endforeach; ?>

Now edit myUser.class.php to code the addOwnerCredentials method

// apps/frontend/lib/myUser.class.php
class myUser extends sfGuardSecurityUser
  protected static $OWNER_CREDENTIAL = 'owner';

   * If the user is the owner of the $object adds owner credentials,
   * otherwise remvoques it.
  public function addOwnerCredentials($object)
    if (!$this->isAuthenticated())
      return false;

    if (!$object)
      return false;

    if (get_class($object) == 'sfOutputEscaperObjectDecorator')
      $object = $object->getRawValue();

    if (method_exists($object, 'isOwner') && $object->isOwner($this->getGuardUser()))
    } else

Tunning Post model

The only thing we have to do now is to add the isOnwer method to each model that we want to be checked against owner credential, Post model in our case. So lets edit lib/model/Post.php

// lib/model/Post.php
class Post extends BasePost {
  public function isOwner($sf_guard_user) {
    return $this->owner_id == $sf_guard_user->getId();

Try it

And thats all. Clear caches, login as Andrew or Alice and see how the admin generator only shows the action links to the owner of each post. See the post list logged as Andrew:

Very immportant final comment

Remember that the only thing that we have done with this is to automatize the view layer generated by the admin generator. You still must securize the edit and delete actions of each module in order to avoid undeserable people to be able to execute them.

How I beat Bejeweled Blitz

Tired of being beated week after week by my facebook friends, I decided to investigate the most hidden secrets of Bewejeled Blitz. After one or two thousands games, I finally realize that the only secret was to be the fastest. As I was not the fastest, there was only one thing I could do to beat my friends… I would program a script that beat them for me.

Of course, it was an incredible opportunity to learn a little more about python also.

How the script works

Basically, the script do what a human do while playing:

  1. Look at the board. The script captures the Bejeweled Blitz board, and analyzes where the jewels are. To do so, I used the PIL library to capture the screen. After that, I posterize the image reducing the number of colors, making it easy to distinguish the jewels between each other.
  2. Find a goal. The script simulates a jeweled move in memory, and looks up for a goal (three-in-a-row). If there is a goal, the script moves the mouse (PyWin32 API) and actually makes the move.
  3. Repeat everything again and again until game-time ends.

How to use it

The first thing that you need is to find out the (x, y) coordinates of the Bejeweled Blitz Board. To do this, start a Bejewelled Blitz and capture the screen with the –save option of the script. The next command makes a capture of the screen and saves it with the name “my_capture.bmp”.

c:>python Bwbot.pyw --save my_capture.bmp 0 0

Then open the image with a program like GIMP (http://www.gimp.org/) and look up the upper left coordinates of the Bejewelled Blitz Board.

Bejeweled Blitz Board coordinates

To make the script play, just start a new game, and execute the script passing it the x and y coordinates you has just find out, for example:

c:>python Bwbot.pyw 527 455

The script

## Bwbot.pyw
## A python script to play Bejeweled Blitz
## Author:  Fernando García
## Licence: The script is under public domain. You can use, distribute or
##          modify it at your own risk.
import win32api, win32con
import random
import Image
import ImageChops
import math, operator
from PIL import ImageGrab
from PIL import ImageOps
import time
import argparse

def saveImage(filename):
    "Capture screen and save it to a bmp file"
    _im = ImageGrab.grab()
    _im.save(filename, "BMP")        

class Bwbot:    
    WIDTH = 320   # width of the board
    HEIGHT = 320  # height of the board
    COLS = 8      # number of cols of the board
    ROWS = 8      # number of rows of the board
    LAST_ROW_INDEX = 7 # index of the last row of the board
    LAST_COL_INDEX = 7 # index of the last row of the board
    SQUARE_WIDTH = WIDTH / COLS   # width of a grid square
    MID_SQUARE_WIDTH = SQUARE_WIDTH / 2 # mid width of a grid square
    SQUARE_HEIGHT = HEIGHT / ROWS   # heigth of a grid square
    MID_SQUARE_HEIGHT = SQUARE_HEIGHT / 2 # mid height of a grid square

    def __init__(self, ox, oy):
        "Initializes instance variables"
        # play time in secconds
        self.playtime = 55  
        # drag delay in miliseconds
        # 20.-human 15.-optimal 10.-quick
        self.drag_delay = 20
        # screen position of the board in pixels
        self.ox = ox        # x screen coordinate of the board
        self.oy = oy        # y screen coordinate of the board
        # rect of screen to capture de board 
        self.grid = (self.ox, self.oy, self.ox + Bwbot.WIDTH, self.oy + Bwbot.HEIGHT)
        # Inner representation of the board
        self.matrix = [[None for _col in xrange(Bwbot.COLS)] \
            for _row in xrange(Bwbot.ROWS)]
        # Sample size. The size in pixels of the square used to sample colors
        self.sample_size = 8
    def toMouseCoordinates(self, pos):
        "Translates grid coordinates to mouse coordinates"
        _x = self.ox + pos[0] * Bwbot.SQUARE_WIDTH + Bwbot.MID_SQUARE_WIDTH
        _y = self.oy + pos[1] * Bwbot.SQUARE_HEIGHT + Bwbot.MID_SQUARE_HEIGHT
        return (_x, _y)
    def click(self, pos):
        "Make a mouse click. Pos especified in grid coordinates"
        _x, _y = self.toMouseCoordinates(pos)
        win32api.SetCursorPos((_x, _y))
        win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, _x, _y, 0, 0)
        win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, _x, _y, 0, 0)
    def dragJewel(self, pos1, pos2):
        "Drags a jewel from pos1 to pos2"
    def wait(self, delay):
        "Do nothing during delay miliseconds"
        _delay = delay / 1000.0
        _ref = time.time()
        while (time.time() - _ref) < _delay:
    def captureBoard(self):
        "Capture and preproceses Bejeweled Blitz board"
        _im = ImageGrab.grab()                           # capture and
        _im = ImageOps.posterize(_im.crop(self.grid), 3) # reduce colors
        return _im
    def buildMatrix(self):
        "Build the matrix of colors representing the board"
        _im = self.captureBoard()
        for _row in range(Bwbot.ROWS):
            for _col in range(Bwbot.COLS):
                _cx = (_col * Bwbot.SQUARE_WIDTH) + Bwbot.MID_SQUARE_WIDTH
                _cy = (_row * Bwbot.SQUARE_HEIGHT) + Bwbot.MID_SQUARE_HEIGHT
                _rect = (_cx, _cy, _cx + self.sample_size, _cy + self.sample_size)
                _sample_im = _im.crop(_rect)
                # store only de predominant color
                self.matrix[_col][_row] = sorted(_sample_im.getcolors())[0][1]
    def evaluatePos(self, pos):
        "Returns True if matrix contains a goal in the specified pos"
        _col, _row = pos
        _val = self.matrix[_col][_row]
        _col_m1 = _col - 1 # col minus 1
        _col_m2 = _col - 2 # col minus 2
        _col_p1 = _col + 1 # col plus 1
        _col_p2 = _col + 2 # col plus 2
        _row_m1 = _row - 1 # row minus 1
        _row_m2 = _row - 2 # row minus 2
        _row_p1 = _row + 1 # row plus 1
        _row_p2 = _row + 2 # row plus 2

        # horizontal
        if _col_m1 >= 0 and _val == self.matrix[_col_m1][_row]:
          if _col_m2 >= 0 and _val == self.matrix[_col_m2][_row]:
            return True
          elif _col_p1 < Bwbot.COLS and _val == self.matrix[_col_p1][_row]:
            return True
        elif _col_p2 < Bwbot.COLS and _val == self.matrix[_col_p1][_row] \
            and _val == self.matrix[_col_p2][_row]:
          return True
        # vertical
        if _row_m1 >=0 and _val == self.matrix[_col][_row_m1]:
          if _row_m2 >= 0 and _val == self.matrix[_col][_row_m2]:
            return True
          elif _row_p1 < Bwbot.ROWS and _val == self.matrix[_col][_row_p1]:
            return True
        elif _row_p2 < Bwbot.ROWS and _val == self.matrix[_col][_row_p1] \
            and _val == self.matrix[_col][_row_p2]:
          return True
        # no goal                
        return False
    def swap(self, pos1, pos2):
        "Swap the values of pos1 and pos2 in the matrix"
        _aux = self.matrix[pos1[0]][pos1[1]]
        self.matrix[pos1[0]][pos1[1]] = self.matrix[pos2[0]][pos2[1]]
        self.matrix[pos2[0]][pos2[1]] = _aux

    def tryMove(self, pos1, pos2):
        "Simulates de move in the matrix and return True if there is a goal"
        self.swap(pos1, pos2)                                  # swap positions
        _goal = self.evaluatePos(pos1) or self.evaluatePos(pos2) # evaluate and
        self.swap(pos1, pos2)                                  # restore matrix
        return _goal
    def play(self):
        "plays the game during"
        _tnow = _tini = time.time()
        while(_tnow - _tini) < self.playtime:
            for _row in xrange(Bwbot.ROWS):
                for _col in xrange(Bwbot.LAST_COL_INDEX):
                    # evaluating pos
                    _pos1 = (_col, _row)
                    # try swap right
                    _pos2 = (_col + 1, _row)
                    _goal = self.tryMove(_pos1, _pos2)
                    # if not goal, try swap down
                    if _row < Bwbot.LAST_ROW_INDEX and not _goal:
                        _pos2 = (_col, _row + 1)
                        _goal = self.tryMove(_pos1, _pos2)
                    # if goal, do the move 
                    if _goal:
                        self.dragJewel(_pos1, _pos2)
            # time elapsed
            _tnow = time.time()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Bejeweled Blitz Bot.")
    parser.add_argument('x', type=int, nargs=1, \
        help='xpos of the Bejeweled Blitz board')
    parser.add_argument('y', type=int, nargs=1, \
        help='ypos of the Bejeweled Blitz board')
    parser.add_argument('--save', nargs=1, required=False, \
        help='capture de screen and saves it to a file')
    args = parser.parse_args()
    if args.save:        
        bwb = Bwbot(args.x[0], args.y[0])

Requisites and Limitations

Future improvements and ToDo List

My only intention was to beat my friends, something that I successfully did. My hi-score is now over 1.000.000 points, much more than any human player could even dream. But the script is far to be perfect. It lacks of a more efficient evaluation routine that selects the best move at each moment, choosing a 5-in-a-row move before the others for example. Another important defect is its inability to detect multipliers and other special jewels.

I am not planning to improve the script, but if you want to improve it, I will appreciate that you email me, just to know how the script evolves.


Snippet: PHP Calling static class methods

Given the example class foo:

class foo {
  public static function myFunc() {
    return "foo - myFunc";

The class name is known in the calling context:

  echo foo::myFunc();

The class name is stored in a php var:

  $class_name = "foo";

  /** PHP 5.2.x */
  echo call_user_func(array($class_name, "myFunc"));  

  /** PHP 5.3.x */
  echo $class_name::myFunc();

More info on call_user_func: http://es.php.net/manual/en/function.call-user-func.php
More info on static keyword: http://es.php.net/manual/en/language.oop5.static.php