Author Topic: Text Mode Squash  (Read 6336 times)

0 Members and 1 Guest are viewing this topic.

Mike Lobanovsky

  • Guest
Text Mode Squash
« on: September 11, 2014, 04:11:28 PM »
Here's my little tribute to retro gaming.

Use <- and -> arrows to play, and Esc or [X], to quit. Note also that this time you won't be able to resize, minimize, or maximize the console window no matter what you do. :)

Code: [Select]
  INCLUDEPATH "$\inc\"
  '$FILENAME  "O2Squash.exe"
  'INCLUDE    "Rtl32.inc"
  INCLUDE "Console.inc"
 
  ! FUNCTION GetTickCount     LIB "KERNEL32.DLL" () AS DWORD
  ! SUB Sleep                 LIB "KERNEL32.DLL" (DWORD mSecs)
  ! SUB GetConsoleCursorInfo  LIB "KERNEL32.DLL" (DWORD hConsoleOutput, DWORD lpConsoleCursorInfo)
  ! SUB SetConsoleCursorInfo  LIB "KERNEL32.DLL" (DWORD hConsoleOutput, DWORD lpConsoleCursorInfo)
  ! SUB SetConsoleWindowInfo  LIB "KERNEL32.DLL" (DWORD hConsoleOutput, DWORD bAdsolute, DWORD lpConsoleWindow)
  ! FUNCTION GetConsoleWindow LIB "KERNEL32.DLL" () AS DWORD
  ! FUNCTION GetSystemMenu    LIB "USER32.DLL"   (DWORD hWnd, DWORD bRevert) AS DWORD
  ! FUNCTION GetAsyncKeyState LIB "USER32.DLL"   (DWORD dwKeyKode) AS DWORD
  ! SUB DeleteMenu            LIB "USER32.DLL"   (DWORD hMenu, DWORD uPosition, DWORD uFlags)
 
  TYPE CONSOLE_CURSOR_INFO
    DWORD dwSize
    DWORD bVisible
  END TYPE
 
  DIM ConsoleCursorInfo AS CONSOLE_CURSOR_INFO
  DIM ConsoleWindow AS SMALL_RECT
  DIM hMenu AS DWORD
 
  #DEFINE VK_ESCAPE           27
  #DEFINE VK_LEFT             37
  #DEFINE VK_RIGHT            39
  #DEFINE TICKS()             GetTickCount()
  #DEFINE CLOCK()             ROUND(TICKS() / 1000)
  #DEFINE DROWSE()            Sleep(10)
  #DEFINE DISPLAY(A, B, C)    SetPos(B - 1, A): PRINT C
  #DEFINE GETKEYB(A)          GetAsyncKeyState(A)
  #DEFINE CURSOFF()           GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), @ConsoleCursorInfo): ConsoleCursorInfo.bVisible = 0: SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), @ConsoleCursorInfo)
  #DEFINE SETCONS()           ConsoleWindow.Right = 18: ConsoleWindow.Bottom = 18: SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), 1, @ConsoleWindow): SetConsoleTitle("DO IT!")
  #DEFINE FIXCONS()           hMenu = GetSystemMenu(GetConsoleWindow(), 0): DeleteMenu(hMenu, &HF000, 0): DeleteMenu(hMenu, &HF020, 0): DeleteMenu(hMenu, &HF030, 0)
 
  ' ========= HERE WE GO! =========
  DIM AS SYS X, H
  DIM AS SYS Y, V
  DIM AS SYS O, P
  DIM AS SYS R, S
  DIM AS SYS T1 = TICKS(), T2, T3
  DIM AS STRING T4

  CURSOFF()
  SETCONS()
  FIXCONS()
 
  CLS
  DISPLAY(1, 1, "===================")
  DISPLAY(3, 4, "OXYGEN SQUASH")
  DISPLAY(5, 1, "===================")
  DISPLAY(8, 2, "BROUGHT TO YOU BY")
  DISPLAY(10, 5, "THE WATCHER")
  DISPLAY(18, 2, "ENTER TO START...")
  WAITKEY()

  START:
  CLS
 
  X = 10: IF (TICKS() AND 9) > 4 THEN H = 1 ELSE H = -1
  Y = 15: V = -1
  R = 10: S = 0
  T3 = CLOCK()
 
  DISPLAY(16, R, "==")
  WHILE Y <= 16
  T4 = "TIME: " & CLOCK() - T3
  O = X: P = Y
  X = X + H: Y = Y + V
  DISPLAY(18, 1, "SCORE: " & S)
  DISPLAY(18, 19 - LEN(T4), T4)
  DISPLAY(Y, X, "o")
  DISPLAY(P, O, " ")
 
  IF GETKEYB(VK_ESCAPE) THEN GOTO FINISH
  IF GETKEYB(VK_LEFT) <> 0 AND R >= 1 THEN
  P = R
  R = R - 2
  IF R < 1 THEN R = 1
  DISPLAY(16, P, "  ")
  DISPLAY(16, R, "==")
  ELSE IF GETKEYB(VK_RIGHT) <> 0 AND R <= 17 THEN
  P = R
  R = R + 2
  IF R > 19 THEN R = 19
  DISPLAY(16, P, "  ")
  DISPLAY(16, R, "==")
  END IF
 
  IF X + H < 1 OR X + H > 19 THEN H = -H
  IF Y + V < 1 THEN V = -V
  IF Y + V > 15 AND X + H >= R AND X + H <= R + 1 THEN
  V = -V
  S = S + 1
  IF (X + H = R AND H > 0) OR (X + H = R + 1 AND H < 0) THEN H = -H
  END IF
  WHILE T2 - T1 < 50
  T2 = TICKS()
  DROWSE()
  WEND
  T1 = TICKS()
  WEND
  GOTO START
 
  FINISH:
  END
  ' ========= THAT'S IT! ==========



