Microsoft Small Basic

Program Listing:
Embed this in your website
' Basketball 0.3
' Copyright (c) 2012 Nonki Takahashi. All rights reserved.
'
' History :
' 0.3 2012/08/21 Detected collision between ball and board or goal. (TKL110-0)
' 0.2 2012/08/09 Equation corrected. (TKL110)
' 0.1 2012/06/24 Created.
'
' TODO :
' (1) Rewrite structure of limb (leg and arm) .
' (2) Correct reflection when bound on the ring.
' (3) Graphical user interface to throw.
'
title = "Basketball 0.3"
GraphicsWindow.Title = title
width = 950
height = 600
GraphicsWindow.Width = width
GraphicsWindow.Height = height
FLOORY = 550  ' floor y
ENDX = 105    ' end x
size = 24
DrawRoom()
Ball_Add()
Goal_Add()
Man_Add()
AddControls()
x = 0
y = FLOORY - size
Ball_Move()
angle = 0 ' [degree]
delta = 8
nGoal = 0
While "True"
  If x <= 0 Then
    x = 0
    For i = 1 To 80
      x = x + delta
      Ball_Move()
      angle = angle + delta / ((size / 2) * (Math.Pi / 180))
      Ball_Rotate()
      Program.Delay(i * 2)
    EndFor
  EndIf
  Man_Hold()
  WaitButton()
  DrawRoom()
  Man_Throw()
EndWhile

Sub AddControls
  GraphicsWindow.BrushColor = "Black"
  oVelocity = Controls.AddTextBox(700, 100)
  Controls.SetTextBoxText(oVelocity, 15)
  oAngle = Controls.AddTextBox(700, 150)
  Controls.SetTextBoxText(oAngle, 45)
  oThrow = Controls.AddButton("Throw", 700, 200)
  Controls.HideControl(oThrow)
  ' unit for textbox
  oUnit = Shapes.AddText("[km/h]")
  Shapes.Move(oUnit, 870, 104)
  oUnit = Shapes.AddText("[degree]")
  Shapes.Move(oUnit, 870, 154)
EndSub

Sub DrawRoom
  ' wall
  GraphicsWindow.BrushColor = "DarkGray"
  GraphicsWindow.FillRectangle(0, 0, width, FLOORY)
  ' floor
  GraphicsWindow.BrushColor = "Sienna"
  GraphicsWindow.FillRectangle(0, FLOORY, width, height - FLOORY)
  GraphicsWindow.PenColor = "#773D22" ' Sienna L: 40->30
  GraphicsWindow.DrawRectangle(0, FLOORY, width, height - FLOORY)
  ' end line
  GraphicsWindow.PenColor = "White"
  GraphicsWindow.DrawRectangle(ENDX - 5, FLOORY, 5, 0)
  ' free throw line
  GraphicsWindow.PenColor = "White"
  GraphicsWindow.DrawRectangle(ENDX + 120 + 455, FLOORY, 5, 0)
  ' free throw circle
  GraphicsWindow.PenColor = "White"
  GraphicsWindow.DrawRectangle(ENDX + 580 + 180, FLOORY, 5, 0)
  ' 3 point line
  GraphicsWindow.PenColor = "White"
  GraphicsWindow.DrawRectangle(ENDX + 120 + 670, FLOORY, 5, 0)
EndSub

Sub WaitButton
  Controls.ShowControl(oThrow)
  notClicked = "True"
  Controls.ButtonClicked = OnButtonClicked
  While notClicked
    Program.Delay(200)
  EndWhile
  Controls.HideControl(oThrow)
EndSub

Sub OnButtonClicked
  notClicked = "False"
EndSub

Sub Ball_Add
  ' Ball | Add ball
  ' param size - ball size
  ' return oBall
  ball["num"] = ball["num"] + 1
  oBall = "Ball" + ball["num"]
  ball[oBall + ".size"] = size
  ball[oBall + ".angle"] = 0
  rad = angle / 180 * Math.Pi  ' radian
  GraphicsWindow.BrushColor = "OrangeRed"
  GraphicsWindow.PenColor = "SaddleBrown"
  GraphicsWindow.PenWidth = 2
  ball[oBall + ".oBall"] = Shapes.AddEllipse(size, size)
  GraphicsWindow.PenWidth = 2
  oLineU = Shapes.AddEllipse(size * 0.6, size * 0.4)
  ball[oBall + ".oLineU"] = oLineU
  oLineC = Shapes.AddEllipse(size * 0.6, size * 0.4)
  ball[oBall + ".oLineC"] = oLineC
  GraphicsWindow.PenColor = "SaddleBrown"
  oLineH = Shapes.AddLine(0, 0, size - 2, 0)
  ball[oBall + ".oLineH"] = oLineH
  oLineV = Shapes.AddLine(0, 0, 0, size - 2)
  ball[oBall + ".oLineV"] = oLineV
  GraphicsWindow.PenColor = "White"
  GraphicsWindow.BrushColor = "White"
  oHighlight = Shapes.AddEllipse(size * 0.7 , size * 0.7)
  Shapes.SetOpacity(oHighlight, 30)
  ball[oBall + ".oHighlight"] = oHighlight
  Ball_Move()
