asked claude
"write a calculator app in purebasic using shunting yard and rpn eval function"
Code: Select all
; Calculator using Shunting Yard Algorithm and RPN Evaluation
; Supports +, -, *, /, ^, and parentheses
Structure Token
value.s
type.i ; 0=number, 1=operator, 2=left paren, 3=right paren
EndStructure
Procedure.i GetPrecedence(op.s)
Select op
Case "+", "-"
ProcedureReturn 1
Case "*", "/"
ProcedureReturn 2
Case "^"
ProcedureReturn 3
EndSelect
ProcedureReturn 0
EndProcedure
Procedure.i IsLeftAssociative(op.s)
If op = "^"
ProcedureReturn #False
EndIf
ProcedureReturn #True
EndProcedure
Procedure.s ShuntingYard(expression.s)
NewList output.s()
NewList opStack.s()
Protected i, len, char.s, token.s, isNumber
len = Len(expression)
i = 1
While i <= len
char = Mid(expression, i, 1)
; Skip whitespace
If char = " "
i + 1
Continue
EndIf
; Check if it's a number or decimal point
If (char >= "0" And char <= "9") Or char = "."
token = ""
While i <= len
char = Mid(expression, i, 1)
If (char >= "0" And char <= "9") Or char = "."
token + char
i + 1
Else
Break
EndIf
Wend
AddElement(output())
output() = token
Continue
EndIf
; Check for operators
If FindString("+-*/^", char, 1)
While ListSize(opStack()) > 0
LastElement(opStack())
If opStack() <> "(" And
((IsLeftAssociative(char) And GetPrecedence(char) <= GetPrecedence(opStack())) Or
(Not IsLeftAssociative(char) And GetPrecedence(char) < GetPrecedence(opStack())))
AddElement(output())
output() = opStack()
DeleteElement(opStack())
Else
Break
EndIf
Wend
AddElement(opStack())
opStack() = char
EndIf
; Left parenthesis
If char = "("
AddElement(opStack())
opStack() = char
EndIf
; Right parenthesis
If char = ")"
While ListSize(opStack()) > 0
LastElement(opStack())
If opStack() <> "("
AddElement(output())
output() = opStack()
DeleteElement(opStack())
Else
DeleteElement(opStack())
Break
EndIf
Wend
EndIf
i + 1
Wend
; Pop remaining operators
While ListSize(opStack()) > 0
LastElement(opStack())
AddElement(output())
output() = opStack()
DeleteElement(opStack())
Wend
; Build result string
Protected result.s = ""
ForEach output()
If result <> ""
result + " "
EndIf
result + output()
Next
ProcedureReturn result
EndProcedure
Procedure.d EvaluateRPN(rpn.s)
NewList stack.d()
Protected token.s, i, len, pos, a.d, b.d, result.d
len = Len(rpn)
i = 1
token = ""
While i <= len
Protected char.s = Mid(rpn, i, 1)
If char = " "
If token <> ""
If FindString("+-*/^", token, 1) And Len(token) = 1
; It's an operator
If ListSize(stack()) >= 2
LastElement(stack())
b = stack()
DeleteElement(stack())
LastElement(stack())
a = stack()
DeleteElement(stack())
Select token
Case "+"
result = a + b
Case "-"
result = a - b
Case "*"
result = a * b
Case "/"
If b <> 0
result = a / b
Else
result = 0
EndIf
Case "^"
result = Pow(a, b)
EndSelect
AddElement(stack())
stack() = result
EndIf
Else
; It's a number
AddElement(stack())
stack() = ValD(token)
EndIf
token = ""
EndIf
Else
token + char
EndIf
i + 1
Wend
; Handle last token
If token <> ""
If FindString("+-*/^", token, 1) And Len(token) = 1
If ListSize(stack()) >= 2
LastElement(stack())
b = stack()
DeleteElement(stack())
LastElement(stack())
a = stack()
DeleteElement(stack())
Select token
Case "+"
result = a + b
Case "-"
result = a - b
Case "*"
result = a * b
Case "/"
If b <> 0
result = a / b
Else
result = 0
EndIf
Case "^"
result = Pow(a, b)
EndSelect
AddElement(stack())
stack() = result
EndIf
Else
AddElement(stack())
stack() = ValD(token)
EndIf
EndIf
LastElement(stack())
ProcedureReturn stack()
EndProcedure
; GUI
Enumeration
#Window
#ExpressionInput
#ResultLabel
#RPNLabel
#CalculateButton
#ClearButton
EndEnumeration
OpenWindow(#Window, 0, 0, 500, 250, "Calculator - Shunting Yard Algorithm", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget(#PB_Any, 10, 10, 480, 20, "Enter expression (e.g., 3+4*2/(1-5)^2):")
StringGadget(#ExpressionInput, 10, 35, 480, 25, "3+4*2/(1-5)^2")
ButtonGadget(#CalculateButton, 10, 70, 100, 30, "Calculate")
ButtonGadget(#ClearButton, 120, 70, 100, 30, "Clear")
TextGadget(#PB_Any, 10, 110, 480, 20, "RPN (Postfix):")
TextGadget(#RPNLabel, 10, 135, 480, 25, "", #PB_Text_Border)
TextGadget(#PB_Any, 10, 170, 480, 20, "Result:")
TextGadget(#ResultLabel, 10, 195, 480, 40, "", #PB_Text_Border)
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case #CalculateButton
Define expr.s = GetGadgetText(#ExpressionInput)
Define rpn.s = ShuntingYard(expr)
Define result.d = EvaluateRPN(rpn)
SetGadgetText(#RPNLabel, rpn)
SetGadgetText(#ResultLabel, StrD(result, 10))
Case #ClearButton
SetGadgetText(#ExpressionInput, "")
SetGadgetText(#RPNLabel, "")
SetGadgetText(#ResultLabel, "")
EndSelect
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
considering there are lots of RPN codes and also a couple of pb samples on the forum it's perhaps not so hard for it to make.
or make an interpreter for purebasic.
Code: Select all
; PureBasic Interpreter written in PureBasic
; Supports: Define, Variables, Debug, If/Else/EndIf, For/To/Next, Arithmetic, Comparisons
EnableExplicit
Enumeration TokenType
#TOKEN_EOF
#TOKEN_NUMBER
#TOKEN_STRING
#TOKEN_IDENTIFIER
#TOKEN_KEYWORD
#TOKEN_OPERATOR
#TOKEN_ASSIGN
#TOKEN_COMMA
#TOKEN_LPAREN
#TOKEN_RPAREN
EndEnumeration
Structure Token
type.i
value.s
numValue.d
EndStructure
Structure Variable
name.s
value.d
stringValue.s
isString.i
EndStructure
Structure Interpreter
List tokens.Token()
List variables.Variable()
List output.s()
tokenIndex.i
errorMsg.s
EndStructure
; ==================== TOKENIZER ====================
Procedure.s TrimString(text.s)
text = RTrim(text)
text = LTrim(text)
ProcedureReturn text
EndProcedure
Procedure.i IsKeyword(word.s)
word = LCase(word)
If word = "define" Or word = "debug" Or word = "if" Or word = "else" Or word = "endif"
ProcedureReturn #True
EndIf
If word = "for" Or word = "to" Or word = "next" Or word = "while" Or word = "wend"
ProcedureReturn #True
EndIf
If word = "str" Or word = "val" Or word = "and" Or word = "or" Or word = "not"
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure Tokenize(code.s, List tokens.Token())
Define pos.i = 1
Define length.i = Len(code)
Define ch.s, nextCh.s
Define word.s
Define inString.i = #False
Define stringContent.s
While pos <= length
ch = Mid(code, pos, 1)
; Skip whitespace
If ch = " " Or ch = #TAB$ Or ch = #CR$ Or ch = #LF$
pos + 1
Continue
EndIf
; Skip comments
If ch = ";"
While pos <= length And Mid(code, pos, 1) <> #LF$
pos + 1
Wend
Continue
EndIf
; String literal
If ch = #DQUOTE$
stringContent = ""
pos + 1
While pos <= length And Mid(code, pos, 1) <> #DQUOTE$
stringContent + Mid(code, pos, 1)
pos + 1
Wend
pos + 1 ; Skip closing quote
AddElement(tokens())
tokens()\type = #TOKEN_STRING
tokens()\value = stringContent
Continue
EndIf
; Number
If ch >= "0" And ch <= "9"
word = ""
While pos <= length
ch = Mid(code, pos, 1)
If (ch >= "0" And ch <= "9") Or ch = "."
word + ch
pos + 1
Else
Break
EndIf
Wend
AddElement(tokens())
tokens()\type = #TOKEN_NUMBER
tokens()\value = word
tokens()\numValue = ValD(word)
Continue
EndIf
; Identifier or Keyword
If (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z") Or ch = "_"
word = ""
While pos <= length
ch = Mid(code, pos, 1)
If (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z") Or (ch >= "0" And ch <= "9") Or ch = "_" Or ch = "."
word + ch
pos + 1
Else
Break
EndIf
Wend
; Remove type suffix (.i, .f, .s, etc)
Define dotPos.i = FindString(word, ".", 1)
Define baseName.s = word
If dotPos > 0
baseName = Left(word, dotPos - 1)
EndIf
AddElement(tokens())
If IsKeyword(baseName)
tokens()\type = #TOKEN_KEYWORD
tokens()\value = baseName
Else
tokens()\type = #TOKEN_IDENTIFIER
tokens()\value = baseName
EndIf
Continue
EndIf
; Two-character operators
If pos < length
nextCh = Mid(code, pos + 1, 1)
Define twoChar.s = ch + nextCh
If twoChar = "<=" Or twoChar = ">=" Or twoChar = "<>" Or twoChar = "=="
AddElement(tokens())
tokens()\type = #TOKEN_OPERATOR
tokens()\value = twoChar
pos + 2
Continue
EndIf
EndIf
; Single character operators
If ch = "+" Or ch = "-" Or ch = "*" Or ch = "/" Or ch = "<" Or ch = ">"
AddElement(tokens())
tokens()\type = #TOKEN_OPERATOR
tokens()\value = ch
pos + 1
Continue
EndIf
; Assignment
If ch = "="
AddElement(tokens())
tokens()\type = #TOKEN_ASSIGN
tokens()\value = "="
pos + 1
Continue
EndIf
; Parentheses
If ch = "("
AddElement(tokens())
tokens()\type = #TOKEN_LPAREN
tokens()\value = "("
pos + 1
Continue
EndIf
If ch = ")"
AddElement(tokens())
tokens()\type = #TOKEN_RPAREN
tokens()\value = ")"
pos + 1
Continue
EndIf
; Comma
If ch = ","
AddElement(tokens())
tokens()\type = #TOKEN_COMMA
tokens()\value = ","
pos + 1
Continue
EndIf
; Unknown character, skip it
pos + 1
Wend
; Add EOF token
AddElement(tokens())
tokens()\type = #TOKEN_EOF
EndProcedure
; ==================== INTERPRETER ====================
Procedure.i CurrentToken(*interp.Interpreter)
If *interp\tokenIndex > ListSize(*interp\tokens())
ProcedureReturn #TOKEN_EOF
EndIf
If SelectElement(*interp\tokens(), *interp\tokenIndex)
ProcedureReturn *interp\tokens()\type
EndIf
ProcedureReturn #TOKEN_EOF
EndProcedure
Procedure Advance(*interp.Interpreter)
*interp\tokenIndex + 1
EndProcedure
Procedure.s GetTokenValue(*interp.Interpreter)
If SelectElement(*interp\tokens(), *interp\tokenIndex)
ProcedureReturn *interp\tokens()\value
EndIf
ProcedureReturn ""
EndProcedure
Procedure.d GetTokenNumber(*interp.Interpreter)
If SelectElement(*interp\tokens(), *interp\tokenIndex)
ProcedureReturn *interp\tokens()\numValue
EndIf
ProcedureReturn 0
EndProcedure
Procedure.d GetVariable(*interp.Interpreter, name.s)
ForEach *interp\variables()
If LCase(*interp\variables()\name) = LCase(name)
ProcedureReturn *interp\variables()\value
EndIf
Next
ProcedureReturn 0
EndProcedure
Procedure.s GetVariableString(*interp.Interpreter, name.s)
ForEach *interp\variables()
If LCase(*interp\variables()\name) = LCase(name)
If *interp\variables()\isString
ProcedureReturn *interp\variables()\stringValue
Else
ProcedureReturn StrD(*interp\variables()\value)
EndIf
EndIf
Next
ProcedureReturn ""
EndProcedure
Procedure SetVariable(*interp.Interpreter, name.s, value.d)
ForEach *interp\variables()
If LCase(*interp\variables()\name) = LCase(name)
*interp\variables()\value = value
*interp\variables()\isString = #False
ProcedureReturn
EndIf
Next
AddElement(*interp\variables())
*interp\variables()\name = name
*interp\variables()\value = value
*interp\variables()\isString = #False
EndProcedure
Procedure SetVariableString(*interp.Interpreter, name.s, value.s)
ForEach *interp\variables()
If LCase(*interp\variables()\name) = LCase(name)
*interp\variables()\stringValue = value
*interp\variables()\isString = #True
ProcedureReturn
EndIf
Next
AddElement(*interp\variables())
*interp\variables()\name = name
*interp\variables()\stringValue = value
*interp\variables()\isString = #True
EndProcedure
Declare.d ParseExpression(*interp.Interpreter)
Procedure.d ParsePrimary(*interp.Interpreter)
Define result.d
Define tokenType.i = CurrentToken(*interp)
If tokenType = #TOKEN_NUMBER
result = GetTokenNumber(*interp)
Advance(*interp)
ProcedureReturn result
EndIf
If tokenType = #TOKEN_IDENTIFIER
Define varName.s = GetTokenValue(*interp)
Advance(*interp)
ProcedureReturn GetVariable(*interp, varName)
EndIf
If tokenType = #TOKEN_LPAREN
Advance(*interp)
result = ParseExpression(*interp)
If CurrentToken(*interp) = #TOKEN_RPAREN
Advance(*interp)
EndIf
ProcedureReturn result
EndIf
; Handle Str() function
If tokenType = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "str"
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_LPAREN
Advance(*interp)
result = ParseExpression(*interp)
If CurrentToken(*interp) = #TOKEN_RPAREN
Advance(*interp)
EndIf
ProcedureReturn result ; Return as number, will convert to string later
EndIf
EndIf
; Handle Val() function
If tokenType = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "val"
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_LPAREN
Advance(*interp)
result = ParseExpression(*interp)
If CurrentToken(*interp) = #TOKEN_RPAREN
Advance(*interp)
EndIf
ProcedureReturn result
EndIf
EndIf
ProcedureReturn 0
EndProcedure
Procedure.d ParseMultiplicative(*interp.Interpreter)
Define left.d = ParsePrimary(*interp)
While CurrentToken(*interp) = #TOKEN_OPERATOR
Define op.s = GetTokenValue(*interp)
If op <> "*" And op <> "/"
Break
EndIf
Advance(*interp)
Define right.d = ParsePrimary(*interp)
If op = "*"
left = left * right
ElseIf op = "/"
If right <> 0
left = left / right
EndIf
EndIf
Wend
ProcedureReturn left
EndProcedure
Procedure.d ParseAdditive(*interp.Interpreter)
Define left.d = ParseMultiplicative(*interp)
While CurrentToken(*interp) = #TOKEN_OPERATOR
Define op.s = GetTokenValue(*interp)
If op <> "+" And op <> "-"
Break
EndIf
Advance(*interp)
Define right.d = ParseMultiplicative(*interp)
If op = "+"
left = left + right
ElseIf op = "-"
left = left - right
EndIf
Wend
ProcedureReturn left
EndProcedure
Procedure.d ParseComparison(*interp.Interpreter)
Define left.d = ParseAdditive(*interp)
While CurrentToken(*interp) = #TOKEN_OPERATOR
Define op.s = GetTokenValue(*interp)
If op <> "<" And op <> ">" And op <> "<=" And op <> ">=" And op <> "<>" And op <> "=="
Break
EndIf
Advance(*interp)
Define right.d = ParseAdditive(*interp)
Select op
Case "<"
If left < right : left = 1 : Else : left = 0 : EndIf
Case ">"
If left > right : left = 1 : Else : left = 0 : EndIf
Case "<="
If left <= right : left = 1 : Else : left = 0 : EndIf
Case ">="
If left >= right : left = 1 : Else : left = 0 : EndIf
Case "<>"
If left <> right : left = 1 : Else : left = 0 : EndIf
Case "=="
If left = right : left = 1 : Else : left = 0 : EndIf
EndSelect
Wend
ProcedureReturn left
EndProcedure
Procedure.d ParseExpression(*interp.Interpreter)
ProcedureReturn ParseComparison(*interp)
EndProcedure
Declare ExecuteStatement(*interp.Interpreter)
Procedure ExecuteDefine(*interp.Interpreter)
Advance(*interp) ; Skip Define keyword
While CurrentToken(*interp) = #TOKEN_IDENTIFIER
Define varName.s = GetTokenValue(*interp)
SetVariable(*interp, varName, 0)
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_COMMA
Advance(*interp)
Else
Break
EndIf
Wend
EndProcedure
Procedure ExecuteAssignment(*interp.Interpreter)
Define varName.s = GetTokenValue(*interp)
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_ASSIGN
Advance(*interp)
Define value.d = ParseExpression(*interp)
SetVariable(*interp, varName, value)
EndIf
EndProcedure
Procedure ExecuteDebug(*interp.Interpreter)
Advance(*interp) ; Skip Debug keyword
; Handle string literals
If CurrentToken(*interp) = #TOKEN_STRING
Define output.s = GetTokenValue(*interp)
Advance(*interp)
; Check for string concatenation
While CurrentToken(*interp) = #TOKEN_OPERATOR And GetTokenValue(*interp) = "+"
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_STRING
output + GetTokenValue(*interp)
Advance(*interp)
ElseIf CurrentToken(*interp) = #TOKEN_IDENTIFIER
output + StrD(GetVariable(*interp, GetTokenValue(*interp)))
Advance(*interp)
ElseIf CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "str"
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_LPAREN
Advance(*interp)
Define val.d = ParseExpression(*interp)
output + StrD(val)
If CurrentToken(*interp) = #TOKEN_RPAREN
Advance(*interp)
EndIf
EndIf
Else
output + StrD(ParseExpression(*interp))
EndIf
Wend
AddElement(*interp\output())
*interp\output() = output
Else
Define value.d = ParseExpression(*interp)
AddElement(*interp\output())
*interp\output() = StrD(value)
EndIf
EndProcedure
Procedure ExecuteIf(*interp.Interpreter)
Advance(*interp) ; Skip If keyword
Define condition.d = ParseExpression(*interp)
; Save positions for then and else blocks
Define thenStart.i = *interp\tokenIndex
Define elseStart.i = -1
Define depth.i = 1
; Find Else and EndIf
While CurrentToken(*interp) <> #TOKEN_EOF And depth > 0
If CurrentToken(*interp) = #TOKEN_KEYWORD
Define kw.s = LCase(GetTokenValue(*interp))
If kw = "if"
depth + 1
ElseIf kw = "else" And depth = 1
elseStart = *interp\tokenIndex + 1
ElseIf kw = "endif"
depth - 1
If depth = 0
Break
EndIf
EndIf
EndIf
Advance(*interp)
Wend
Define endPos.i = *interp\tokenIndex
; Execute appropriate block
If condition <> 0
*interp\tokenIndex = thenStart
While *interp\tokenIndex < endPos
If CurrentToken(*interp) = #TOKEN_KEYWORD
kw = LCase(GetTokenValue(*interp))
If kw = "else" Or kw = "endif"
Break
EndIf
EndIf
ExecuteStatement(*interp)
Wend
ElseIf elseStart >= 0
*interp\tokenIndex = elseStart
While *interp\tokenIndex < endPos
If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "endif"
Break
EndIf
ExecuteStatement(*interp)
Wend
EndIf
*interp\tokenIndex = endPos
If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "endif"
Advance(*interp)
EndIf
EndProcedure
Procedure ExecuteFor(*interp.Interpreter)
Advance(*interp) ; Skip For keyword
Define loopVar.s = GetTokenValue(*interp)
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_ASSIGN
Advance(*interp)
EndIf
Define startVal.d = ParseExpression(*interp)
If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "to"
Advance(*interp)
EndIf
Define endVal.d = ParseExpression(*interp)
Define loopStart.i = *interp\tokenIndex
; Find matching Next
Define depth.i = 1
While CurrentToken(*interp) <> #TOKEN_EOF And depth > 0
If CurrentToken(*interp) = #TOKEN_KEYWORD
Define kw.s = LCase(GetTokenValue(*interp))
If kw = "for"
depth + 1
ElseIf kw = "next"
depth - 1
If depth = 0
Break
EndIf
EndIf
EndIf
Advance(*interp)
Wend
Define loopEnd.i = *interp\tokenIndex
; Execute loop
Define i.d
While i < endVal
SetVariable(*interp, loopVar, i)
*interp\tokenIndex = loopStart
While *interp\tokenIndex < loopEnd
ExecuteStatement(*interp)
Wend
i+1
Wend
*interp\tokenIndex = loopEnd
If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "next"
Advance(*interp)
If CurrentToken(*interp) = #TOKEN_IDENTIFIER
Advance(*interp)
EndIf
EndIf
EndProcedure
Procedure ExecuteStatement(*interp.Interpreter)
If CurrentToken(*interp) = #TOKEN_EOF
ProcedureReturn
EndIf
If CurrentToken(*interp) = #TOKEN_KEYWORD
Define kw.s = LCase(GetTokenValue(*interp))
Select kw
Case "define"
ExecuteDefine(*interp)
Case "debug"
ExecuteDebug(*interp)
Case "if"
ExecuteIf(*interp)
Case "for"
ExecuteFor(*interp)
Default
Advance(*interp)
EndSelect
ElseIf CurrentToken(*interp) = #TOKEN_IDENTIFIER
ExecuteAssignment(*interp)
Else
Advance(*interp)
EndIf
EndProcedure
Procedure Execute(*interp.Interpreter)
While CurrentToken(*interp) <> #TOKEN_EOF
ExecuteStatement(*interp)
Wend
EndProcedure
; ==================== MAIN PROGRAM ====================
Define code.s
code = "; PureBasic Interpreter Test" + #LF$
code + "Define x, y, result" + #LF$
code + "x = 10" + #LF$
code + "y = 20" + #LF$
code + "result = x + y" + #LF$
code + "Debug " + #DQUOTE$ + "Sum: " + #DQUOTE$ + " + Str(result)" + #LF$
code + #LF$
code + "If result > 25" + #LF$
code + " Debug " + #DQUOTE$ + "Result is greater than 25" + #DQUOTE$ + #LF$
code + "Else" + #LF$
code + " Debug " + #DQUOTE$ + "Result is 25 or less" + #DQUOTE$ + #LF$
code + "EndIf" + #LF$
code + #LF$
code + "For i = 1 To 5" + #LF$
code + " Debug " + #DQUOTE$ + "Loop iteration: " + #DQUOTE$ + " + Str(i)" + #LF$
code + "Next i" + #LF$
Debug "=== PureBasic Interpreter ===" + #LF$
Debug "Input Code:"
Debug code
Debug ""
Debug "=== Execution Output ==="
Define interp.Interpreter
Tokenize(code, interp\tokens())
Execute(@interp)
ForEach interp\output()
Debug interp\output()
Next
If interp\errorMsg <> ""
Debug "Error: " + interp\errorMsg
EndIf
or Breakout
Code: Select all
; Breakout Game in PureBasic
; Use arrow keys to move paddle, SPACE to launch ball, ESC to quit
#WINDOW_WIDTH = 640
#WINDOW_HEIGHT = 480
#PADDLE_WIDTH = 100
#PADDLE_HEIGHT = 15
#BALL_SIZE = 10
#BRICK_WIDTH = 60
#BRICK_HEIGHT = 20
#BRICK_ROWS = 5
#BRICK_COLS = 10
#BRICK_PADDING = 5
Structure Brick
x.i
y.i
active.i
color.i
EndStructure
Global Dim bricks.Brick(#BRICK_ROWS * #BRICK_COLS)
Global paddleX.f = (#WINDOW_WIDTH - #PADDLE_WIDTH) / 2
Global ballX.f = #WINDOW_WIDTH / 2
Global ballY.f = #WINDOW_HEIGHT - 100
Global ballSpeedX.f = 0
Global ballSpeedY.f = 0
Global ballLaunched.i = #False
Global score.i = 0
Global lives.i = 3
Procedure InitBricks()
Define i, row, col, idx
Define startY = 50
For row = 0 To #BRICK_ROWS - 1
For col = 0 To #BRICK_COLS - 1
idx = row * #BRICK_COLS + col
bricks(idx)\x = col * (#BRICK_WIDTH + #BRICK_PADDING) + 20
bricks(idx)\y = row * (#BRICK_HEIGHT + #BRICK_PADDING) + startY
bricks(idx)\active = #True
; Different colors for different rows
Select row
Case 0: bricks(idx)\color = RGB(255, 0, 0) ; Red
Case 1: bricks(idx)\color = RGB(255, 165, 0) ; Orange
Case 2: bricks(idx)\color = RGB(255, 255, 0) ; Yellow
Case 3: bricks(idx)\color = RGB(0, 255, 0) ; Green
Case 4: bricks(idx)\color = RGB(0, 0, 255) ; Blue
EndSelect
Next
Next
EndProcedure
Procedure ResetBall()
ballX = paddleX + #PADDLE_WIDTH / 2
ballY = #WINDOW_HEIGHT - 100
ballSpeedX = 0
ballSpeedY = 0
ballLaunched = #False
EndProcedure
Procedure Clamp(x,y,z)
Protected res = x
If x < y
res = y
ElseIf x > z
res = z
EndIf
ProcedureReturn res
EndProcedure
Procedure CheckCollisions()
Define i, brickRight, brickBottom
; Ball collision with walls
If ballX <= 0 Or ballX >= #WINDOW_WIDTH - #BALL_SIZE
ballSpeedX = -ballSpeedX
ballX = Clamp(ballX, 0, #WINDOW_WIDTH - #BALL_SIZE)
EndIf
If ballY <= 0
ballSpeedY = -ballSpeedY
ballY = 0
EndIf
; Ball falls below paddle - lose life
If ballY >= #WINDOW_HEIGHT
lives - 1
If lives > 0
ResetBall()
EndIf
EndIf
; Ball collision with paddle
If ballY + #BALL_SIZE >= #WINDOW_HEIGHT - 50 And
ballY + #BALL_SIZE <= #WINDOW_HEIGHT - 35 And
ballX + #BALL_SIZE >= paddleX And
ballX <= paddleX + #PADDLE_WIDTH
ballSpeedY = -Abs(ballSpeedY)
; Add spin based on where ball hits paddle
Define hitPos.f = (ballX - paddleX) / #PADDLE_WIDTH
ballSpeedX = (hitPos - 0.5) * 8
EndIf
; Ball collision with bricks
For i = 0 To #BRICK_ROWS * #BRICK_COLS - 1
If bricks(i)\active
brickRight = bricks(i)\x + #BRICK_WIDTH
brickBottom = bricks(i)\y + #BRICK_HEIGHT
If ballX + #BALL_SIZE >= bricks(i)\x And
ballX <= brickRight And
ballY + #BALL_SIZE >= bricks(i)\y And
ballY <= brickBottom
bricks(i)\active = #False
ballSpeedY = -ballSpeedY
score + 10
Break
EndIf
EndIf
Next
EndProcedure
; Main program
InitSprite()
InitKeyboard()
If OpenWindow(0, 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT, "Breakout Game", #PB_Window_ScreenCentered)
If OpenWindowedScreen(WindowID(0), 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT, 0, 0, 0)
InitBricks()
Repeat
; Handle input
Repeat
Define event = WindowEvent()
If event = #PB_Event_CloseWindow
Break 2
EndIf
Until event = 0
; Keyboard input
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
Break
EndIf
If KeyboardPushed(#PB_Key_Left) And paddleX > 0
paddleX - 8
EndIf
If KeyboardPushed(#PB_Key_Right) And paddleX < #WINDOW_WIDTH - #PADDLE_WIDTH
paddleX + 8
EndIf
If KeyboardPushed(#PB_Key_Space) And Not ballLaunched
ballLaunched = #True
ballSpeedX = Random(2) - 1
ballSpeedY = -5
EndIf
; Update ball position
If ballLaunched
ballX + ballSpeedX
ballY + ballSpeedY
CheckCollisions()
Else
; Ball follows paddle before launch
ballX = paddleX + #PADDLE_WIDTH / 2 - #BALL_SIZE / 2
EndIf
; Check for game over or win
Define allBricksGone = #True
For i = 0 To #BRICK_ROWS * #BRICK_COLS - 1
If bricks(i)\active
allBricksGone = #False
Break
EndIf
Next
; Draw everything
ClearScreen(RGB(0, 0, 0))
StartDrawing(ScreenOutput())
; Draw bricks
For i = 0 To #BRICK_ROWS * #BRICK_COLS - 1
If bricks(i)\active
Box(bricks(i)\x, bricks(i)\y, #BRICK_WIDTH, #BRICK_HEIGHT, bricks(i)\color)
EndIf
Next
; Draw paddle
Box(paddleX, #WINDOW_HEIGHT - 50, #PADDLE_WIDTH, #PADDLE_HEIGHT, RGB(200, 200, 200))
; Draw ball
Circle(ballX + #BALL_SIZE / 2, ballY + #BALL_SIZE / 2, #BALL_SIZE / 2, RGB(255, 255, 255))
; Draw score and lives
DrawText(10, 10, "Score: " + Str(score), RGB(255, 255, 255))
DrawText(#WINDOW_WIDTH - 100, 10, "Lives: " + Str(lives), RGB(255, 255, 255))
; Check win/lose conditions
If lives <= 0
DrawText(#WINDOW_WIDTH / 2 - 80, #WINDOW_HEIGHT / 2, "GAME OVER! Press ESC", RGB(255, 0, 0))
ElseIf allBricksGone
DrawText(#WINDOW_WIDTH / 2 - 80, #WINDOW_HEIGHT / 2, "YOU WIN! Press ESC", RGB(0, 255, 0))
EndIf
StopDrawing()
FlipBuffers()
Delay(16) ; ~60 FPS
ForEver
CloseScreen()
EndIf
CloseWindow(0)
EndIf
End