'Tetris clone in a console window
'logic adapted from:
'https://github.com/curaai00/Tetris 


$ filename "AsciTerix.exe"
'uses rtl32
'uses rtl64

uses corewin 
uses console

indexbase 0

type CONSOLE_CURSOR_INFO ' cci  
    dword dwSize
    bool  bVisible 
end type 

#define keypressed(k)  GetAsyncKeyState(k) 'and 0x7fff

sub setcolor(int fg, bg)
  SetConsoleTextAttribute (ConsOut, fg+bg*16)
end sub

sub locate (int row,int col, optional int cursor_visible=0,int shape=12)
  CONSOLE_CURSOR_INFO cci
  SetPos(col-1,row-1)
  cci.bVisible = cursor_visible
  cci.dwSize   = shape
  SetConsoleCursorInfo(ConsOut, cci)
end sub

sub display(int col, row, string txt, optional int cursor_visible=0,int shape=12) 
  locate(row, col, cursor_visible, shape) 
  print txt
end sub  

sub AdaptConsole()
   'Set size to 80,24
   dim ConsoleWindow as SMALL_RECT
   ConsoleWindow.Right = 80 : ConsoleWindow.Bottom = 24
   SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), 1, ConsoleWindow)
   'Fix size
   sys hConsMenu=GetSystemMenu(GetConsoleWindow(), 0) 
   DeleteMenu(hConsMenu, &HF000, 0)
   DeleteMenu(hConsMenu, &HF020, 0)
   DeleteMenu(hConsMenu, &HF030, 0)   
end sub

extern lib "winmm.dll"
! mciSendString "mciSendStringA"    '4
end extern

int with_sound
int midi_len

sub play_midi(string fName)
   int err_code
   string temp=space 255
   string fn=fName ".mid"
   with_sound=1
   err_code=mciSendString("open " fn " type sequencer alias midifile", 0,0,0)
   if err_code !=0 then
     with_sound=0
   else  
     mciSendString("status midifile length", temp, 255,0)
     midi_len=val(temp)
     mciSendString("play midifile", 0,0,0)
   end if  
end sub

====================================
'msvcrt.dll

' To seed the pseudorandom-number 
sub Randomize()
   srand GetTickCount()
end sub 

'function rand()
'creates random number max 32767
====================================

SetConsoleTitle "AsciTerix - Tetris clone in Oxygenbasic"

AdaptConsole

'int shapes[7 * 4 * 4 * 4] = {
int shapes[] = {
'Z
    0,0,0,0,
    0,1,1,0,
    0,0,1,1,
    0,0,0,0,

    0,0,1,0,
    0,1,1,0,
    0,1,0,0,
    0,0,0,0,

    0,0,0,0,
    0,1,1,0,
    0,0,1,1,
    0,0,0,0,

    0,0,1,0,
    0,1,1,0,
    0,1,0,0,
    0,0,0,0,
'S
    0,0,0,0,
    0,0,1,1,
    0,1,1,0,
    0,0,0,0,

    0,1,0,0,
    0,1,1,0,
    0,0,1,0,
    0,0,0,0,

    0,0,0,0,
    0,0,1,1,
    0,1,1,0,
    0,0,0,0,

    0,1,0,0,
    0,1,1,0,
    0,0,1,0,
    0,0,0,0,
'I
    0,0,0,0,
    1,1,1,1,
    0,0,0,0,
    0,0,0,0,

    0,1,0,0,
    0,1,0,0,
    0,1,0,0,
    0,1,0,0,

    0,0,0,0,
    0,0,0,0,
    1,1,1,1,
    0,0,0,0,

    0,1,0,0,
    0,1,0,0,
    0,1,0,0,
    0,1,0,0,
'J
    0,0,0,0,
    0,1,0,0,
    0,1,1,1,
    0,0,0,0, 

    0,0,0,0,
    0,1,1,0,
    0,1,0,0,
    0,1,0,0,

    0,0,0,0,
    1,1,1,0,
    0,0,1,0,
    0,0,0,0,

    0,0,1,0,
    0,0,1,0,
    0,1,1,0,
    0,0,0,0,
'l
    0,0,0,0,
    0,0,1,0,
    1,1,1,0,
    0,0,0,0,

    0,1,0,0,
    0,1,0,0,
    0,1,1,0,
    0,0,0,0,

    0,0,0,0,
    0,1,1,1,
    0,1,0,0,
    0,0,0,0,

    0,0,0,0,
    0,1,1,0,
    0,0,1,0,
    0,0,1,0,


'O
    0,0,0,0,
    0,1,1,0,
    0,1,1,0,
    0,0,0,0,

    0,0,0,0,
    0,1,1,0,
    0,1,1,0,
    0,0,0,0,

    0,0,0,0,
    0,1,1,0,
    0,1,1,0,
    0,0,0,0,

    0,0,0,0,
    0,1,1,0,
    0,1,1,0,
    0,0,0,0,
'T
    0,0,0,0,
    0,1,0,0,
    1,1,1,0,
    0,0,0,0,

    0,1,0,0,
    0,1,1,0,
    0,1,0,0,
    0,0,0,0,

    0,0,0,0,
    1,1,1,0,
    0,1,0,0,
    0,0,0,0,

    0,1,0,0,
    1,1,0,0,
    0,1,0,0,
    0,0,0,0
}

