This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Ashton's profile picture

Read/edit only 1 "bit" in a value?

Started by Ashton, 03 July 2013 - 07:11 PM
Ashton #1
Posted 03 July 2013 - 09:11 PM
I'm building a SMP-adventure-map with multiple "dungeons" and am thinking a good way to keep track of these (and the locations they open) is using the mag-cards/RFID cards (imnbris peripherals). My plan is to fill the data-block of the card with 0s and when a specific dungeon is completed and new area unlocked change it's corresponding 0 to a 1.

i.e.:
000000000000000

Dungeon 3 is unlocked:
001000000000000

Dungon 3 and 5 are unlocked:
001010000000000

How could I easily setup the reader/writers to read/change ONLY the bit that's related to their dungeon (so that all other progress is not erased)? My only thought is to use complex strings of IF statements (i.e. "if data = 000000000000000 or 000000000000001 or 000000000000010 or 000000000000011 or 000000000000010…. (etc) then data == data + 100000000000000" which would take a LOT of time to code into all 16 (or however many) locations…
Lyqyd #2
Posted 03 July 2013 - 09:23 PM
Check out the bit API if you're going to store the value as an actual number, or the string library (string.sub, specifically) if you want to store it as ASCII 1 and 0 characters.
Yevano #3
Posted 03 July 2013 - 10:00 PM
Specifically, you can do this:


-- Read bit r from value v
b = bit.bor(bit.brshift(v, r), 1)

-- Set bit r in value v to b (where b is 0 or 1)
if b == 1 then
	v = bit.bor(v, bit.blshift(b, 1))
else
	v = bit.bnot(bit.bor(bit.bnot(v), bit.blshift(b, 1)))
end

I'm fairly sure that works, but you should test it to make sure.
theoriginalbit #4
Posted 04 July 2013 - 12:48 AM
ok so as Lyqyd stated you will need to use numbers, not what you have used there, Lua doens't understand or have a data type for binary numbers. So 00100000 it actually just sees as 100000, so in order to use that number you would actually need to use 32 (which is decimal) or 0x20 (which is hex). Both of these formats is supported by Lua.

Now as for making levels only modify their own info first you need to understand binary AND and OR, so I'll detail now

AND

1 AND 1 = 1
1 AND 0 = 0 
0 AND 1 = 0
0 AND 0 = 0

OR

1 OR 1 = 1
1 OR 0 = 1
0 OR 1 = 1
0 OR 0 = 0

Ok so now that you can see what happens with these we can start to understand how to do the dungeon checking and modifying.
Now you have made it nice and easy for checking and setting since each dungeon uses a new bit.

Ok so checking existence of a dungeon is as easy as

local function hasAccess( code, require ) --# code is their current access level ... require is the level required to access
  return bit.band( code, require ) == require
end
Now the way this works is because of the rules I showed you above, if the bit is on in both the code and the require then it stays on in the result. However if either one is missing a bit when it should be there then it will be off in the result, and obviously if they're both off it will also stay off. So the only way for this statement to return true is for the result from the bit AND to be the code needed. small example:

0000 0001 &
0000 0001 =
0000 0001 (match)

0000 0011 &
0000 0001 =
0000 0001 (match)

0000 0011 &
0000 0100 =
0000 0000 (failed)

0000 0100 &
0000 0010 =
0000 0000 (failed)
So as you can see it will not succeed unless that specific bit is on, now in contrast if you use OR this would be the result


0000 0001 |
0000 0001 =
0000 0001 (match)

0000 0011 |
0000 0001 =
0000 0011 (failed)

0000 0011 |
0000 0100 =
0000 0111 (failed)

0000 0100 |
0000 0010 =
0000 0110 (failed)
So as you can see with OR it will only let the person through if that bit is the only bit on.

Why did I show you both ways? Well your description of your problem and use case can be interpreted different ways, and as I'm unsure of which way you wanted I figured best to just show you both.

Now to add a new dungeon to the persons access is easy too, you can do the following

local function add( code, new ) --# code is the current access level ... new is the new dungeon to be added
  return bit.bor(code, new)
end
Now this will only add the dungeon if required, if it is already in there, then nothing is changed

To remove a dungeon is also as easy as


local function remove( code, del ) --# code is the existing access level ... del is the dungeon to be removed
  return bit.band(code, bit.bnot(del))
end

Ok so now you see how you can add and remove, I'm going to show you the easy way to add the dungeon code for adding and removing so you don't have to remember how the numbers.

--# example of added dungeon 6 to the current access
--# as a binary number dungeon 6 is 0010 0000