EndSub

Sub Ball_Move
  ' Ball | Move ball
  ' param oBall
  ' param x, y - position to move
  ball[oBall + ".x"] = x
  ball[oBall + ".y"] = y
  angle = ball[oBall + ".angle"]
  rad = angle / 180 * Math.Pi  ' radian
  size = ball[oBall + ".size"]
  Shapes.Move(ball[oBall + ".oBall"], x, y)
  xU = x + size * (0.5 + 0.3 * Math.Sin(rad) - 0.3)
  yU = y + size * (0.5 - 0.3 * Math.Cos(rad) - 0.2)
  Shapes.Move(ball[oBall + ".oLineU"], xU, yU)
  xC = x + size * (0.5 - 0.3 * Math.Sin(rad) - 0.3)
  yC = y + size * (0.5 + 0.3 * Math.Cos(rad) - 0.2)
  Shapes.Move(ball[oBall + ".oLineC"], xC, yC)
  Shapes.Move(ball[oBall + ".oLineH"], x + 1, y + size * 0.5)
  Shapes.Move(ball[oBall + ".oLineV"], x + size * 0.5, y)
  Shapes.Move(ball[oBall + ".oHighlight"], x + 1, y + 1)
EndSub

Sub Ball_Rotate
  ' Ball | Rotate ball
  ' param oBall
  ' param angle - rotate angle
  angle = Math.Remainder(angle, 360)
  If angle < 0 Then
    angle = 360 - angle
  EndIf
  ball[oBall + ".angle"] = angle
  rad = angle / 180 * Math.Pi  ' radian
  x = ball[oBall + ".x"]
  y = ball[oBall + ".y"]
  xU = x + size * (0.5 + 0.3 * Math.Sin(rad) - 0.3)
  yU = y + size * (0.5 - 0.3 * Math.Cos(rad) - 0.2)
  Shapes.Move(ball[oBall + ".oLineU"], xU, yU)
  Shapes.Rotate(ball[oBall + ".oLineU"], angle)
  xC = x + size * (0.5 - 0.3 * Math.Sin(rad) - 0.3)
  yC = y + size * (0.5 + 0.3 * Math.Cos(rad) - 0.2)
  Shapes.Move(ball[oBall + ".oLineC"], xC, yC)
  Shapes.Rotate(ball[oBall + ".oLineC"], angle)
  Shapes.Rotate(ball[oBall + ".oLineH"], angle)
  Shapes.Rotate(ball[oBall + ".oLineV"], angle)
EndSub