function get_shapes(int w,x,y,z) as int
    return shapes[w*4*4*4 + x*4*4 + y*4 + z]
end function


'set the board
% BOARD_WIDTH = 14
% BOARD_HEIGHT = 23
% BOARD_LEFT_EDGE = 0 
% BOARD_RIGHT_EDGE = 13
% BOARD_DOWN_EDGE = 23

type PIECE
    int block_x
    int block_y
    int shape
    int next_shape
    int direction
end type


int board[BOARD_HEIGHT * BOARD_WIDTH]
macro get_board(x,y) board[(x)*BOARD_WIDTH + (y)]

int edge[BOARD_WIDTH]
int next_shape

int level=1
int time_delay=3
int first_row=1
int points=1

Randomize()
   
function first_block() as sys ptr
    sys pPiece=getmemory(sizeof(PIECE))
    PIECE block at pPiece

    'state == 0 is touched base 1 is falling
    block.shape = mod(rand(),7)
    block.block_x = 5
    block.block_y = 0
    block.direction = 0

    return block
end function

function make_block(PIECE* previous) as sys ptr
    sys pPiece=getmemory(sizeof(PIECE))
    PIECE block at pPiece

    'state == 0 is touched base 1 is falling
    block.shape = next_shape
    next_shape = mod(rand(),7)
    block.block_x = 5
    block.block_y = 0
    block.direction = 0
    
    points++

    'eliminate block before
    freememory(previous)

    return block
end function

sub fill_board()
    int i

    'fill the edge of width
    for i = 0 to BOARD_HEIGHT-1
        get_board(i,0) = 1
        get_board(i,BOARD_WIDTH - 1) = 1
    next
    'fill the array's base
    for i = 1 to BOARD_WIDTH-1
        get_board(BOARD_HEIGHT - 1,i) = 1
    next
end sub

sub show_board()
    int i, j
    int j1

    'print board
    for i = 0 to BOARD_HEIGHT-1
        j1=0

        for j = 0 to BOARD_WIDTH-1
            if get_board(i,j) = 1 then
                if j = BOARD_LEFT_EDGE and i = BOARD_DOWN_EDGE then
                    setcolor(4,12) ' Red, LightRed
                    display(5+j1,1+i, "OO")
                    setcolor(7,0)
                elseif j = BOARD_RIGHT_EDGE and i = BOARD_DOWN_EDGE then
                    setcolor(4,12) ' Red, LightRed
                    display(5+j1,1+i, "OO")
                    setcolor(7,0)
                elseif j = BOARD_LEFT_EDGE or j = BOARD_RIGHT_EDGE then
                    setcolor(14,6) ' Yellow, Brown
                    display(5+j1,1+i, "__")
                    setcolor(7,0)
                elseif i = BOARD_DOWN_EDGE-1 then
                    setcolor(14,6) ' Yellow, Brown
                    display(5+j1,1+i, "| ")
                    setcolor(7,0)
                else
                    setcolor(4,12) ' Red, LightRed
                    display(5+j1,1+i, "OO")
                    setcolor(7,0)
                end if
            else
                display(5+j1,1+i, "  ")
            end if
            j1+=2    
        next j
    next i
