Get every combination of on/off

3 replies to this topic
Posted 2 months ago #1
slenkar

I have a command that has 4 actions

each action has on/off states

so a command could consist of turn left and walk forward
or if walk forward is turned off then it would just be turn left

turn left and turn right could be turned on at the same time, although it would be pointless

So, the question is, how would I get every combination of on/off for 4 actions?

for example if there were 2 actions the 4 possible commands would be:

TurnLeft 1
TurnRight 1

Turn Left1
TurnRight0

Turn Left0
TurnRight1

Turn Left0
TurnRight0

The only way i can think of doing it would be to make a byte class because bytes are the best way to represent on/off states that I can think of.

 
Posted 2 months ago #2
dawlane

Using the bits of an integer for such thing is I think the best method. I use the method for all sorts of things from controlling a sprite character, triggering custom events to constructing SQL commands.
It makes sense to use an integer as a flag variable where you can use multiple controllers to move a player character. There is no need to write a 'byte' class as you can just implement the logic to read whats in the flag using the bit-wise and (&) operator and perform the action. If you need to handle more that 32 bits, a wrapper class for the flags would be a good method to pass them to a function method for processing.
A working example for handling two players.

Strict

Import mojo

' Global Constants 
Const CTRL_PLAYER1_RIGHT:Int = $00000001
Const CTRL_PLAYER1_LEFT:Int = $00000002
Const CTRL_PLAYER1_UP:Int = $00000004
Const CTRL_PLAYER1_DOWN:Int = $00000008
Const CTRL_PLAYER1_FIRE:Int = $00000010

Const CTRL_PLAYER2_RIGHT:Int = $00000020
Const CTRL_PLAYER2_LEFT:Int = $00000040
Const CTRL_PLAYER2_UP:Int = $00000080
Const CTRL_PLAYER2_DOWN:Int = $00000100
Const CTRL_PLAYER2_FIRE:Int = $00000200

Const PLAYER_ONE:Int = 0
Const PLAYER_TWO:Int = 1

Class CGame Extends App
	Field players:CPlayers
	Field ctrls:CControls
	
	Method OnCreate:Int()
		SetUpdateRate(60)
		players = New CPlayers
		ctrls = New CControls
		Return 0
	End Method
	
	Method OnUpdate:Int()
		Self.ctrls.ReadInput()
		Self.players.Update(ctrls.ControllerState)
		Return 0
	End Method
	
	Method OnRender:Int()
		Cls()
		Self.players.Draw()
		Return 0
	End Method
End Class

Class CControls
	Public
		' Using a global for this would make it easier to access it, but there could be
		' times where it could unintetionally altered, so make it so that the controls can
		' only be read to avoid accidental changes
		Method ControllerState:Int() Property
			Return flag
		End Method
		
		Method ReadInput:Void()
			' Clear the flag for fresh input
			Self.flag = 0
			
			' Read any inputs to set the flags
			' Special note about keys. Certain key combinations will
			' not work correctly on Linux haven't tested Window or OS X
			If KeyDown(KEY_A) Then flag |= CTRL_PLAYER1_LEFT
			If KeyDown(KEY_D) Then flag |= CTRL_PLAYER1_RIGHT
			If KeyDown(KEY_W) Then flag |= CTRL_PLAYER1_UP
			If KeyDown(KEY_S) Then flag |= CTRL_PLAYER1_DOWN
			If KeyDown(KEY_CONTROL) Then flag |= CTRL_PLAYER1_FIRE
			
			If KeyDown(KEY_J) Then flag |= CTRL_PLAYER2_LEFT
			If KeyDown(KEY_L) Then flag |= CTRL_PLAYER2_RIGHT
			If KeyDown(KEY_I) Then flag |= CTRL_PLAYER2_UP
			If KeyDown(KEY_K) Then flag |= CTRL_PLAYER2_DOWN
			If KeyDown(KEY_SHIFT) Then flag |= CTRL_PLAYER2_FIRE
		End Method
	Private
		' An integer is only 4 bytes giving you 32 possible on/off states
		' This means that we can handle 6 players with four directions and a fire button.		
		Field flag:Int
End Class

Class CBlob
	Field pX:Int
	Field pY:Int
	Field fire:Int
	Field pl:Int
	
	Method New(x:Int, y:Int, c:Int)
		Self.pX = x
		Self.pY = y
		Self.fire = 0
		Self.pl = c
	End Method
	
	Method Draw:Void()
		If Self.fire Then
			SetColor(255,0,0)
		Else
			If Self.pl = PLAYER_ONE Then SetColor(0,255,255) Else SetColor(255,255,0)
		EndIf
		DrawRect(Self.pX, Self.pY, 32, 32)
		DrawText("P"+(Self.pl+1), Self.pX, Self.pY)
	End Method
End Class

Class CPlayers
	Field player:CBlob[2]
	
	Method New()
		Self.player[PLAYER_ONE] = New CBlob(0, DeviceHeight()/2, PLAYER_ONE)
		Self.player[PLAYER_TWO] = New CBlob(DeviceWidth() - 32, DeviceHeight()/2, PLAYER_TWO)
	End Method
	
	Method Update:Void(action:Int)
		For Local i:CBlob = Eachin Self.player
			' Trap the low order bits that would be assigned to the first players controls
			' So we can process them.
			If (action & $1F) Then
				If (action & CTRL_PLAYER1_LEFT) And ((action & CTRL_PLAYER1_RIGHT) = 0) And i.pX > 0 Then i.pX -=4
				If (action & CTRL_PLAYER1_RIGHT) And ((action & CTRL_PLAYER1_LEFT) = 0) And (i.pX < DeviceWidth() - 32) Then i.pX +=4
				If (action & CTRL_PLAYER1_UP) And ((action & CTRL_PLAYER1_DOWN) = 0) And i.pY > 0 Then i.pY -=4
				If (action & CTRL_PLAYER1_DOWN) And ((action & CTRL_PLAYER1_UP) = 0) And (i.pY < DeviceHeight() - 32) Then i.pY +=4
				If action & CTRL_PLAYER1_FIRE Then i.fire = 1
			EndIf
				' Shift the bits right by five to see if the next player has any set.
				action = (action Shr 5)
		Next
	End Method
	
	Method Draw:Void()
		For Local i:CBlob = Eachin Self.player
			i.Draw()
			i.fire = 0 ' Clear the render states after drawing
		Next
	End Method	
End Class

Function Main:Int()
	New CGame()
	Return 0
End Function
 
Posted 2 months ago #3
slenkar

thanks i wasnt sure how to use the bitwise commands

 
Posted 2 months ago #4
dawlane

Using an integers with bit-wise operators is also a reasonable way to optimize tile maps if you are a bit pushed for memory. You can encode maps of 65536 x 65536 tiles using a minimum of 8 bytes by using one 32 bit integer to hold the tile location on a virtual grid and only display those that are in the region of the view port, while the second can hold information about that tile or other little optimization tricks like how main tiles to draw in any direction from that start grid location. The down side would be the more tile you have the bigger the list to parse, but there are ways round this.

If you need to clear a bit while preserving the others you can hard code the bits to preserve or do

val &= ~BIT_TO_CLEAR ' BIT_TO_CLEAR being the value of the bit to set
an all in one to clear and set another

val = (val & (~BIT_TO_CLEAR)) | NEW_BIT_TO_SET

Hard coding would probably save you a few clock cycles as the processor would have to assign the value rhs value of whatever you wanted to clear to a register, then eXclusive OR the register contents before bit-wise AND to the lhs value.

The docs on the shift bits operators are incorrect. If I remember there was a few posts about it a while back.