Sub Goal_Add
  ' Goal | Add goal
  ' pole
  GraphicsWindow.PenColor = "#333333"
  GraphicsWindow.BrushColor = "Black"
  oPole = Shapes.AddRectangle(ENDX + 120 - 24 - 15, 10)
  Shapes.Move(oPole, 0, FLOORY - 305 - 107 + 15 + 10)
  oPole = Shapes.AddRectangle(ENDX + 120 - 24 - 15, 10)
  Shapes.Move(oPole, 0, FLOORY - 305 + 15 - 20)
  ' flange
  GraphicsWindow.PenColor = "DarkRed"
  oFlange = Shapes.AddLine(0, 0, 15, 0)
  r = 24
  x1 = x + ENDX + 120 - r - 15
  y1 = FLOORY - 305
  Shapes.Move(oFlange, x1, y1)
  posFlange = "x1=" + x1 + ";x2=" + (x1 + 15) + ";y=" + y1 + ";"
  ' ring
  GraphicsWindow.PenColor = "Red"
  oRing = Shapes.AddLine(0, 0, r * 2, 0)
  x1 = x + ENDX + 120 - r
  Shapes.Move(oRing, x1, y1)
  posRing = "x1=" + x1 + ";x2=" + (x1 + r * 2) + ";y=" + y1 + ";"
  posGoal = "x=" + (x1 + r) + ";y=" + y1 + ";"
  ' board
  GraphicsWindow.PenColor = "Gray"
  GraphicsWindow.BrushColor = "White"
  oBoard = Shapes.AddRectangle(6, 107)
  x1 = x + ENDX + 120 - r - 15 - 6
  y1 = FLOORY - 305 - 107 + 15
  Shapes.Move(oBoard, x1, y1)
  posBoard = "x=" + (x1 + 6) + ";y1=" + y1 + ";y2=" + (y1 + 107) + ";"
  ' net
  xNet[0] = "0=0;1=5;2=19;3=41;4=69;5=100;6=131;7=159;8=181;9=195;10=200;"
  xNet[1] = "0=30;1=33;2=43;3=59;4=78;5=100;6=122;7=141;8=157;9=167;10=170;"
  xNet[2] = "0=40;1=52;2=60;3=71;4=85;5=100;6=115;7=129;8=140;9=148;10=160;"
  xNet[3] = "0=50;1=52;2=60;3=71;4=85;5=100;6=115;7=129;8=140;9=148;10=150;"
  x0 = x + ENDX + 120 - r
  y0 = FLOORY - 305
  y1 = 0
  y2 = 15
  GraphicsWindow.PenColor = "White"
  For j = 0 To 2
    For i = 0 To 4
      If Math.Remainder(j, 2) = 0 Then
        x1 = xNet[j][i * 2] * r / 100
        x2 = xNet[j + 1][i * 2 + 1] * r / 100
        oNet = Shapes.AddLine(0, 0, x2 - x1, y2 - y1)
        Shapes.Move(oNet, x0 + x1, y0 + y1)
        x1 = xNet[j + 1][i * 2 + 1] * r / 100
        x2 = xNet[j][i * 2 + 2] * r / 100
        oNet = Shapes.AddLine(0, y2 - y1, x2 - x1, 0)
        Shapes.Move(oNet, x0 + x1, y0 + y1)
      Else
        x1 = xNet[j + 1][i * 2] * r / 100
        x2 = xNet[j][i * 2 + 1] * r / 100
        oNet = Shapes.AddLine(0, y2 - y1, x2 - x1, 0)
        Shapes.Move(oNet, x0 + x1, y0 + y1)
        x1 = xNet[j][i * 2 + 1] * r / 100
        x2 = xNet[j + 1][i * 2 + 2] * r / 100
        oNet = Shapes.AddLine(0, 0, x2 - x1, y2 - y1)
        Shapes.Move(oNet, x0 + x1, y0 + y1)
      EndIf
    EndFor
    y1 = y1 + 15
    y2 = y2 + 15
  EndFor
  GraphicsWindow.FontSize = 50
  GraphicsWindow.BrushColor = "Red"
  oBanner = Shapes.AddText("Goal")
  Shapes.Move(oBanner, width / 2 - 25 * 2, height / 2 - 50 / 2)
  Shapes.SetOpacity(oBanner, 0)
  GraphicsWindow.FontSize = 14
EndSub

Sub Goal_BoundOnBoard
  ' param x, y - ball position
  ' param lastx, lasty - ball last position
  ' return bound - "True" if bound (collision detected)
  bound = "False"
  cy = y + size / 2
  If posBoard["y1"] < cy And cy < posBoard["y2"] And x < posBoard["x"] And posBoard["x"] < x + size Then
    bound = "True"
    x = posBoard["x"]
    't = 0
    y0 = y
    x0 = x
    lastvx = vx
    vx = -vx * Math.SquareRoot(1.3 / 1.8)
    da = vy * dt * 100 * 360 / Math.Pi / size
  EndIf
EndSub

Sub Goal_BoundOnFlange
  ' param x, y - ball position
  ' param lastx, lasty - ball last position
  ' return bound - "True" if bound (collision detected)
  bound = "False"
  cx = x + size / 2
  If posFlange["x1"] < cx And cx < posFlange["x2"] And y < posFlange["y"] And posFlange["y"] < y + size Then
    bound = "True"
    y = posFlange["y"] - size
    't = 0
    y0 = y
    x0 = x
    vy = -vy * Math.SquareRoot(1.3 / 1.8)
    da = -vx * dt * 100 * 360 / Math.Pi / size
  EndIf
EndSub