end sub

sub show_score()
    display(45,  7,      "Level  : " level)
    display(45,  8,      "Points : " points)
end sub

' show help for keys
sub show_help()
    int start = 10

    display(45, start,   "Keys:")   
    display(45, start+1, "Left   - Move left")
    display(45, start+2, "Up     - Rotate")
    display(45, start+3, "Right  - Move right")
    display(45, start+4, "Down   - Move down")
    display(45, start+6, "Space  - Drop")
    display(45, start+7, "P      - Pause")
    display(45, start+8, "ESC, Q - Quit")
end sub

'show next shape
sub show_nextshape()
    int i, j, j1

    display(45,2, "Next Shape")

    for i=0 to 3
       display(45,3+i, "        ")
    next
       
    for i = 0 to 3
        j1=0
        for j = 0 to 3
            if get_shapes(next_shape,0,i,j) = 1 then
                setcolor(4,12) ' Red, LightRed
                display(45+j1,3+i, "OO")
                setcolor(7,0)
            else
                display(45+j1,3+i, " ")
            end if
            j1+=2
        next j
    next i

end sub

function checkdown(PIECE* block) as int
    int x, y, xx, yy, shape, direction
    int edge[] = { 0,0,0,0 }

    shape = block.shape
    direction = block.direction

    for x = 0 to 3
        for y = 3 to 1 step -1
            if get_shapes(shape,direction,y,x) = 1 then
                edge[x] = y
                exit for
            end if
        next y
    next x
 
    'if next place is filled return 0; else return 1
    for x = 0 to 3
        if edge[x] != 0 then
            yy=block.block_y + edge[x] + 1
            xx=block.block_x + x        
            if get_board(yy,xx) = 1 then return 0
        end if
    next x
    return 1
end function


'check the next shape place
function checkshape(PIECE* block) as int
    int x, y, xx, yy, shape, direction, next_direction
    int i, flag, plus_y = 0, plus_x = 0
    int NextShape[4*4] = { 0 }
    int edge[] = { -1, -1, -1, -1 }

    shape = block.shape
    direction = block.direction
    next_direction = block.direction + 1

    next_direction = mod(next_direction, 4)

    'current-shape - next_shape
    for y = 0 to 3
        for x = 0 to 3
            if get_shapes(shape, direction, y, x) != 1 then
                NextShape[y*4 +x] = get_shapes(shape, next_direction, y, x)
            end if
        next x
    next y

    '0 is check left 1 is check right 2 is check down 3is check up
    for flag = 0 to 3
        'make edge by flag
        select case flag

        case 0:
            for y = 0 to 3
                for x = 0 to 3
                    if NextShape[y*4 +x] = 1 then
                        edge[y] = x
                        exit for
                    end if
                next x
            next y

        case 1:
            for y = 0 to 3
                for x = 3 to 1 step -1
                    if NextShape[y*4 +x] = 1 then
                        edge[y] = x
                        exit for
                    end if

                next x
            next y

        case 2:
            for x = 0 to 3
                for y = 3 to 1 step -1
                    if NextShape[y*4 +x] = 1 then
                        edge[x] = y
                        exit for
                    end if
                next y
            next x

        case 3:
            for x = 0 to 3
                for y = 0  to 3
                    if NextShape[y*4 +x] = 1 then
                        edge[x] = y
                        exit for
                    end if
                next y
            next x

        end select

        'check edge by flag
        for i = 0 to 3
            if edge != 0 then
                if i < 2 then
                    yy=block.block_y + i
                    xx=block.block_x + edge[i]
                    if get_board(yy,xx) = 1 then return 0   
                else
                    yy=block.block_y + edge[i]
                    xx=block.block_x + i
                    if get_board(yy,xx) = 1 then return 0    
                end if
            end if
        next i
        'clear edge
        for i = 0 to 3
            edge[i] = -1
        next i
    next flag
    
    return 1