@Charles:

I'm seeing two strange glitches with Oxygen PRINT in this extremely simple script:

1. If not ROUND()-ed, GetTickCount() / 1000 - T3 to which line 86 resolves by the preprocessor would often print TIME: -1 which it shouldn't do under any circumstances as per the given program flow. I've seen a similar glitch with GetTickCount in thinBasic too where it is a language keyword. Why is that?

2. Un-ROUND()-ed GetTickCount() / 1000 - T3 would sometimes also produce weird TIME: -???.< hieroglyphics. Why is that?

I'm not seeing any such glitches under FBSL's BASIC where PRINT is built around C language printf().

.
« Last Edit: September 11, 2014, 04:18:21 PM by Mike Lobanovsky »

Charles Pegge

  • Guest
Re: Text Mode Squash
« Reply #1 on: September 11, 2014, 09:15:09 PM »
Thanks Mike,

I think the problem may be caused by a combination of precedence bracketing and type conversion.

print GetTickCount() / 1000 - T3

solution:

explicit brackets around the print expression:

print ( GetTickCount() / 1000 - T3 )

JRS

  • Guest
Re: Text Mode Squash
« Reply #2 on: September 11, 2014, 10:59:01 PM »
Mike,

Here is the SB CIO extension module. If you're working with SB and need basic console mode screen control this is a handy ext.

Code: Text
  1. '
  2. ' This program lists all the possible console character
  3. ' colors on a windows console. This program can not
  4. ' be executed under UNIX
  5. '
  6. import cio.bas
  7.  
  8. cio::SetColor FWhite
  9. cio::cls
  10. cio::SetTitle "Testing console colors"
  11. for i=1 to 255
  12.   cio::gotoxy +(i \ 16) * 4 , +(i % 16) * 2
  13.   cio::gotoxy( (i \ 16) * 4 , +(i % 16) * 2 )
  14.   cio::gotoxy (i \ 16) * 4 , +(i % 16) * 2
  15.   cio::SetColor (i)
  16.   j = i
  17.   if i < 100 then j = "0" & j
  18.   print j
  19. next i
  20. cio::SetColor FWhite
  21. cio::SetCursor 0
  22. i = cio::getch()
  23.  


.

Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #3 on: September 12, 2014, 02:21:24 AM »
solution:
explicit brackets around the print expression:

Nah, doesn't help. -1 remains.

And what about the -???.< stuff? Have you ever seen it?

Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #4 on: September 12, 2014, 02:32:50 AM »
Here is the SB CIO extension module.

John,

Oxygen's Console.inc offers SETPOS and COLOR console-mode keywords to do exactly what's being done in your SB example. When I say "retro", "retro" is what I mean -- no graphics, no color, no anything, just you and letters. :)


(Charles, how could you? It should've been COLOUR! :D )

Charles Pegge

  • Guest
Re: Text Mode Squash
« Reply #5 on: September 12, 2014, 05:52:05 AM »
Mike,

Can you give me a small example, reproducing the print anomaly pls?

PS: color - shorter spelling preferred :)

Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #6 on: September 12, 2014, 12:21:04 PM »
Charles,

Please change line 68 of the script to verbatim

Code: [Select]
T4 = "TIME: " & (GetTickCount() / 1000 - T3)
as you suggested, or even to

Code: [Select]
T4 = "TIME: " & STR(GetTickCount() / 1000 - T3)
to be even more precise, and let the "ball" fall through down without kicking it back. You will see TIME: -1 instead of the expected TIME: 0 almost every time when the game is reset for a new round as shown in the picture below. The TIME: -???.< glitch is very difficult to reproduce due to its seldom and totally random occurence but I assure you I wasn't seeing things that weren't there.


