Mostrar mensagens com a etiqueta App Development Framework. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta App Development Framework. Mostrar todas as mensagens

domingo, agosto 25, 2019

Android App: Tic-Tac-Toe Game


(Cover page of JME's Nr. 194 edition; 2001-01-15)


Many eons ago (2001 to be precise) I published a paper on the JME (Jornal de Matemática Elementar) edited by SPM - Portuguese Mathematical Society (edited by Prof. Sérgio Macias Marques), regarding the Tic-Tac-Toe game.




At the time I implemented the code in VB with a Game Engine, i.e., game implemented in a Human vs. Computer fashion [(1) the code comments are in Portuguese and (2) it's not even indented; copy pasting ancient code is a bummer! I can't be arsed with doing them both...if you want, do it yourself you lazy sod!]:

VB Code:

Option Explicit

' Desenhar o quadrado.

Private Sub DrawSquare(i As Integer) Select Case Board(i)
Case PlayerX X1(i).Visible = True X2(i).Visible = True
Case PlayerO O(i).Visible = True

End Select End Sub

Inicializar as opções.

Private Sub Form_Load() CenterForm Me Initialize
End Sub

Verificar se alguém ganhou.

Private Function GameOver() As Integer Dim pl As Integer
Dim i As Integer Dim color As Long

pl = Winner()
If pl = PLAYER_NONE Then GameOver = False
Exit Function End If

GameInProgress = False If pl = PlayerX Then
Msg.Caption = "X ganhou." ElseIf pl = PlayerO Then
Msg.Caption = "O ganhou." Else
Msg.Caption = "Empate." End If
Beep

GameOver = True End Function

Inicializar as opções.

Private Sub Initialize()

Inicializar opções iniciais.

SkillLevel = 1 mnuOptionsLevel(SkillLevel).Checked = True

PlayerX = PLAYER_HUMAN PlayerO = PLAYER_COMPUTER
mnuOptionsPlayX.Checked = True

' Começar novo jogo.

StartNewGame End Sub

Fazer o movimento do computador.

Private Sub MakeComputerMove()
Dim Square As Integer

Preparação.

Msg.Caption = "É a vez do computador." Msg.Refresh
MousePointer = vbHourglass

Escolher uma jogada.

Square = PickComputerMove

Seleccionar uma célula.

Board(Square) = NextPlayer

' Actualizar o display.
DrawSquare Square

' Preparar-se para a próxima jogada. NextPlayer = PLAYER_HUMAN MousePointer = vbDefault
End Sub

Jogada do humano.

Private Sub MakeHumanMove() Msg.Caption = "É a sua vez."
End Sub

Private Sub mnuFileExit_Click() Unload Me
End Sub

Seleccionar o nível de dificuldade.

Private Sub mnuOptionsLevel_Click(Index As Integer) mnuOptionsLevel(SkillLevel).Checked = False SkillLevel = Index mnuOptionsLevel(SkillLevel).Checked = True StartNewGame
End Sub

O humano é O.

Private Sub mnuOptionsPlayO_Click() PlayerX = PLAYER_COMPUTER PlayerO = PLAYER_HUMAN mnuOptionsPlayX.Checked = False mnuOptionsPlayO.Checked = True StartNewGame
End Sub

O humano é X.

Private Sub mnuOptionsPlayX_Click() PlayerX = PLAYER_HUMAN PlayerO = PLAYER_COMPUTER
mnuOptionsPlayX.Checked = True mnuOptionsPlayO.Checked = False StartNewGame
End Sub

Preparar-se para receber a próxima jogada.

Private Sub PlayerHasMoved()

Se o computador precisar de jogar, faça-o.

If NextPlayer = PLAYER_COMPUTER Then MakeComputerMove
If GameOver() Then Exit Sub End If

Agora o humano deve jogar.

MakeHumanMove End Sub

O humano está a jogar.

Private Sub Square_Click(Index As Integer)

' Validar se existe um jogo a decorrer. If Not GameInProgress Then Exit Sub

Validar se é o humano a jogar.

If NextPlayer <> PLAYER_HUMAN Then Exit Sub

Validar que ninguém está a ocupar esta célula.

If Board(Index) <> PLAYER_NONE Then Exit Sub

Seleccionar a célula. Board(Index) = NextPlayer DrawSquare Index

If GameOver() Then Exit Sub

Preparar-se para a próxima jogada. NextPlayer = PLAYER_COMPUTER PlayerHasMoved
End Sub

Inicializar o tabuleiro.

Private Sub StartNewGame()
Dim i As Integer

Inicializar o tabuleiro.

For i = 1 To NUM_SQUARES Board(i) = PLAYER_NONE X1(i).Visible = False X2(i).Visible = False O(i).Visible = False
Next i
NextPlayer = PlayerX GameInProgress = True

Iniciar primeira jogada.

PlayerHasMoved End Sub

Retornar o jogador que ganhou. Private Function Winner() As Integer Dim i As Integer

Procurar vitórias nas linhas.

For i = 1 To 7 Step 3
If Board(i) <> PLAYER_NONE Then
If Board(i) = Board(i + 1) And Board(i) = Board(i + 2) Then Winner = Board(i)
Exit Function End If
End If Next i

Procurar vitórias nas colunas.

For i = 1 To 3
If Board(i) <> PLAYER_NONE Then
If Board(i) = Board(i + 3) And Board(i) = Board(i + 6) Then Winner = Board(i)
Exit Function End If

End If Next i

Procurar vitórias nas diagonais.

If Board(1) <> PLAYER_NONE Then
If Board(1) = Board(5) And Board(1) = Board(9) Then Winner = Board(1)
Exit Function End If
End If
If Board(3) <> PLAYER_NONE Then
If Board(3) = Board(5) And Board(3) = Board(7) Then Winner = Board(3)
Exit Function End If
End If

Não há vencedor. Validar se é um empate.

Winner = PLAYER_NONE ' partir do princípio que o jogo ainda não terminou.
For i = 1 To NUM_SQUARES
If Board(i) = PLAYER_NONE Then Exit Function Next i

' É um empate.
Winner = PLAYER_DRAW End Function

O código fonte do nosso “module” é : Option Explicit
Public Const NUM_SQUARES = 9
Public Board(1 To NUM_SQUARES) As Integer Public GameInProgress As Integer

Valores dos jogadores.

Public Const PLAYER_DRAW = -1 Public Const PLAYER_NONE = 0 Public Const PLAYER_HUMAN = 1 Public Const PLAYER_COMPUTER = 2 Public Const NUM_PLAYERS = 2

Public NextPlayer As Integer ' O jogador que se deve mover a seguir.
Public PlayerX As Integer ' Os jogadores que são X/O são
Public PlayerO As Integer ' PLAYER_HUMAN ou PLAYER_COMPUTER.

Public SkillLevel As Integer


Public Function PickComputerMove() Dim pick As Integer

Select Case SkillLevel

Case 1 ' Nível de dificuldade 1

O computador limita-se a efectuar um ' movimento aleatório.


PickComputerMove = MakeRandomMove Exit Function


Case 2 ' Nível de dificuldade 2
pick = MakeWinningMove If pick = 0 Then
pick = MakeSavingMove
Else
PickComputerMove = pick Exit Function
End If

If pick = 0 Then
PickComputerMove = MakeRandomMove Else
PickComputerMove = pick End If


Case 3 'Nível de dificuldade 3

pick = MakeWinningMove


If pick = 0 Then
pick = MakeSavingMove Else
PickComputerMove = pick Exit Function
End If


If pick = 0 Then
pick = MakeSavingMove2 Else
PickComputerMove = pick Exit Function
End If


If pick = 0 Then
PickComputerMove = MakeStandardMove Else
PickComputerMove = pick End If

End Select End Function
Public Function MakeStandardMove() As Integer Dim Square As Integer
Dim pick As Integer

If Board(5) = PLAYER_NONE Then MakeStandardMove = 5
Exit Function

End If


For Square = 1 To 9 Step 2
If Board(Square) = PLAYER_NONE Then MakeStandardMove = Square
Exit Function End If
Next

For Square = 2 To 8 Step 2
If Board(Square) = PLAYER_NONE Then MakeStandardMove = Square
Exit Function

End If Next
End Function
Public Function MakeWinningMove() As Integer Dim computersquares As Integer
Dim freesquare As Integer
Dim Square As Integer Dim i As Integer

‘ Procurar uma jogada que provoque uma vitória em linha.


For Square = 1 To 7 Step 3

computersquares = 0
freesquare = 0

For i = 0 To 2

Select Case Board(Square + i)

Case PLAYER_COMPUTER
computersquares = computersquares + 1

Case PLAYER_NONE
freesquare = Square + i End Select
Next
If computersquares = 2 And freesquare <> 0 Then MakeWinningMove = freesquare
Exit Function

End If Next

‘ Procurar uma jogada que provoque uma vitória em coluna.


For Square = 1 To 3

computersquares = 0
freesquare = 0

For i = 0 To 6 Step 3

Select Case Board(Square + i)

Case PLAYER_COMPUTER
computersquares = computersquares + 1 Case PLAYER_NONE
freesquare = Square + i End Select
Next
If computersquares = 2 And freesquare <> 0 Then MakeWinningMove = freesquare
Exit Function End If
Next

‘ Procurar uma jogada que provoque uma vitória na diagonal.


computersquares = 0
freesquare = 0

For i = 1 To 9 Step 4 Select Case Board(i)
Case PLAYER_COMPUTER
computersquares = computersquares + 1 Case PLAYER_NONE
freesquare = i End Select

Next

If computersquares = 2 And freesquare <> 0 Then MakeWinningMove = freesquare
Exit Function End If

computersquares = 0
freesquare = 0

For i = 3 To 7 Step 2

Select Case Board(i)
Case PLAYER_COMPUTER
computersquares = computersquares + 1 Case PLAYER_NONE
freesquare = i End Select

Next

If computersquares = 2 And freesquare <> 0 Then MakeWinningMove = freesquare
Exit Function End If

End Function
Public Function MakeSavingMove() As Integer Dim humansquares As Integer
Dim freesquare As Integer
Dim Square As Integer Dim i As Integer

'Procurar uma jogada que impeça uma vitória em linha.


For Square = 1 To 7 Step 3

humansquares = 0
freesquare = 0

For i = 0 To 2
Select Case Board(Square + i)
Case PLAYER_HUMAN
humansquares = humansquares + 1 Case PLAYER_NONE
freesquare = Square + i End Select

Next

If humansquares = 2 And freesquare <> 0 Then MakeSavingMove = freesquare
Exit Function End If

Next

'Procurar uma jogada que impeça uma vitória em coluna.

For Square = 1 To 3

humansquares = 0
freesquare = 0

For i = 0 To 6 Step 3

Select Case Board(Square + i)
Case PLAYER_HUMAN
humansquares = humansquares + 1 Case PLAYER_NONE
freesquare = Square + i

End Select Next
If humansquares = 2 And freesquare <> 0 Then MakeSavingMove = freesquare
Exit Function End If

Next

'Procurar uma jogada que impeça uma vitória na diagonal.


humansquares = 0
freesquare = 0
For i = 1 To 9 Step 4 Select Case Board(i)
Case PLAYER_HUMAN
humansquares = humansquares + 1 Case PLAYER_NONE
freesquare = i End Select

Next

If humansquares = 2 And freesquare <> 0 Then MakeSavingMove = freesquare
Exit Function End If

humansquares = 0
freesquare = 0
For i = 3 To 7 Step 2 Select Case Board(i)
Case PLAYER_HUMAN
humansquares = humansquares + 1 Case PLAYER_NONE
freesquare = i End Select

Next

If humansquares = 2 And freesquare <> 0 Then MakeSavingMove = freesquare
Exit Function End If

End Function

Public Function MakeSavingMove2() As Integer Dim pick As Integer

Select Case Board(5) = PLAYER_HUMAN Case True

If Board(1) = PLAYER_HUMAN Then

If Board(7) = PLAYER_NONE Then pick = 7
ElseIf Board(4) = PLAYER_NONE Then pick = 4
End If

ElseIf Board(3) = PLAYER_HUMAN Then

If Board(9) = PLAYER_NONE Then pick = 9
ElseIf Board(6) = PLAYER_NONE Then pick = 6
End If

ElseIf Board(7) = PLAYER_HUMAN Then

If Board(1) = PLAYER_NONE Then pick = 1
ElseIf Board(4) = PLAYER_NONE Then pick = 4
End If

ElseIf Board(9) = PLAYER_HUMAN Then

If Board(3) = PLAYER_NONE Then pick = 3
ElseIf Board(6) = PLAYER_NONE Then pick = 6
End If End If

End Select MakeSavingMove2 = pick End Function
Public Function MakeRandomMove() As Integer

Dim pick As Integer Dim Square As Integer

Do Until pick <> 0 Square = Int(Rnd * 8) + 1
If Board(Square) = PLAYER_NONE Then pick = Square Loop
MakeRandomMove = pick End Function
Public Function CenterForm(frm As Form) frm.Left = (Screen.Width - frm.Width) / 2 frm.Top = (Screen.Height - frm.Height) / 2
End Function


This time around I wanted to convert this code to a more modern platform: Android/Java.

I won't bother you with Android/Java code. It follows the code above (also with a Game Engine implemented), but migrated to a different coding paradigm (namely OOP). A few screens:



My 4-year old kid is having a blast "playing" it...

domingo, março 04, 2018

Samsung Gear S3: My First Wearable APP Using the Tizen OS by MySelfie


Samsung Gear S3 showing what it can do...

0 - Install the Development Environment with the SDK Tools (Tizen Studio 2.2 with IDE installer, Tizen Extension SDK, Samsung Accessory SDK, and the Samsung Rich Notification SDK):

1 - Install the Tizen Studio IDE;

2 - In the Tizen Studio package manager, install everything for the 2.3.2 wearable;



Tizen SDK Tools:



3 - Install the Samsung Certificate Extension;


4 - Creating the App; I chose the watch because I like all things that glitter...

Some C code spinets of the Watch Gear S3 App:




5 - Running the emulator a gazillion times (from 11:28 in the morning until 2 o'clock in the afternoon...Wife calling for lunch...) until the frigging thing was ok:

(Linux emulator kernel loading) 




6 - Find the IP on your Samsung Gear S3 given by your router:



7 - Run sdb connect 192.168.1.128 on the command prompt;

8 - Turn "debugging mode" on Gear S3;

9 - After the connection is established, use "device manager" on Tools in the Tizen Studio and install it. If you want to go the way of deploying the tpk, you can install the Watch App to a connected device with the tpk ("watchmaantao-1.0.0-x86.tpk"; (vide next screenshot). In the device list, right-click the device to which you want to install an application, and select Install app. In the file dialog, select the "watchmaantao-1.0.0-x86.tpk" file to install and click OK.


The application is installed and launched on the Samsung Gear S3:

(final version; app already with all the builtin bell and whistles that should come with every Watch app; the photos before this one belonged to the barebone version of my Watch App)



domingo, dezembro 03, 2017

Thinking in Code: "Antao's Tetris Ported from Python"


If you want to have a go at my version of the game, you can install it from here at the Google Play Store (Python version running on the Chrome Browser here).

The above app layout was modelled after my own gameboy:


Compare both pictures above. Was I successful? You tell me!

Any idiot can code, and many do. The skill is in understanding the maths and algorithms (speaking as an idiot who codes).

Lazy (or to be fair perhaps just poorly informed) people have taken to use the word "coding" as a synonym for software development. Actually, coding is tricky in and of itself (read the C++11 reference manual and tell me it ain't so) but it is still only the "bricklaying" part of construction, so to speak. Programming is creative and exploratory and stimulating to the imagination. It's a different way of thinking that serves people well in all sorts of life situations outside of software. So as a way to develop skills in logical thinking, problem solving and invention it's very useful. Not every child will go on to write software as an adult but having a basic understanding and insight into systems has to be a fundamental part of every child's education I say. If you don't understand the systems controlling your life how can you ever use them to your best advantage?

Actually the basics of code are not directly computer related at all. A simple binary device is an on-off light switch which says something like when I am 'on' my user is at home and when I am 'off' my user is away. A second light switch allows this develop into when both lights are on my user is awake and when only one light is on my user is asleep and so on. The code is what you make the light sequences mean. The same use is made of morse code, an abacus, flag signals , hand gestures, anything that can be logically explained to mean something for each different state. I think most children develop logic rather more quickly than we believe possible, and the only problem with computers is demystifying their apparent complexity when they are only ever very fast at very simple arithmetic and being given very precise instructions about what to do with all that speed adding numbers together.

I have to say that contemporary programming is much poorer than it was ten years ago, and I believe that is because we have provided far too many lazy ways to do complex things without making students graft hard at the basics and that is where I do believe improvements are to be found. However , ask a 20 something year old to sit there all day and write "select from where" ad infinitum and it doesn't take long to realise why younger people are not motivated by "heavy lifting" programming...

Programming is easy. Like any language, it is easy once you know how to talk, and think it. We have to stop thinking of programming as mathematics, science or engineering. It needs to be taught as a language skill. The moment we treat it as a language skill, the traditional barriers begin to fall. It becomes acceptable for parents of both sexes, at any age, and any religion. I don't think there are many people who would object to their kids having the opportunity to learn multiple languages, as early as possible, if they could. I don't think I have ever heard anyone object to a child being able to speak multiple languages. Once academics can get over their misconceptions on what they need programming to be, we could all move on, and start teaching our kids how to make them look stupid.

Once you can program in one language it is easy enough with the help of Google to pick up other languages. I've spent quite a lot of time programming in languages that I didn't know. In my experience what people find harder to pick up is the logic and the ability to break down the task required into the small logical steps required to make a computer perform the task. I started out very young; I learned to program because I wanted to play games and back then that meant typing in program listings from computer magazines. They never worked properly so I worked out how to fix them. Do a few of those, and you soon enough prick up how to program. After that new languages are easy. The only time I struggled a little bit was in making the leap from procedural coding to object-orientated as that's a bit of a paradigm shift.

I used to be a software developer, and have developed lots of software for the last 25 years. Believe me when I tell you that 99% of the job, has nothing to do with either mathematics, or science. The only reason you, and most other people, who probably don't program, think it does, is because everyone you ever see, working in this branch, drives that, image. Syntax is language, but expression is also language. Being able to understand, and then describe a process, is absolutely, a pure language skill. Most of the work ever done, is little more than connecting dots, and building logical expressions with existing tools and libraries. The language behind every programming language, is pretty much universal. once you have learned how to express yourself, and approach problems, you can easily skip to any language, and syntax, with a short learning curve. Ask a database developer, or a network developer how much math and science they need, to do their job. Not a lot, that is for sure. some do, but in general, very little real math is required, and it sure ain't rocket science these days. of course further skills are required in some areas, depending on what you want to do, but I would argue with anyone that this job is generally NOT science anymore. I would also argue that that math is absolutely NOT a prerequisite to be able to program today. If you are a good communicator, and you have normal math skills, you can be a great programmer. If IMO, we exclude the wrong people from programming. We need less geeks with personality disorders in this branch, and more talented communicators. To get that, we need to rethink who we think are going to be good at the job, and include a completely different skill set, as prerequisite to what makes a programmer today.

I still sometimes dream of creating some games and getting a bit of cash from it. I still tinker with Android (a few more examples of coding in Android here), and have a few half-finished games that I did in XNA for the Xbox Live Indie store. I think part of the problem for games now, or at least console games, is the publisher and retailer taking so much of the pie. Digital distribution is great in principle, but on console at least, unless you're using Xbox Live Gold or PSN+ or whatever there seems to be absolutely no discount - in fact a lot of games are more expensive than their retail counterpart (which is expensive and fickle enough). If a developer could get a lot more of the money without going through a publisher or retailer, I'd be all for it. At the moment, the vast, vast majority of games I buy are old ones (2 years old or more) that you just can't buy new, so it's all second hand. This extra money, if going to the developers themselves, would give them a lot more freedom and make conditions at them a lot more tolerable.

As for "thinking in code", I used to be the same. I could have code swimming around in my head for hours, and to an extent still do (only now, with me doing a lot of QLIK work too, a lot of mine is maps, statistics and graphs). The experience hasn't put me off playing games, but given me more respect for the people that make them - and then, sometimes, less respect when they get things wrong, especially design-wise (Dead Rising's save system, I'm looking at you).


NB: The core of the engine is the following class.

class TetrisGame implements PosPaintable {
    Grid playfield;
    TetrisPiece piece;
    int curScore;
    int px,py; 
    intNonZero testNonZero;
    intOr      putDown;

    public TetrisGame(int xs,int ys,boolean randomCrap)
{
playfield=new Grid(xs,ys,1);
clear();
piece = new TetrisPiece();
py=0;
px=(xs-piece.sizex())/2;
testNonZero = new intNonZero();
putDown = new intOr();
curScore=0;
}
    
    void clear()
{
playfield.fill(0,0,playfield.sizex(),playfield.sizey(),0);
}

    public void paint(Graphics g, int x,int y)
{
g.drawRect(x,y,2+playfield.sizex()*10,2+playfield.sizey()*10);
for(int j=0;j
    for(int i=0;i
if((playfield.grid[i][j]!=0) ||
   ((i>=px) && (i
    (j>=py) && (j
    (piece.grid[i-px][j-py]!=0)))
    g.fillRect(3+x+i*10,3+y+j*10,8,8);
        }


    public boolean step()
{
if(playfield.compare(piece,px,py+1,testNonZero))
    {
    // Put down the piece
    playfield.put_on(piece,px,py,putDown);
    // Clear out full lines
    for(int j=playfield.sizey()-1;j>=0;j--)
while(testFullLine(j)==true)
    {
    deleteLine(j);
    // Simple scoring function
    curScore+=10;
    }

            // Put on a new piece
    piece = new TetrisPiece();
    py=0;
    px=(playfield.sizex()-piece.sizex())/2;
    if(playfield.compare(piece,px,py,testNonZero))
return true;
            }
        py++;
return false;
        }

    private boolean testFullLine(int y)
{
for(int i=0;i
    if(playfield.grid[i][y]==0)
return false;
        return true;
}

    private void deleteLine(int y)
{
for(int j=y;j>0;j--)
            for(int i=0;i
playfield.grid[i][j]=playfield.grid[i][j-1];
        for(int i=0;i
    playfield.grid[i][0]=0;
        }

// Data-returning methods

    public int score()
{
return curScore;
}

// Game-play interface methods

    public void move_left(int i)
{
if(playfield.compare(piece,px-i,py,testNonZero))
    return; // Should we throw an exception here?
        px-=i;
}
    public void move_left()
{
        move_left(1);
}

    public void move_right(int i)
{
if(playfield.compare(piece,px+i,py,testNonZero))
    return; // Should we throw an exception here?
        px+=i;
}
    public void move_right()
{
        move_right(1);
}

    public void rotate_cw()
{
TetrisPiece test=new TetrisPiece(piece);
        test.rotate_cw();
if(!playfield.compare(test,px,py,testNonZero))
    piece=test;
        }

    public void rotate_ccw()
{
TetrisPiece test=new TetrisPiece(piece);
        test.rotate_ccw();
if(!playfield.compare(test,px,py,testNonZero))
    piece=test;
        }

    public void drop()
{
while(!playfield.compare(piece,px,py+1,testNonZero))
    py++;
        }







quarta-feira, novembro 29, 2017

Argghh, Just One More Go Mum: "Exploring Tetris On My Own"



"but...but...the Spectrum is for educational purposes Mum! Honest, look it comes with a game called Chess and Scrabble!"


Ping pong, Pacman, Defender, Elite, Sabre Wolf, Manic Miner, Jetset Willy, Tekken Sensible Soccer, Mario, Sonic, NHL, PES, Fifa, COD, Forza, Dirt, Bubble Burst, Candy Crush, Donald Trump, Mickey Mouse... Never affected me in anyway whatsoever, thinking back, I still haven't handed in my Technical Drawing homework due in 1985. "Argghh, just one more go mum."


I grew up with a commodore 64 and ZX Spectrum. I use to play games obsessively, especially Elite but it never impacted my social and family life. Apart from a bit of cop killing and drug, slave and alien artifact trading here and there I have grown up to be a well rounded member of society

I seem to be the only person in public spaces sitting staring into space, thinking without a device of some kind. Looking at other people using their phones, it seems that we're at the point where they're virtually programmed to reach for their phones as soon at every conceivable opportunity without even thinking about it. Often going through the same cycle, checking text, mail, news. Seems the whole concept of independent reflection has gone out the window.

In fairness sitting by yourself and staring into space in public was considered a bit odd even before computer games and smart phones. I feel the same way about people who can only run if they do so with headphones, that on some level they are saying 'Running is crap and I need this ear candy to make it bearable'.

It just depends if you have an addictive nature or not. Everyone's brains are wired differently. Our brains goe haywire with dopamine and serotonin whenever we receive a positive feedback. Doesn't matter if it's a blackjack, some under 20 team in Uzbekistan winning, landing a dragon punch in SFII, scoring a Tetris, looking at porn, reading or writing reviews... I'm just a born addict and will keep on doing that thing, usually addictive stuff.

Tetris. Ah. Those were the days...It's a beautiful game except when the pieces start falling faster. I always played with the sound off to reduce the stress but the pile-up . . . (deep breath) . . . .is always on the horizon . . . (deep breath) . . . .(deep breath) . . . .(deep breath) . . . .

What follows is something I concocted to while away the time when I was dealing with tossers I won't say where...

NB: Next step is porting the the following Tetris Engine to Android, which is already under way. The hard part is finished (e.g., collision detection, rotation matrices, and so forth).

Python Tetris Engine:


import simplegui
import random

BACKGROUND_COLOR = "White"
WIDTH = 700
HEIGHT = 600
BOARD_WIDTH = 10
BOARD_HEIGHT = 20
BOARD_UPPER_LEFT = (300, 75)
BLOCK_SIZE = 25
START_POSITION = (3, 0)
gameInPlay = False
score = 0
time = 0
vel_y = 0
next_piece = None
speed = 15

# global game objects
piece_O = None
piece_I = None
piece_T = None
piece_L = None
piece_J = None
piece_S = None
piece_Z = None
piece_B = None
board = None
pieces = None
pos = [10, 10]
current_piece = None

# test shape of one block
SHAPE_ONE_BLOCK = ["X"]
SHAPE_ONE_BLOCK.append("B")
SHAPE_ONE_BLOCK.append("Green")

SHAPE_O_0 = [["O", "X", "X", "O"],
             ["O", "X", "X", "O"],
             ["O", "O", "O", "O"],
             ["O", "O", "O", "O"]]
temp = []
temp.append(SHAPE_O_0)
temp.append(SHAPE_O_0)
temp.append(SHAPE_O_0)
temp.append(SHAPE_O_0)
SHAPE_O = []
SHAPE_O.append(temp)
SHAPE_O.append("O")
SHAPE_O.append("Yellow")

SHAPE_I_0 = [["O", "O", "O", "O"],
             ["X", "X", "X", "X"],
             ["O", "O", "O", "O"],
             ["O", "O", "O", "O"]]
SHAPE_I_1 = [["O", "X", "O", "O"],
             ["O", "X", "O", "O"],
             ["O", "X", "O", "O"],
             ["O", "X", "O", "O"]]
SHAPE_I_2 = [["O", "O", "O", "O"],
             ["O", "O", "O", "O"],
             ["X", "X", "X", "X"],
             ["O", "O", "O", "O"]]
SHAPE_I_3 = [["O", "O", "X", "O"],
             ["O", "O", "X", "O"],
             ["O", "O", "X", "O"],
             ["O", "O", "X", "O"]]
temp = []
temp.append(SHAPE_I_0)
temp.append(SHAPE_I_1)
temp.append(SHAPE_I_2)
temp.append(SHAPE_I_3)
SHAPE_I = []
SHAPE_I.append(temp)
SHAPE_I.append("I")
SHAPE_I.append("LightBlue") ##ADD8E6

SHAPE_T_0 = [["O", "X", "O"],
             ["X", "X", "X"],
             ["O", "O", "O"]]
SHAPE_T_90 = [["O", "X", "O"],
              ["X", "X", "O"],
              ["O", "X", "O"]]
SHAPE_T_180 = [["O", "O", "O"],
               ["X", "X", "X"],
               ["O", "X", "O"]]
SHAPE_T_270 = [["O", "X", "O"],
               ["O", "X", "X"],
               ["O", "X", "O"]]
temp = []
temp.append(SHAPE_T_0)
temp.append(SHAPE_T_90)
temp.append(SHAPE_T_180)
temp.append(SHAPE_T_270)
SHAPE_T = []
SHAPE_T.append(temp)
SHAPE_T.append("T")
SHAPE_T.append("Purple")

SHAPE_L_0 = [["O", "O", "X"],
             ["X", "X", "X"],
             ["O", "O", "O"]]
SHAPE_L_1 = [["X", "X", "O"],
             ["O", "X", "O"],
             ["O", "X", "O"]]
SHAPE_L_2 = [["O", "O", "O"],
             ["X", "X", "X"],
             ["X", "O", "O"]]
SHAPE_L_3 = [["O", "X", "O"],
             ["O", "X", "O"],
             ["O", "X", "X"]]
temp = []
temp.append(SHAPE_L_0)
temp.append(SHAPE_L_1)
temp.append(SHAPE_L_2)
temp.append(SHAPE_L_3)
SHAPE_L = []
SHAPE_L.append(temp)
SHAPE_L.append("L")
SHAPE_L.append("Orange")

SHAPE_J_0 = [["X", "O", "O"],
             ["X", "X", "X"],
             ["O", "O", "O"]]
SHAPE_J_1 = [["O", "X", "O"],
             ["O", "X", "O"],
             ["X", "X", "O"]]
SHAPE_J_2 = [["O", "O", "O"],
             ["X", "X", "X"],
             ["O", "O", "X"]]
SHAPE_J_3 = [["O", "X", "X"],
             ["O", "X", "O"],
             ["O", "X", "O"]]
temp = []
temp.append(SHAPE_J_0)
temp.append(SHAPE_J_1)
temp.append(SHAPE_J_2)
temp.append(SHAPE_J_3)
SHAPE_J = []
SHAPE_J.append(temp)
SHAPE_J.append("J")
SHAPE_J.append("DarkBlue") #00008B

SHAPE_S_0 = [["O", "X", "X"],
             ["X", "X", "O"],
             ["O", "O", "O"]]
SHAPE_S_1 = [["X", "O", "O"],
             ["X", "X", "O"],
             ["O", "X", "O"]]
SHAPE_S_2 = [["O", "O", "O"],
             ["O", "X", "X"],
             ["X", "X", "O"]]
SHAPE_S_3 = [["O", "X", "O"],
             ["O", "X", "X"],
             ["O", "O", "X"]]
temp = []
temp.append(SHAPE_S_0)
temp.append(SHAPE_S_1)
temp.append(SHAPE_S_2)
temp.append(SHAPE_S_3)
SHAPE_S = []
SHAPE_S.append(temp)
SHAPE_S.append("S")
SHAPE_S.append("Green")

SHAPE_Z_0 = [["X", "X", "O"],
             ["O", "X", "X"],
             ["O", "O", "O"]]
SHAPE_Z_1 = [["O", "X", "O"],
             ["X", "X", "O"],
             ["X", "O", "O"]]
SHAPE_Z_2 = [["O", "O", "O"],
             ["X", "X", "O"],
             ["O", "X", "X"]]
SHAPE_Z_3 = [["O", "O", "X"],
             ["O", "X", "X"],
             ["O", "X", "O"]]
temp = []
temp.append(SHAPE_Z_0)
temp.append(SHAPE_Z_1)
temp.append(SHAPE_Z_2)
temp.append(SHAPE_Z_3)
SHAPE_Z = []
SHAPE_Z.append(temp)
SHAPE_Z.append("Z")
SHAPE_Z.append("Red")

class Board():
 
    def __init__(self, upper_left_corner):
        self.upper_left = upper_left_corner
        self.upper_right = (upper_left_corner[0] + (BLOCK_SIZE * BOARD_WIDTH), self.upper_left[1])
        self.lower_right = (upper_left_corner[0] + (BLOCK_SIZE * BOARD_WIDTH), self.upper_left[1] + (BLOCK_SIZE * BOARD_HEIGHT))
        self.lower_left = (self.upper_left[0], self.upper_left[1] + (BLOCK_SIZE * BOARD_HEIGHT))

        # setup grid
        self.grid = []
        for i in range(BOARD_HEIGHT):
            row = [None] *  BOARD_WIDTH
            self.grid.append(row)
     
    def draw(self, canvas):
        for c in range(BOARD_WIDTH):
            for r in range(BOARD_HEIGHT):
                if self.grid[r][c] != None:
                    block = self.grid[r][c]
                    draw_block(canvas, (self.upper_left[0] + (BLOCK_SIZE * c), self.upper_left[1] + (BLOCK_SIZE * r)), 1, BACKGROUND_COLOR, block.get_color())
        canvas.draw_polygon((self.upper_left, self.upper_right, self.lower_right, self.lower_left), 1, "Black")

    def fill(self, pos, piece):
        self.grid[pos[0]][pos[1]] = piece
     
    def empty(self, pos):
        self.grid[pos[0]][pos[1]] = None
     
    def isEmpty(self, pos):
        return (self.grid[pos[0]][pos[1]] == None)
 
    def isRowFull(self, row):
        for col in range(BOARD_WIDTH):
            if self.grid[row][col] == None:
                return False
        return True
 
class Piece():
    def __init__(self, shape, pos = None):
        self.color = shape[2]
        self.type = shape[1]
        self.shapes = shape[0]
        self.shape_index = 0
        self.shape = self.shapes[0]
        self.width = len(self.shape[0])
        self.height = len(self.shape)
        if pos != None:
            self.x = pos[0]
            self.y = pos[1]
     
    def __str__(self):
        s = ""
        s += "Piece (shape = " + self.type + ", color = " + self.color + ")\n"
        s += " (width = " + str(self.width) + ", height = " + str(self.height) + ")\n"
        for row in self.shape:
            value = ""
            for col in row:
                value += col
            s += value + "\n"         
        return s
 
    def isValidRotation(self, direction):
        if direction == "CCW":
            if self.shape_index == len(self.shapes) - 1:
                idx = 0
            else:
                idx = self.shape_index + 1
        else:
            if self.shape_index == 0:
                idx = len(self.shapes) - 1
            else:
                idx = self.shape_index - 1
        temp_piece = Piece([[self.shapes[idx]], "temp", "Blue"], (self.x, self.y))

        canRotate = True
     
        #Check top side
        for row in range(len(temp_piece.shape)):
            for col in range(len(temp_piece.shape[row])):
                if temp_piece.shape[row][col] == "X":
                    if (temp_piece.y + row) < 0:
                        canRotate = False
                     
        #check down side
        for row in range(len(temp_piece.shape) - 1, -1, -1):
           for col in range(len(temp_piece.shape[row])):
                if temp_piece.shape[row][col] == "X":
                    if (temp_piece.y + row) >= BOARD_HEIGHT:
                        canRotate = False
                     
        #check right side
        for row in range(len(temp_piece.shape)):
            if "X" in temp_piece.shape[row]:
                reversed_row = list(temp_piece.shape[row])
                reversed_row.reverse()
                offset = reversed_row.index("X")
                if (temp_piece.x + temp_piece.width - offset) > BOARD_WIDTH:
                    canRotate = False
     
        #check left side
        for row in range(len(temp_piece.shape)):
            if "X" in temp_piece.shape[row]:
                offset = temp_piece.shape[row].index("X")
                if (temp_piece.x + offset) < 0:
                    canRotate = False
                 
        #check for overlaps
        for row in range(len(temp_piece.shape)):
            for col in range(len(temp_piece.shape[row])):
                if temp_piece.shape[row][col] == "X":
                    if temp_piece.x + col >= BOARD_WIDTH or temp_piece.y + row >= BOARD_HEIGHT:
                        canRotate = False
                    elif temp_piece.x + col < 0 or temp_piece.y + row < 0:
                        canRotate = False
                    elif not board.isEmpty((temp_piece.y + row, temp_piece.x + col)):
                        canRotate = False     
     
        return canRotate

    def rotate_ccw(self):
        if self.shape_index == len(self.shapes) - 1:
            self.shape_index = 0
        else:
            self.shape_index += 1
        self.shape = self.shapes[self.shape_index]
        self.width = len(self.shape[0])
        self.height = len(self.shape)
 
    def rotate_cw(self):
        if self.shape_index == 0:
            self.shape_index = len(self.shapes) - 1
        else:
            self.shape_index -= 1
        self.shape = self.shapes[self.shape_index]
        self.width = len(self.shape[0])
        self.height = len(self.shape)

    def get_color(self):
        return self.color
 
    def get_width(self):
        return self.width
 
    def get_height(self):
        return self.height
 
    def move_left(self):
        canMove = True
         
        for row in range(len(self.shape)):
            if "X" in self.shape[row]:
                offset = self.shape[row].index("X")
                if (self.x + offset) - 1 < 0:
                    canMove = False
                elif not board.isEmpty((self.y + row, self.x + offset - 1)):
                    canMove = False

        if canMove:
            self.x -= 1
         
    def move_right(self):
            canMove = True
         
            for row in range(len(self.shape)):
                if "X" in self.shape[row]:
                    reversed_row = list(self.shape[row])
                    reversed_row.reverse()
                    offset = reversed_row.index("X")
                    if (self.x + self.width - offset) + 1 > BOARD_WIDTH:
                        canMove = False
                    elif not board.isEmpty((self.y + row, self.x + self.width - offset)):
                        canMove = False

            if canMove:
                self.x += 1
     
    def move_up(self):
            canMove = True
         
            for row in range(len(self.shape)):
                for col in range(len(self.shape[row])):
                    if self.shape[row][col] == "X":
                        if (self.y + row - 1) < 0:
                            canMove = False
                        elif not board.isEmpty((self.y + row - 1, self.x + col)):
                            canMove = False

            if canMove:
                self.y -= 1       
 
    def move_down(self):
            canMove = True
         
            for row in range(len(self.shape) - 1, -1, -1):
                for col in range(len(self.shape[row])):
                    if self.shape[row][col] == "X":
                        if (self.y + row + 1) >= BOARD_HEIGHT:
                            canMove = False
                        elif not board.isEmpty((self.y + row + 1, self.x + col)):
                            canMove = False

            if canMove:
                self.y += 1
             
            return canMove
 
    def get_blocks(self):
        positions = []
        for row in range(len(self.shape)):
            for col in range(len(self.shape[row])):
                if self.shape[row][col] == "X":
                    positions.append((self.x + col, self.y + row))
        return positions
     
    def draw(self, canvas):
        for row in range(len(self.shape)):
            pos_y = BOARD_UPPER_LEFT[1] + (BLOCK_SIZE * self.y) + (BLOCK_SIZE * row)
            for col in range(len(self.shape[row])):
                pos_x = BOARD_UPPER_LEFT[0] + (BLOCK_SIZE * self.x) + (BLOCK_SIZE * col)
                if self.shape[row][col] == "X":
                    draw_block(canvas, (pos_x, pos_y), 1, BACKGROUND_COLOR, self.color)
                else:
                    draw_block(canvas, (pos_x, pos_y), 1, BACKGROUND_COLOR, BACKGROUND_COLOR)
 
    def get_position(self):
        return (self.x, self.y)
 
def draw_block(canvas, pos, line_width = 1, line_color = "Black", fill_color = None):
    block = [(pos[0], pos[1]), (pos[0] + BLOCK_SIZE, pos[1]), (pos[0] + BLOCK_SIZE, pos[1] + BLOCK_SIZE), (pos[0], pos[1] + BLOCK_SIZE)]
    canvas.draw_polygon(block, line_width, line_color, fill_color)

def draw(canvas):
    global time
    global gameInPlay
 
    time += 1
 
    if gameInPlay:
     
        if (time % speed == 0):
            piece_moved = current_piece.move_down()

            if current_piece.get_position() == START_POSITION:

                gameInPlay = False


            if (not piece_moved):
                lockdown_handler()
                create_new_piece_handler()

        for i in range(BOARD_HEIGHT):
            check_full_rows_handler()
         
        # draw next piece
        positions = next_piece.get_blocks()

        color = next_piece.get_color()
     
        pos[0] = BOARD_UPPER_LEFT[0] + (BOARD_WIDTH * BLOCK_SIZE)
        pos[1] = BOARD_UPPER_LEFT[1] + 60
     
        canvas.draw_text("Next Piece", [pos[0] + 20, pos[1] - 40], 20, "Black")

        for i in positions:
            draw_block(canvas, [pos[0] + (BLOCK_SIZE * i[0]) - 50, pos[1] + (BLOCK_SIZE * i[1])], 1, BACKGROUND_COLOR, color)
         
    current_piece.draw(canvas)
    board.draw(canvas)
 
    # Game Over Check
    if not gameInPlay:
        width = frame.get_canvas_textwidth("Game Over", 50)
        canvas.draw_text("Game Over", (BOARD_UPPER_LEFT[0] + (BOARD_WIDTH * 0.50 * BLOCK_SIZE) - (width * 0.50), BOARD_UPPER_LEFT[0] + (BOARD_HEIGHT * 0.5)), 50, "Black")
 
    # Instructions
    offset = 275
    text_score = "0000" + str(score)
    text_score = text_score[-4:]
    canvas.draw_text(text_score, (BOARD_UPPER_LEFT[0], BOARD_UPPER_LEFT[1] - 25), 50, "Blue")
    canvas.draw_text("left arrow = move piece left", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 20), 15, "Black")
    canvas.draw_text("right arrow = move piece right", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 40), 15, "Black")
    canvas.draw_text("down arrow = move piece down", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 60), 15, "Black")
    canvas.draw_text("'z' = turn piece counter clockwise", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 80), 15, "Black")
    canvas.draw_text("up arrow = turn piece counter clockwise", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 100), 15, "Black")
    canvas.draw_text("'x' = turn piece clockwise", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 120), 15, "Black")
    canvas.draw_text("'n' = new game", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 140), 15, "Black")
    canvas.draw_text("'space' = drops the piece", (BOARD_UPPER_LEFT[0] - offset, BOARD_UPPER_LEFT[1] + 160), 15, "Black")

def init():
    global board
    board = Board(BOARD_UPPER_LEFT)
 
    global piece_O
    piece_O = Piece(SHAPE_O)
 
    global piece_I
    piece_I = Piece(SHAPE_I)
 
    global piece_T
    piece_T = Piece(SHAPE_T)
 
    global piece_L
    piece_L = Piece(SHAPE_L)
 
    global piece_J
    piece_J = Piece(SHAPE_J)
 
    global piece_S
    piece_S = Piece(SHAPE_S)
 
    global piece_Z
    piece_Z = Piece(SHAPE_Z)
 
    global piece_B
    piece_B = Piece(SHAPE_ONE_BLOCK)
 
 
    global current_piece
    global pieces
 
    pieces = [SHAPE_O, SHAPE_I, SHAPE_T, SHAPE_L, SHAPE_J, SHAPE_S, SHAPE_Z]
 
    current_piece = Piece(random.choice(pieces), START_POSITION)
    global next_piece
    next_piece = Piece(random.choice(pieces), START_POSITION)
 
    global gameInPlay
    gameInPlay = True
 
    global score
    score = 0
 
    global speed
    speed = 15

def move_left_handler():
    current_piece.move_left()

def move_right_handler():
    current_piece.move_right()

def move_up_handler():
    current_piece.move_up()

def move_down_handler():
    current_piece.move_down()

def rotate_ccw_handler():
    if current_piece.isValidRotation("CCW"):
        current_piece.rotate_ccw()
 
def rotate_cw_handler():
    if current_piece.isValidRotation("CW"):
        current_piece.rotate_cw()
 
def keydown_handler(key):
    if key == simplegui.KEY_MAP["left"]:
        current_piece.move_left()
    if key == simplegui.KEY_MAP["right"]:
        current_piece.move_right()
    if key == simplegui.KEY_MAP["down"]:
        current_piece.move_down()
    if key == simplegui.KEY_MAP["up"]:
        if current_piece.isValidRotation("CCW"):
            current_piece.rotate_ccw()
    if key == simplegui.KEY_MAP["z"]:
        if current_piece.isValidRotation("CCW"):
            current_piece.rotate_ccw()
    if key == simplegui.KEY_MAP["x"]:
        if current_piece.isValidRotation("CW"):
            current_piece.rotate_cw()
    if key == simplegui.KEY_MAP["space"]:
        drop_piece_handler()
    if key == simplegui.KEY_MAP["n"]:
        new_game_handler()

def check_full_rows_handler():
    row_to_pop = None
    for row in range(BOARD_HEIGHT):
        if board.isRowFull(row):
            row_to_pop = row
    if row_to_pop != None:
        board.grid.pop(row_to_pop)
        row = [None] *  BOARD_WIDTH
        board.grid.insert(0, row)
        global score
        score += 1
        global speed
        if (score % 5) == 0:
            speed -= 2
 
def lockdown_handler():
    positions = current_piece.get_blocks()
    color = current_piece.get_color()
    for i in positions:
        board.fill([i[1], i[0]], Piece(['X', 'B', color]))

def drop_piece_handler():
    while (current_piece.move_down()):
        pass
    lockdown_handler()
    create_new_piece_handler()
         
def new_game_handler():
    init()

def create_new_piece_handler():
    global current_piece
    global next_piece
 
    current_piece = next_piece

    next_piece = Piece(random.choice(pieces), START_POSITION)

def check_ccw_handler():
    print current_piece.isValidRotateCCW()
             
frame = simplegui.create_frame("Antao", WIDTH, HEIGHT)
frame.set_canvas_background(BACKGROUND_COLOR)

frame.set_keydown_handler(keydown_handler)

frame.set_draw_handler(draw)

frame.start()

init()