end function

function checkright(PIECE* block) as int
    int x, y, xx, yy, shape, direction
    int edge[] = { -1,-1,-1,-1 }

    shape = block.shape
    direction = block.direction

    for y = 0 to 3
        for x = 3 to 1 step -1
            if get_shapes(shape, direction, y, x) = 1 then
                edge[y] = x
                exit for
            end if
        next x
    next y

    'if next place is filled return 0; else return 1
    for y = 0 to 3
        if edge[y] != -1 then
             yy=block.block_y + y
             xx=block.block_x + edge[y] + 1
            if get_board(yy, xx) = 1 then return 0    
        end if
    next y
    return 1
end function

function checkleft(PIECE* block) as int
    int x, y, xx, yy, shape, direction
    int edge[] = { -1,-1,-1,-1 }

    shape = block.shape
    direction = block.direction

    for y = 0 to 3
        for x = 0 to 3
            if get_shapes(shape, direction, y, x) = 1 then
                edge[y] = x
                exit for
            end if
        next x
    next y

    'if next place is filled return 0; else return 1
    for y = 0 to 3
        if edge[y] != -1 then
            yy=block.block_y + y
            xx=block.block_x + edge[y] - 1
            if get_board(yy,xx) = 1 then return 0    
        end if
    next y
    return 1
end function

sub control_shape(PIECE* block)
    if keypressed(VK_UP) then
            if checkshape(block) = 1 then
                block.direction++
                block.direction = mod(block.direction,4)
            end if
    elseif keypressed(VK_DOWN) then
            if checkdown(block) = 1 then
                block.block_y++
            end if    
    elseif keypressed(VK_RIGHT) then
            if checkright(block) = 1 then
                block.block_x++
            end if
    elseif keypressed(VK_LEFT) then
            if checkleft(block) = 1 then
                block.block_x--
            end if
    elseif keypressed(32) then 'space        
            while checkdown(block)
                block.block_y++
            wend
            points+=4
            Sleep(200)
    elseif keypressed(VK_P) then
           string ans
           display (35,20, "")
           'waitkey()
           display (35,20, "Pause. Press C to continue ...")
loop1:     
           ans=getkey()
           if lcase(chr(ans))<>"c" then goto loop1           
           display(35,20, "                                          ")
    elseif keypressed(VK_Q) then
fin:
           display(50, 20, "Terminate ...")
           mciSendString("close midifile", 0,0,0)
           terminate
           Sleep(1000)
           ExitProcess(1)
    elseif keypressed(VK_ESCAPE) then
           goto fin           
    end if        
end sub

sub input_block(PIECE* block)
    int x, y, shape, direction

    shape = block.shape
    direction = block.direction

    'shape's fill part to move board
    for y = 0 to 3
        for x = 0 to 3
            if get_shapes(shape,direction,y,x) = 1 then
                get_board(block.block_y + y, block.block_x + x) = 1
            end if
        next x
    next y
end sub

sub remove_block(PIECE* block)
    int x, y, shape, direction

    shape = block.shape
    direction = block.direction

    if checkdown(block) != 0 then
        for y = 0 to 3
            for x = 0 to 3
                if get_shapes(shape,direction,y,x) = 1 then
                    get_board(block.block_y + y, block.block_x + x) = 0
                end if
            next x
        next y
    end if
end sub

'if something in the line return 1
function Check_line(int line) as int
    int i

    for i = 1 to BOARD_WIDTH - 3
        if get_board(line, i) = 1 then
            return 1
        end if
    next i
    return 0
