- Home
- Discussion Forum
- AROS FORUMS
- PortablE
- bitset inventory example
bitset inventory example
Last updated on 13 years ago
SamuraiCrowSoftware Dev
Posted 13 years agoOk class, welcome to the PortablE programming course.
For these lessons, we're going to be implementing a bitset class. This will allow the presence or absence of up to 32 items to be mapped into only 4 bytes of memory using logic operations.
In order to make a class to hold items, the first thing we have to do is make a definition of what kind of items we can put into storage in the bitset. What we need for this to take place is to make a unique identification number for each item starting at zero. The next thing we want is a name so we can identify each item going into the list. Thirdly we'll need a way to list the contents of the set.
If there are are any questions, I'll answer them as we go. We'll be making this using a purely object-oriented approach. Anyone who has been in the IRC channel discussions will have to note that these lessons are in PortablE rather than AmigaE so they will look a little different.
For these lessons, we're going to be implementing a bitset class. This will allow the presence or absence of up to 32 items to be mapped into only 4 bytes of memory using logic operations.
In order to make a class to hold items, the first thing we have to do is make a definition of what kind of items we can put into storage in the bitset. What we need for this to take place is to make a unique identification number for each item starting at zero. The next thing we want is a name so we can identify each item going into the list. Thirdly we'll need a way to list the contents of the set.
If there are are any questions, I'll answer them as we go. We'll be making this using a purely object-oriented approach. Anyone who has been in the IRC channel discussions will have to note that these lessons are in PortablE rather than AmigaE so they will look a little different.
Edited by SamuraiCrow on 17-10-2011 19:41, 13 years ago
SamuraiCrowSoftware Dev
Posted 13 years agoI apologize for not actually starting the example code, but I'm trying to get a decent development environment going for AROS and PortablE.
I'll probably just install Icaros on a VirtualBox VM and forget about being able to transfer files in and out except via the internet.
I'll probably just install Icaros on a VirtualBox VM and forget about being able to transfer files in and out except via the internet.
amigamiaAdmin
Posted 13 years agoWould you mind to post the link to online material such as Books or Manuals we may need on E and PortablE? :)
SamuraiCrowSoftware Dev
Posted 13 years agohttp://cshandley.co.uk/amigae/ is the E manual as a webpage.
http://cshandley.co.uk/portable/ is the homepage of PortablE including download links.
http://cshandley.co.uk/portable/PortablE.html is the PortablE manual listing all of the differences between PortablE and AmigaE.
http://cshandley.co.uk/portable/StandardFunctionality.html is the manual for the PortablE classes.
http://cshandley.co.uk/portable/ is the homepage of PortablE including download links.
http://cshandley.co.uk/portable/PortablE.html is the PortablE manual listing all of the differences between PortablE and AmigaE.
http://cshandley.co.uk/portable/StandardFunctionality.html is the manual for the PortablE classes.
SamuraiCrowSoftware Dev
Posted 13 years agoOk! Here is the long awaited inventory example!
Call this file inventory.e
And here's the code to test it out:
I'll break this down for you in the next week or so.
Call this file inventory.e
Code Download source
OPT MODULE
CLASS item
itemnum:INT
pad1:INT
description:ARRAY OF CHAR
ENDCLASS
DEF nextitem:INT,itemlist[32]:ARRAY OF PTR TO item
PROC makeitem(describe:ARRAY OF CHAR) NEW OF item
self.itemnum:=nextitem
itemlist[nextitem]:=self
++nextitem
IF nextitem>31 THEN Raise("ARG")
self.description:=describe
ENDPROC
PROC getmask() OF item RETURNS mask:LONG IS 1 SHL self.itemnum
PROC getdescription() OF item IS self.description
CLASS inventory
mask:LONG
ENDCLASS
PROC makeinventory() NEW OF inventory
self.mask:=0
ENDPROC
PROC additem(myitem:PTR TO item) OF inventory
self.mask:=self.mask OR myitem.getmask()
ENDPROC
PROC removeitem(youritem:PTR TO item) OF inventory
self.mask:=self.mask AND NOT youritem.getmask()
ENDPROC
PROC hasitem(myitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:= (self.mask AND myitem.getmask()=myitem.getmask())
ENDPROC
PROC hasany(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=(self.mask AND items.mask <> 0)
ENDPROC
PROC hasall(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=(self.mask AND items.mask = items.mask)
ENDPROC
PROC getitem(source:PTR TO inventory, theitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:=FALSE
IF source.hasitem(theitem)
source.removeitem(theitem)
self.additem(theitem)
ret:=TRUE
ENDIF
ENDPROC
PROC giveitem(destination:PTR TO inventory, theitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:=destination.getitem(self,theitem)
ENDPROC
PROC useall(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=FALSE
IF self.hasall(items)
self.mask:=self.mask AND NOT items.mask
ret:=TRUE
ENDIF
ENDPROC
PROC list() OF inventory
DEF count:INT,current:PTR TO item
FOR count:=0 TO nextitem-1
current:=itemlist[count]
IF self.hasitem(current)
Print('\s\n',current.getdescription())
ENDIF
ENDFOR
ENDPROC
And here's the code to test it out:
Code Download source
MODULE '*inventory'
DEF lamp:PTR TO item, torch:PTR TO item, bag:PTR TO item,
room:PTR TO inventory, mystuff:PTR TO inventory, lights:PTR TO inventory,
allstuff:PTR TO inventory
PROC getit(what:PTR TO item)
IF mystuff.getitem(room,what)
Print('You\'ve picked up \s.\n', what.getdescription())
ELSE
Print('You aleady have \s.\n', what.getdescription())
ENDIF
ENDPROC
PROC main()
DEF in[4]:STRING
NEW lamp.makeitem('a lamp')
NEW torch.makeitem('a torch')
NEW bag.makeitem('a bag')
NEW room.makeinventory()
NEW mystuff.makeinventory()
NEW lights.makeinventory()
NEW allstuff.makeinventory()
room.additem(lamp)
room.additem(torch)
room.additem(bag)
lights.additem(lamp)
lights.additem(torch)
allstuff.additem(lamp)
allstuff.additem(torch)
allstuff.additem(bag)
REPEAT
IF mystuff.hasany(lights)
Print('You are in a room with no doors and no windows.\n')
Print('You see:\n')
room.list()
ELSE
Print('It\'s dark. You are likely to be eaten by a grue.\n')
ENDIF
Print('\nWhat do you grab?\n')
PrintFlush()
ReadStr(stdin,in)
SELECT in[0]
CASE "l"; getit(lamp)
CASE "t"; getit(torch)
CASE "b"; getit(bag)
DEFAULT; Print('I don\'t see it here.\n')
ENDSELECT
UNTIL mystuff.hasall(allstuff)
Print('You have everything! You win!\n')
PrintFlush()
FINALLY
PrintException()
ENDPROC
I'll break this down for you in the next week or so.
Edited by SamuraiCrow on 01-11-2011 13:57, 13 years ago
SamuraiCrowSoftware Dev
Posted 13 years agoOk, class, now let's look through each line of the inventory.e module.
This tells PortablE that this will be a module rather than a program. A module is similar to a linker library on other languages.
The class definition tells what data items are stored in a single item record. Notice that pad1 is not used later on. It is only there because the array won't perform quickly if it isn't aligned to a 4-byte boundary. An INT in PortablE is only 2 bytes long.
Here are some global variables. The nextitem variable will be used to hold the number of items available defined.
The itemlist variable allocates space to store a table of pointers to the item definitions. The reason it's only 32 items in length is that that is the number of bits in a LONG variable. Since this is a bitset, each bit will represent a slot in the inventory.
This is a procedure. It defines an action the computer can take to do a particular purpose. Since it says NEW OF item at the beginning it defines when it can be used. The NEW tells that it is used with the NEW operator so this is considered to be a constructor. The OF item tells that this is actually a method associated with the item class. A constructor tells the computer what needs to be done to set the initial values for the item class.
Here is another method of the item class. It is not a constructor because the NEW is not listed before the OF item. It tells how to define a mask of an item.
This method is called a "getter" method. It just gets some information from the item class and returns it. The reason for having a getter method is that it allows a piece of information to be hidden in the class using the PRIVATE directive. Since there is no PRIVATE directive this is just used for future necessity rather than real present need.
Here's another class definition. It holds only one item: the mask. It is not optional to have this as a class though, because we will be defining many methods of it.
Here is the constructor. All it does is clear the mask. It is necessary to do this to a class because the operating system might not clear the memory a class is declared in. If it does, we wouldn't have needed a constructor at all in this case.
Another thing to note: Since there is only one command inside the procedure, we could have used the IS command to make it all one line of code. It's just for clarity that I wrote it out the long way.
These two methods mask on or off a single bit of memory using AND, OR and NOT operators. These are packing boolean values. These are the fundamental operators of a bitset.
These three methods test a bit or bits to see if the masks of an item or another inventory can compare to the masks in the current item.
This method uses 3 other methods to do a bit transfer. The hasitem method checks if the item is available, and the other two clear and set the appropriate bitmask.
This method does the reverse of the getitem method. Notice how it just uses the getitem method with the source and destination reversed.
This method allows items to be consumed in their use. Like the previous two, it returns a boolean value (true or false) that tells if the method succeeded.
And this last method is a bit more complicated. It lists out each item in an inventory by counting through all the used items, fetching the item from the table, checking if it is used in this inventory, and printing its description if it is.
Code Download source
OPT MODULE
This tells PortablE that this will be a module rather than a program. A module is similar to a linker library on other languages.
Code Download source
CLASS item
itemnum:INT
pad1:INT
description:ARRAY OF CHAR
ENDCLASS
The class definition tells what data items are stored in a single item record. Notice that pad1 is not used later on. It is only there because the array won't perform quickly if it isn't aligned to a 4-byte boundary. An INT in PortablE is only 2 bytes long.
Code Download source
DEF nextitem:INT,itemlist[32]:ARRAY OF PTR TO item
Here are some global variables. The nextitem variable will be used to hold the number of items available defined.
The itemlist variable allocates space to store a table of pointers to the item definitions. The reason it's only 32 items in length is that that is the number of bits in a LONG variable. Since this is a bitset, each bit will represent a slot in the inventory.
Code Download source
PROC makeitem(describe:ARRAY OF CHAR) NEW OF item
self.itemnum:=nextitem
itemlist[nextitem]:=self
++nextitem
IF nextitem>31 THEN Raise("ARG")
self.description:=describe
ENDPROC
This is a procedure. It defines an action the computer can take to do a particular purpose. Since it says NEW OF item at the beginning it defines when it can be used. The NEW tells that it is used with the NEW operator so this is considered to be a constructor. The OF item tells that this is actually a method associated with the item class. A constructor tells the computer what needs to be done to set the initial values for the item class.
Code Download source
PROC getmask() OF item RETURNS mask:LONG IS 1 SHL self.itemnum
Here is another method of the item class. It is not a constructor because the NEW is not listed before the OF item. It tells how to define a mask of an item.
Code Download source
PROC getdescription() OF item IS self.description
This method is called a "getter" method. It just gets some information from the item class and returns it. The reason for having a getter method is that it allows a piece of information to be hidden in the class using the PRIVATE directive. Since there is no PRIVATE directive this is just used for future necessity rather than real present need.
Code Download source
CLASS inventory
mask:LONG
ENDCLASS
Here's another class definition. It holds only one item: the mask. It is not optional to have this as a class though, because we will be defining many methods of it.
Code Download source
PROC makeinventory() NEW OF inventory
self.mask:=0
ENDPROC
Here is the constructor. All it does is clear the mask. It is necessary to do this to a class because the operating system might not clear the memory a class is declared in. If it does, we wouldn't have needed a constructor at all in this case.
Another thing to note: Since there is only one command inside the procedure, we could have used the IS command to make it all one line of code. It's just for clarity that I wrote it out the long way.
Code Download source
PROC additem(myitem:PTR TO item) OF inventory
self.mask:=self.mask OR myitem.getmask()
ENDPROC
PROC removeitem(youritem:PTR TO item) OF inventory
self.mask:=self.mask AND NOT youritem.getmask()
ENDPROC
These two methods mask on or off a single bit of memory using AND, OR and NOT operators. These are packing boolean values. These are the fundamental operators of a bitset.
Code Download source
PROC hasitem(myitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:= (self.mask AND myitem.getmask()=myitem.getmask())
ENDPROC
PROC hasany(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=(self.mask AND items.mask <> 0)
ENDPROC
PROC hasall(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=(self.mask AND items.mask = items.mask)
ENDPROC
These three methods test a bit or bits to see if the masks of an item or another inventory can compare to the masks in the current item.
Code Download source
PROC getitem(source:PTR TO inventory, theitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:=FALSE
IF source.hasitem(theitem)
source.removeitem(theitem)
self.additem(theitem)
ret:=TRUE
ENDIF
ENDPROC
This method uses 3 other methods to do a bit transfer. The hasitem method checks if the item is available, and the other two clear and set the appropriate bitmask.
Code Download source
PROC giveitem(destination:PTR TO inventory, theitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:=destination.getitem(self,theitem)
ENDPROC
This method does the reverse of the getitem method. Notice how it just uses the getitem method with the source and destination reversed.
Code Download source
PROC useall(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=FALSE
IF self.hasall(items)
self.mask:=self.mask AND NOT items.mask
ret:=TRUE
ENDIF
ENDPROC
This method allows items to be consumed in their use. Like the previous two, it returns a boolean value (true or false) that tells if the method succeeded.
Code Download source
PROC list() OF inventory
DEF count:INT,current:PTR TO item
FOR count:=0 TO nextitem-1
current:=itemlist[count]
IF self.hasitem(current)
Print('\s\n',current.getdescription())
ENDIF
ENDFOR
ENDPROC
And this last method is a bit more complicated. It lists out each item in an inventory by counting through all the used items, fetching the item from the table, checking if it is used in this inventory, and printing its description if it is.
Edited by SamuraiCrow on 01-11-2011 14:01, 13 years ago
SamuraiCrowSoftware Dev
Posted 13 years agoFor those of you who have downloaded the source, I'm afraid you'll have to download it again. ChrisH was kind enough to point out a mistake in my code regarding QUAD variables. I had to replace them with LONGs instead and correct the size of the bitset.
ChrisHSoftware Dev
Posted 13 years agoI haven't had time to closely examine the code, but...
Just a little bit of advice: It almost never makes sense to use the :LONG type, since it causes unnecessary casts without any benefit. Better to use no type at all, which means it automatically defaults to :VALUE . This avoids unnecessary casts, but is the same size (at least until PortablE runs on a 64-bit native OS+CPU).
Quote
Code Download source
PROC getmask() OF item RETURNS mask:LONG IS 1 SHL self.itemnum
Code Download source
CLASS inventory
mask:LONG
ENDCLASS
Just a little bit of advice: It almost never makes sense to use the :LONG type, since it causes unnecessary casts without any benefit. Better to use no type at all, which means it automatically defaults to :VALUE . This avoids unnecessary casts, but is the same size (at least until PortablE runs on a 64-bit native OS+CPU).
SamuraiCrowSoftware Dev
Posted 13 years agoI'm looking forward to the equivalent of long long in PortablE. :) I know that C99 supports it. Besides, casts cost nothing when it's run through GCC.
ChrisHSoftware Dev
Posted 13 years ago@SamuraiCrow
PortablE already supports the equivalent of "long long" (i.e. 64-bits). It is called BIGVALUE. (Although you can only write 32-bit numbers directly in the code.)
P.S. Cast cost programmer time & in general should be avoided.
PortablE already supports the equivalent of "long long" (i.e. 64-bits). It is called BIGVALUE. (Although you can only write 32-bit numbers directly in the code.)
P.S. Cast cost programmer time & in general should be avoided.
SamuraiCrowSoftware Dev
Posted 13 years agoThanks for the tip about BIGVALUE! Here's the latest version.
Notice that I switched from using assumed constants for the size of the BIGVALUE and used the SIZEOF operator to find the size in bytes? That should make the code easier to read and easier to port! :)
Code Download source
OPT MODULE
CLASS item
itemnum:INT
pad1:INT
description:ARRAY OF CHAR
ENDCLASS
DEF nextitem:INT,itemlist[SIZEOF BIGVALUE*8]:ARRAY OF PTR TO item
PROC makeitem(describe:ARRAY OF CHAR) NEW OF item
self.itemnum:=nextitem
itemlist[nextitem]:=self
++nextitem
IF nextitem>(SIZEOF BIGVALUE*8) THEN Raise("ARG")
self.description:=describe
ENDPROC
PROC getmask() OF item RETURNS mask:BIGVALUE IS 1 SHL self.itemnum
PROC getdescription() OF item IS self.description
CLASS inventory
mask:BIGVALUE
ENDCLASS
PROC makeinventory() NEW OF inventory
self.mask:=0
ENDPROC
PROC additem(myitem:PTR TO item) OF inventory
self.mask:=self.mask OR myitem.getmask()
ENDPROC
PROC removeitem(youritem:PTR TO item) OF inventory
self.mask:=self.mask AND NOT youritem.getmask()
ENDPROC
PROC hasitem(myitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:= (self.mask AND myitem.getmask()=myitem.getmask())
ENDPROC
PROC hasany(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=(self.mask AND items.mask <> 0)
ENDPROC
PROC hasall(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=(self.mask AND items.mask = items.mask)
ENDPROC
PROC getitem(source:PTR TO inventory, theitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:=FALSE
IF source.hasitem(theitem)
source.removeitem(theitem)
self.additem(theitem)
ret:=TRUE
ENDIF
ENDPROC
PROC giveitem(destination:PTR TO inventory, theitem:PTR TO item) OF inventory RETURNS ret:BOOL
ret:=destination.getitem(self,theitem)
ENDPROC
PROC useall(items:PTR TO inventory) OF inventory RETURNS ret:BOOL
ret:=FALSE
IF self.hasall(items)
self.mask:=self.mask AND NOT items.mask
ret:=TRUE
ENDIF
ENDPROC
PROC list() OF inventory
DEF count:INT,current:PTR TO item
FOR count:=0 TO nextitem-1
current:=itemlist[count]
IF self.hasitem(current)
Print('\s\n',current.getdescription())
ENDIF
ENDFOR
ENDPROC
Notice that I switched from using assumed constants for the size of the BIGVALUE and used the SIZEOF operator to find the size in bytes? That should make the code easier to read and easier to port! :)
You can view all discussion threads in this forum.
You cannot start a new discussion thread in this forum.
You cannot reply in this discussion thread.
You cannot start on a poll in this forum.
You cannot upload attachments in this forum.
You can download attachments in this forum.
You cannot start a new discussion thread in this forum.
You cannot reply in this discussion thread.
You cannot start on a poll in this forum.
You cannot upload attachments in this forum.
You can download attachments in this forum.
Moderator: Administrator