local access = 0
--# in hex (requires knowing the hex value for 32
access = add(access, 0x20)
--# as a decimal number (this can start to get harder to calculate the further along you get)
access = add(access, 32)
--# the easiest way 2^(n-1), so in this case is 2^(6-1) = 32
access = add(access, 2^(6-1))
Using the last method it means that you can easily write loops to check existence and such

local access = 0x20
for i = 1, 16 do --# for dungeons 1 -> 16
  if hasAccess(access, 2^(i-1)) then
    print( "You have access to dungeon: "..i )
  end
end

So with all the above information you should be able to build your programs that you require that give the players access to the dungeons.

I hope all this has helped, if you have any questions just ask :)/>

— BIT
immibis #5
Posted 04 July 2013 - 02:26 AM
Strings work for this purpose and are easier to understand, don't overcomplicate it. I'd explain string.sub here if I wasn't on my phone.
Ashton #6
Posted 05 July 2013 - 01:38 AM
Wow… I'm gonna have to re-read all that slowly… (and/or use a leading 1 that has no value so that it wont dump the other digits…)

Thanks for the info, and if there's an easier way using a string, immimbris, I would love to hear it as well ^_^/>

EDIT:
another thought, is there a "contains" option? (i.e. "If data contains A, then {}")? I could just use a different letter for each dungeon and tell it to check if the letter is contained in the string. (i.e. A = Dungeon 1, B = dungeon 2, etd, so ADC = Dungeon 1 completed, Dungeon 2 not completed, Dungeon 3 completed, Dungeon 4 completed)
Bubba #7
Posted 05 July 2013 - 01:56 AM
Wow… I'm gonna have to re-read all that slowly… (and/or use a leading 1 that has no value so that it wont dump the other digits…)

Thanks for the info, and if there's an easier way using a string, immimbris, I would love to hear it as well ^_^/>

EDIT:
another thought, is there a "contains" option? (i.e. "If data contains A, then {}")? I could just use a different letter for each dungeon and tell it to check if the letter is contained in the string. (i.e. A = Dungeon 1, B = dungeon 2, etd, so ADC = Dungeon 1 completed, Dungeon 2 not completed, Dungeon 3 completed, Dungeon 4 completed)

Like string.match()?


local dungeons = "A,B,C,F,Q"
if dungeons:match("A") then
  --#Do stuff
else
  --#Don't do stuff
end

Also, why use Mag-Cards? Floppies seem like a much simpler way to go about it.

Edit: Not sure if you know about the ":" character, but it essentially just tells the object that it is operating on to insert itself into the function. For example:

local dungeons = "This is a string, which has several different methods you can use on it."
if string.match(dungeons, "string") then
  print("yup")
end
if dungeons:match("string") then
  print("yup")
end

This would print "yup" twice, because both of the if statements do the exact same thing. The second one is just shorter, which is why I use it.
Lyqyd #8
Posted 05 July 2013 - 03:29 AM
If you really insist on using ones and zeros rather than doing actual binary manipulation, you'll want to use string.sub.


local completion = "00000000"

--we completed dungeon 3
local before = string.sub(completion, 1, 2)
local after = string.sub(completion, 4)
completion = before.."1"..after

--see if dungeon five is done
if string.sub(completion, 5, 5) == "1" then
  --it is done!
end
theoriginalbit #9
Posted 05 July 2013 - 04:13 AM
Also, why use Mag-Cards? Floppies seem like a much simpler way to go about it.
I would hazard a guess and say because they are not as easily edited than floppy discs and don't store data in plain text.
Really I it was me I'd use an RFID, just because there is no swiping needed, it's just a 5 block range ;)/>
Ashton #10
Posted 05 July 2013 - 05:05 AM
Wow… I'm gonna have to re-read all that slowly… (and/or use a leading 1 that has no value so that it wont dump the other digits…)

Thanks for the info, and if there's an easier way using a string, immimbris, I would love to hear it as well ^_^/>

EDIT:
another thought, is there a "contains" option? (i.e. "If data contains A, then {}")? I could just use a different letter for each dungeon and tell it to check if the letter is contained in the string. (i.e. A = Dungeon 1, B = dungeon 2, etd, so ADC = Dungeon 1 completed, Dungeon 2 not completed, Dungeon 3 completed, Dungeon 4 completed)

Like string.match()?


local dungeons = "A,B,C,F,Q"
if dungeons:match("A") then
  --#Do stuff
else
  --#Don't do stuff
end

Also, why use Mag-Cards? Floppies seem like a much simpler way to go about it.