end function

'find the filled line in board
function clear_line() as int
    int x, y
    int count = 0

    for y = BOARD_HEIGHT-2 to 1 step -1
        if Check_line(y) then
            count = 0
            for x = 1 to BOARD_WIDTH-2
                if get_board(y,x) = 1 then
                    count += 1
                end if
            next x
            if count = BOARD_WIDTH - 2 then
                return y
            end if
        end if
    next y

    return 0
end function

sub shift_board(int y)
    int x
    static int c_l
    points+=20
    c_l+=1
    if c_l=4 then 
       c_l=0 : level++
       select case level
       case 4
          mciSendString("close midifile", 0,0,0)
          play_midi("Silence.mid")
       case 5
          mciSendString("close midifile", 0,0,0)
          play_midi("Tetris2")
       case 7
          mciSendString("close midifile", 0,0,0)
          play_midi("Tetris3")
       case 10 
          display(10,15, "Cool!  Now it is")
          display(10,16, "getting exciting!")  
          mciSendString("close midifile", 0,0,0)
          play_midi("Tetris")                       
       end select   
    end if      
    
    while y > 0
        for x = 1 to BOARD_WIDTH - 2
            get_board(y, x) = get_board(y - 1, x)
        next x
        y--
    wend
end sub

'if game is over return 1 else return 0
function game_over() as int
    int i, t

    for i = 0 to 3
        if get_board(first_row,5 + i) = 1 then
            setcolor(10, 0) 'LightGreen, Black
            for t=1 to 7            
            display(50, 20, "Game over",1)
            sleep(200)                        
            display(50, 20, "         ",1)
            sleep(200)
            next t
            display(50, 20, "Game over",1)
            setcolor(7,0)
            mciSendString("close midifile", 0,0,0)
            return 1
        end if
    next i
    return 0
end function


sub run_game(PIECE* block)
    int i, line, unit
    string temp=space 255

    show_nextshape()
    show_score()
    show_help()
        
    while (1)
            if level>10 then
              i=0
              time_delay=1
              first_row=2
            elseif level>6
              i=0
              time_delay=2   
            end if          

        while (1)
             if with_sound then      
             mciSendString("status midifile position", temp, 255,0)
             unit=val(temp) 
             if unit=midi_len then
                 mciSendString("seek midifile to start", NULL, 0, NULL)
                 mciSendString("play midifile", 0,0,0)    
             end if
             end if            

            'to move block fast
            i++
            if i = time_delay then
                i = 0
                block.block_y++
            end if

            control_shape(block)
            'fill that block to array
            input_block(block)
            show_board()
            show_score()
            
            'change place(side) and direction
            'if next place(down) is filled break while and make new block
            if checkdown(block) = 0 then
                exit while
            end if    

            'remove current block
            remove_block(block)
            Sleep(100)
            
        wend

        while (1)
            line = clear_line()
            if line = 0 then
                exit while
            end if    
            shift_board(line)
        wend

        if game_over() then
            exit while
        end if
        
        'make new block
        &block = make_block(block)
        next_shape = mod(rand(),7)

        if block.shape = next_shape and next_shape != 6 then
            next_shape += 1
        end if        
        show_nextshape()
            
    wend

end sub

sub main()
    PIECE *block
    int n
    string s="s c i T e r i x"
    
    cls
    &block = first_block()
    fill_board()
    show_board()    
    
    play_midi("Tetris1")

    for n=1 to 6
      s ="A " + s
      setcolor(9+n,0)  
      display(50-n, 8+n, s)
    next
    setcolor(7,0)
    display(50, 21, "                   ")
    display(50, 20, "Enter to Start Game")
    waitkey
    for n=1 to 6  
      display(50-n, 8+n, "                            ")
    next
    display(50, 20, "                      ")
    
    run_game(block)
end sub


main()

string ans
loop2:
display(50,22, "Enter Q to end ...")
ans=getkey()
if lcase(chr(ans))<>"q" then goto loop2