Sub Goal_BoundOnRing
  ' param x, y - ball position
  ' param lastx, lasty - ball last position
  ' return bound - "True" if bound (collision detected)
  bound = "False"
  cx = x + size / 2
  cy = y + size / 2
  bx = posRing["x1"]   ' bound point x
  by = posRing["y"]   ' bound point y
  distance = Math.SquareRoot(Math.Power(cx - bx, 2) + Math.Power(cy - by, 2))
  If distance <= size / 2 Then
    Goal_CalcReflection()
  EndIf
  bx = posRing["x2"]   ' bound point x
  distance = Math.SquareRoot(Math.Power(cx - bx, 2) + Math.Power(cy - by, 2))
  If distance <= size / 2 Then
    Goal_CalcReflection()
  EndIf
EndSub

Sub Goal_CalcReflection
  bound = "True"
  lastcx = lastx + size / 2
  lastcy = lasty + size / 2
  lastdist = Math.SquareRoot(Math.Power(lastcx - bx, 2) + Math.Power(lastcy - by, 2))
  k = (size / 2 - distance) / (lastdist - distance)
  cx = (1 - k) * cx + k * lastcx
  cy = (1 - k) * cy + k * lastcy
  x = cx - size / 2
  y = cy - size / 2
  't = 0
  y0 = y
  x0 = x
  vx = -vx * Math.SquareRoot(1.3 / 1.8)
  vy = -vy * Math.SquareRoot(1.3 / 1.8)
  da = -vx * dt * 100 * 360 / Math.Pi / size
EndSub

Sub Goal_Check
  ' param x, y - ball position
  ' param lastx, lasty - ball last position
  ' return bound - "True" if bound (collision detected)
  cx = x + size / 2
  cy = y + size / 2
  gx = posGoal["x"]   ' center x of goal
  gy = posGoal["y"]   ' center y of goal
  distance = Math.SquareRoot(Math.Power(cx - gx, 2) + Math.Power(cy - gy, 2))
  If goal = "False" And distance < size / 2 And gy < cy Then
    Goal_Show()
    nGoal = nGoal + 1
    goal = "True"
  EndIf
EndSub

Sub Goal_Show
  Sound.PlayBellRing()
  For i = 0 To 100
    Shapes.SetOpacity(oBanner, i)
    Program.Delay(10)
  EndFor
  For i = 100 To 0 Step -1
    Shapes.SetOpacity(oBanner, i)
    Program.Delay(10)
  EndFor
EndSub

Sub Joint_Add
  ' param id
  ' param xj[id] - x beam root
  ' param yj[id] - y beam root
  ' param lj[id] - length beam
  ' param aj[id] - angle beam
  ' return xj[id+1] - x beam front
  ' return yj[id+1] - y beam front
  oLeg[id] = Shapes.AddLine(0, 0, lj[id], 0)
  Joint_Move()
EndSub

Sub Joint_Move
  ' param id
  ' param xj[id] - x beam root
  ' param yj[id] - y beam root
  ' param aj[id] - angle beam
  ' return xj[id+1] - x beam front
  ' return yj[id+1] - y beam front
  Stack.PushValue("local", x)
  Stack.PushValue("local", y)
  x = xj[id] + (lj[id] / 2) * Math.Cos(aj[id] * Math.Pi / 180) - (lj[id] / 2)
  y = yj[id] + (lj[id] / 2) * Math.Sin(aj[id] * Math.Pi / 180)
  Shapes.Move(oLeg[id], x, y)
  Shapes.Rotate(oLeg[id], aj[id])
  xj[id + 1] = xj[id] + lj[id] * Math.Cos(aj[id] * Math.Pi / 180)
  yj[id + 1] = yj[id] + lj[id] * Math.Sin(aj[id] * Math.Pi / 180)
  y = Stack.PopValue("local")
  x = Stack.PopValue("local")
EndSub

Sub Man_Add
  ' Man | Add man
  ' return oMan
  xMan = ENDX + 580 + 12 + 28
  yMan = FLOORY - 48 * 2
  man["num"] = man["num"] + 1
  oMan = "Man" + man["num"]
  man[oMan + ".x"] = xMan
  man[oMan + ".y"] = yMan
  GraphicsWindow.BrushColor = "Black"
  GraphicsWindow.PenColor = "Black"
  oHead = Shapes.AddEllipse(24, 24)
  man[oMan + ".oHead"] = oHead
  Shapes.Move(oHead, xMan - 12, yMan - 84)
  oBody = Shapes.AddLine(0, 0, 0, 48)
  man[oMan + ".oBody"] = oBody
  Shapes.Move(oBody, xMan, yMan - 48)
  GraphicsWindow.PenColor = "Black"
  id = 10 ' for arm
  xj[id] = xMan
  yj[id] = yMan - 48
  al = "1=60;2=90;3=-45;4=-15;"
  ll = "1=36;2=36;3=9;4=12;"
  Man_AddLimb()
  id = 20 ' for leg
  xj[id] = xMan
  yj[id] = yMan
  al = "1=100;2=-20;3=100;4=0;"
  ll = "1=48;2=48;3=21;4=7;"
  Man_AddLimb()