Edit: Not sure if you know about the ":" character, but it essentially just tells the object that it is operating on to insert itself into the function. For example:

local dungeons = "This is a string, which has several different methods you can use on it."
if string.match(dungeons, "string") then
  print("yup")
end
if dungeons:match("string") then
  print("yup")
end

This would print "yup" twice, because both of the if statements do the exact same thing. The second one is just shorter, which is why I use it.

That looks like the easiest method, and I can assign upto 36 values that way (dont remember the limit on mag-cards)

As for using the MagCards, it's mostly to create a sense of interaction, when you complete the dungeon you swipe your card and when you want to go back, or go to the area the dungeon has block, you swipe again. Also it does not require players to ever touch a PC - the only reason I'm using CC in the world is to create complex dungeons (Also a huge number of the PCs will be connected to an AGI and CommandBlock, which would give players unprecidented control over the game. As such I've used Forge Essentials to prevent crafting of a PC (or advanced PC) and for the most part they are completely inaccessable.

I would hazard a guess and say because they are not as easily edited than floppy discs and don't store data in plain text.
Really I it was me I'd use an RFID, just because there is no swiping needed, it's just a 5 block range ;)/>

As I said above, interaction, though I do see where the RFID would be more useful for things like a bridge materializing (walk within 5 steps, the bridge appears, leave the 5-step radius of the opposite end and it vanishes) I may consider that instead, I was thinking purely that players would *want* some action that 'proved to them' that they completed the dungeon… (though I will have to think of some way to back-up their progress since at the moment, leaving the world (it's a multi-world server) empties your inventory, not to mention if you fall in lava you'd lose your card/RFID… but at the same time, I dont want to easily enable people sharing cards/RFIDs…)

I severely hope these periferals also get upgraded to 1.6.1… it's going to be such a letdown to do all this work and find out that it was all wasted because MC marched on…
Ashton #11
Posted 12 July 2013 - 02:24 AM
For anyone considering a similar system for their map, I just had a brainstorm!

0000 = all 4 dungeons locked
0001 = dungeon 1 unlocked
0002 = dungeon 1 item X found (This is a Zelda-themed server so it's the Compas, which means upon entry of a room (nearing a RFID sensor) a chime is played if a key/chest is in the room)
0003 = Dungeon 1 cleared

Likewise increasing the count could indicate other progress in the dungeon!

Granted I just made it exponentially more difficult, but the above methods can still be used, just thought I'd share my idea :)/>
Grim Reaper #12
Posted 12 July 2013 - 02:27 AM
Are you doing this with strings? Does that mean there are 9999 states for your dungeon? You can't really do a binary number as 0003… Considering 3 isn't a valid binary digit.
Ashton #13
Posted 12 July 2013 - 02:48 AM
Are you doing this with strings? Does that mean there are 9999 states for your dungeon? You can't really do a binary number as 0003… Considering 3 isn't a valid binary digit.

I'm still building the system, but yes, it's strings (I know Binary is 1 or 0 aka ON or OFF)
Grim Reaper #14
Posted 12 July 2013 - 03:10 AM
Sounds cool. You could have each map file have a list of flags which are defined in the map file so that you can let your engine handle which flags are set based on the file rather than having to hard code all of that kind of logic.
MxHn #15
Posted 21 July 2013 - 09:36 AM
Do you really like bits? I think tables would be easier. Plus you can make it dynamic!
theoriginalbit #16
Posted 22 July 2013 - 12:46 AM
Do you really like bits? I think tables would be easier. Plus you can make it dynamic!
bit manipulation can be dynamic too, and it also has a much smaller memory footprint than a table, I definitely agree with the choice of using binary.
Lyqyd #17
Posted 22 July 2013 - 12:48 AM
He isn't using binary though, he's using strings, for the purpose of storing the data on an item carried by the player (RFID card or something, I think).
MxHn #18
Posted 05 August 2013 - 10:06 AM
@theoriginalbit: True but in the lua doc it says that only the used slots in a table take up memory and nil does not(so fp will be the same).
GopherAtl #19
Posted 05 August 2013 - 11:20 AM
MxHn, uhm. Yes. oinly used values take up memory. Memory for the key, memory for the value, memory for the table itself, which is a hashmap. all told, many times more memory than a single number, which for the true binary approach, or even a single string, for the string approach.

That said, I would also have suggested tables, if not for the condition that this needs to fit on a mag card, which do not have room for a serialized table.
MxHn #20
Posted 07 August 2013 - 02:08 PM
AH, I see so mag-cards! I stand corrected.
p.s. True I also understand that there is no "free" memory in computing. Every character takes up memory.