(Following numerous demands of our francophone friends, FBSL swallows several interpretations of its ADDRESSOF operator: ADRESSEOF, for literates, and ADRESSOF, for hopeless illiterates. But when they also asked me for a possibility to have its operand enclosed in optional parentheses, I said "enough of that crap" for fear lest next time they would be asking for a legit COULEUR function or something.)

.
« Last Edit: September 12, 2014, 12:44:23 PM by Mike Lobanovsky »

Charles Pegge

  • Guest
Re: Text Mode Squash
« Reply #7 on: September 12, 2014, 04:38:13 PM »
Thanks Mike,

The bad behaviour, I find, is due to comparing integer T3 with the expression
getTickCount()/1000. which returns a float.

When assigning a float value to an integer, Oxygen will automatically round up or down to the nearest integer value.

One solution is to dim T4 as an integer, T4=GetTickCount()/1000 and then separately compose a display string TS ="TIME: " & T4
« Last Edit: September 12, 2014, 04:45:28 PM by Charles Pegge »

Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #8 on: September 12, 2014, 10:13:38 PM »
Hi Charles,

Thanks for looking into the problem.

As far as script usability is concerned, I'm pretty happy with the current ROUND() function that does the trick for me because I'd rather not introduce a separate explicit variable into the sources for compatibility with other languages that wouldn't require such a hack.

On the other hand, the situation leaves me with a somewhat uneasy feeling about the evaluator refusing such a trivial thing as adding a temp var for me transparently. I'm doing an elementary arith operation of subtracting 1 from 1.5 and what it actually does instead of humbly obeying my orders is require that I declare a variable and round my 1.5 up or down, otherwise it sabotages the expression with a wrong answer. And what if I have a dozen arith and trig operators in my one-liner that deals concurrently with doubles, singles, longs, words and bytes?

I assure you that following the existing program flow there cannot be a situation when T3 (even rounded up at the start of current game round) would be larger than GetTickCount / 1000 (even rounded down in the current loop) by a value of 1 to yield a mathematically wrong result of -1. GetTickCount / 1000 would always be equal to, or larger than, T3 because whatever the rounding rules are, they are common to both parts of the expression, and because the current GetTickCount is always greater than it was when T3 was calculated and rounded.

If you can describe such a situation to me, please do so. Otherwise the evaluator should be subject to an unscheduled round of brutal vivisection for what it did to me. :)

Charles Pegge

  • Guest
Re: Text Mode Squash
« Reply #9 on: September 12, 2014, 10:46:13 PM »
Hi Mike,

Supposing T3 is assigned 1.5. This is rounded up to integer 2.

Supposing the next GetTickCount()/1000 is 1.6

The next T4 will evaluate 1.6 - 2. Result:  -0.4

Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #10 on: September 12, 2014, 11:15:29 PM »
Hehe, no!  :P

Now show me, following your logic, how -.4 can become -1 if 1.5 was rounded to 2.

Is the evaluator using floor() for its rounding operations? Why should it do that? Who told it to? No, it isn't, othrwise 1.5 would never become 2.

Your move, sir! :)

Charles Pegge

  • Guest
Re: Text Mode Squash
« Reply #11 on: September 12, 2014, 11:41:31 PM »
Aha!

In the T4 expression, an integer (CPU) division is performed, for some reason. - Could be a dodgy optimisation. This truncates (rather than rounding up) to produce -1

#show T4 = "TIME: " & GetTickCount() / 1000 - T3

You get a fractional result by dotting 1000.0

#show T4 = "TIME: " & GetTickCount() / 1000. - T3




Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #12 on: September 13, 2014, 12:27:45 AM »
OK, I see your point.

But, Charles, this is natural e.g. in C where / is ambiguous enough to denote both floating-point and integer division depending on the type of divisor. But BASIC has an unambiguous integer division backslash, and by the same token, its forward slash should denote an unambiguous floating-point division irrespective of the divisor data type.

Any objections?

BTW using a backslash in this expression yields mysterious values that I fail to interpret altogether...

Charles Pegge

  • Guest
Re: Text Mode Squash
« Reply #13 on: September 13, 2014, 12:30:40 AM »
Yes, I agree Mike. That integer division should be a float division. It appears to be switched during the expression analysis involving mixed string and numeric expressions.

Mike Lobanovsky

  • Guest
Re: Text Mode Squash
« Reply #14 on: September 13, 2014, 01:47:57 AM »
Got it.

I'm glad you've been able to isolate the problem. And sorry for stringing you up on the weekend. Mr. bugmagnet who's registered on this forum doesn't show up so it seems I've taken over his usual functions. :)