EndSub

Sub Man_AddLimb
  ' param id - limb id
  ' param al[] - angle of joints
  ' param ll[] - length of linterjoint
  Stack.PushValue("local", id)
  For i = 1 To 4
    lj[id] = ll[i]      ' length
    Joint_Add()
    id = id + 1
  EndFor
  id = Stack.PopValue("local")
  Man_MoveLimb()
EndSub

Sub Man_Catch
  ' Man | Catch ball
  ' param oBall
  ' param oMan
  ' param x, y - top left positon of ball
EndSub

Sub Man_Hold
  ' Man | Hold ball
  ' param oBall
  ' param oMan
  x = man[oMan + ".x"] - 48
  y = man[oMan + ".y"] - 48
  Ball_Move()
  id = 10 ' for arm
  al = "1=85;2=120;3=90;4=-20;"
  Man_MoveLimb()
EndSub

Sub Man_MoveLimb
  ' param id - id limb
  ' param al[] - angle of joints
  Stack.PushValue("local", id)
  aj[id] = al[1]                ' angle
  Joint_Move()
  id = id + 1
  For i = 2 To 4
    aj[id] = aj[id - 1] + al[i] ' angle
    Joint_Move()
    id = id + 1
  EndFor
  id = Stack.PopValue("local")
EndSub

Sub Man_Throw
  ' Man | Throw ball
  ' param oBall
  ' param oMan
  size = ball[oBall + ".size"]
  x = man[oMan + ".x"] - 48
  y = FLOORY - 144 - size
  Ball_Move()
  id = 10 ' for arm
  al = "1=120;2=120;3=110;4=-40;"
  Man_MoveLimb()
  Program.Delay(500)
  x = man[oMan + ".x"] - 48
  y = FLOORY - 192 - 12
  Ball_Move()
  id = 10 ' for arm
  al = "1=180;2=80;3=100;4=-40;"
  Man_MoveLimb()
  Program.Delay(1000)
  id = 10 ' for arm
  al[3] = -45
  Man_MoveLimb()
  g = 9.80665 ' acceleration of gravity [m/s^2]
  y0 = y
  x0 = x
  v0_ = Controls.GetTextBoxText(oVelocity)  ' [km/h]
  v0 = v0_ * 1000 / 3600                    ' [m/s]
  angle = Controls.GetTextBoxText(oAngle)   ' [degree]
  v0x = v0 * Math.Cos(angle * Math.Pi / 180)
  v0y = v0 * Math.Sin(angle * Math.Pi / 180)
  a = ball[oBall + ".angle"]
  da = 10   ' [degree]
  dt = 0.01 ' [s]
  vy = v0y
  vx = v0x
  goal = "False"
  For t = 0.1 To 30.0 Step dt     ' [s]
    a = a + da
    angle = a
    Ball_Rotate()
    vy = vy - g * dt / 2          ' velocity [m/s]
    lasty = y
    lastx = x
    y = y - (vy * dt) * 100       ' vertical position [cm]
    x = x - (vx * dt) * 100       ' horizontal position [cm]
    If x < -24 Or 950 < x Then
      y = FLOORY - size
      Goto exit_throw
    EndIf
    If FLOORY - size < y Then       ' bounds on the floor
      y = FLOORY - size
      't = 0
      y0 = y
      x0 = x
      vy = -vy * Math.SquareRoot(1.3 / 1.8)
      If vy < 0.1 Then
        Goto exit_throw
      EndIf
      da = -vx * dt * 100 * 360 / Math.Pi / size
    EndIf
    Goal_BoundOnRing()
    If bound Then
      Goto move_ball
    EndIf
    Goal_BoundOnBoard()
    If bound Then
      Goto move_ball
    EndIf
    Goal_BoundOnFlange()
    If bound Then
      Goto move_ball
    EndIf
    Goal_Check()
move_ball:
    Ball_Move()
    If Math.Remainder(t * 100, 10) = 0 Then
      GraphicsWindow.BrushColor = "Gray"
      GraphicsWindow.FillEllipse(x, y, size, size)
    EndIf
    Program.Delay(10)
  EndFor
exit_throw:
  id = 10 ' for arm
  al = "1=60;2=90;3=-45;4=-15;"
  Man_MoveLimb()
EndSub
Copyright (c) Microsoft Corporation. All rights reserved.