Talk:Saving

From Robowiki
Revision as of 06:05, 29 August 2009 by Synapse (talk | contribs) (spelling fix)
Jump to navigation Jump to search

Old Wiki: SavingDataHowTo or Saving/How

I am struggling with adding persistence to my robot. Now I don't know a good way to see when the MatchIsOver. -- PEZ


static variables

The easiest way to save data between rounds is to make your variables static. There is no need to save data to a file and retrieve it. You usually store the enemy information you collected in previous rounds (if you need it later) or information stored in files that you need to read only once. -- Albert

As far as I know static variables in a Java class is shared among all instances of that class. Robocode creates a new instance of your bot for each round (and then calls the run() method), but since static variables are shared among all instances, your bot still has access to the data that was saved in the static variables last round. I'm not sure what this means if you have multiple instances of the same bot on the field though? --Zeb

In principle, multiple instances of the bot work OK. I'm not sure why (because they are the same class, they should share the data). My guess is that it is OK because they execute in different threads. -- Albert

I've found the answer in an old posting by Mat: http://www.alphaworks.ibm.com/forum/robocode.nsf/archived/5F86B09EE7DCA3E682798BC09C4B0950?OpenDocument It says that each robot is created by separate ClassLoaders, with the purpose being that they should not share static variables. -- PEZ


I'm still a bit confused about what Robocode does between rounds. I have a static collection of enemies, each which get an instance of my robot when they are created. How come this robot reference is still valid between rounds? Isn't it a new instance of the Robot then? Or why would you need static variables for this otherwise? -- PEZ

Pondering and discussing with my colleagues I now think I know the the answer to this question: Calls like getHeading() are accessing static variables in the robot. My static collection holds a reference to an old and dead robot, but it shares those static variables with each new and fresh instance, which make it work. It's when you try calls like ahead() that things stop to work. -- PEZ


Serialization

Serializable means that a class can be converted into an array of bytes which can then be written to disk, sent over a network, etc. For a class to be Serializable it must (a) have no member variables which hold non-serializable classes and (b) implement the Serializable interface. The only real problem you can run into is if you have an object with a lot of references to other objects: when you serialize it, youll also be serializing and all of those objects, any objects that they have references to, etc. In an early version of Duelist I couldn't understand why my datafiles were about 40k per bot - turned out that one of the things I was serializing had a reference to my bot in it, so I was serializing EVERY object my bot used! :-P --David Alves

You use the transient keyword to indicate that a member variables aren't part of the persistant object. As long as you know how to reconstruct an "incomplete" deserialized object you just put transient in front of the variable declaration and it won't get serialized along with the rest of the object. Beware though that if you initialize a transient variable upon declaration you won't have the initialized value on that variable after deserialization. Like:

transient double largestX = getBattleFieldHeight() - getWidth() / 2;

The variable 'largestX' will have the value of 0 (zero) after deserialization. Now this particular variable you might want to serialize anyway. =) But you get the drift I hope. -- PEZ

You can also compress the serialized files. Kawigi shows how in CompressedSerialization.


Other

If you have your data in simple, primitive type, arrays you can store and retrieve that data compressed by using a variant of the serialization thingy above: WritingArraysToFile. -- PEZ


TIP:

if you want to increase your file quota in your local machine, add the robocode.robot.filesystem.quota=2000000 in your robocode.propoerties file. It will set the file quata to 2MB. --SSO


If you are having problems saving data under 1.4.2, look here: JRE 1.4.2 SecurityException Bug

Old Wiki: Reducing File Size or Saving/Size

I'd like to know how other people reduce the size of the data they save per robot. I was thinking about using the zip stream thing, but didn't have a clue about how to use it. Anyone help? -Wolfman

In my bot Parakeet, I store two arrays of floats instead of two arrays of doubles.. It cuts the filesize in half... I didn't need the precision of doubles anyway. --Dummy

Hrm ... what format do you save as Dummy? I just save as a text file currently. So Im not sure how saving as a float instead of a double will help in that circumstance! -- Wolfman

What are the limits of this file anyway? I was considering just serializing my Enemy objects, but I guess that's out of the question? -- PEZ

Serializing Enemy objects works, but if you find yourself running out of space, you can do better. You don't need to store things like last position, heading, velocity, etc. which are probably in your enemy class. What DuelistMini does is to store an array of doubles in a GZipOutputStream. Remember that arrays are java Objects, so you can write an entire array by using:

myObjectOutputStream.writeObject(myArray);

and read an entire array from an input stream by using:

myArray = (double[][]) myObjectInputStream.readObject();

--David Alves

Parakeet saves one integer and two arrays per datafile (one datafile for each opponent it meets in 1-on-1. No data-storage in melee battles). The source of Parakeet is included in the .jar file on the RoboCodeRepository. Actually, I looked in TheArtOfWar's source code to see how writing to files worked. Which reminds me... forgot to add credits in Parakeet's description and source. --Dummy

Object Streams consume a lot of space. Take a look inside a file written using ObjectOutputStream. If I remember correctly, each field is identified by its name and type, as well as the field's value. The signatures of the object's methods might also be written to the file. On the other hand, these files should compress very well with Zip Streams.

If you are really want to reduce file size, you might want to look at Data Streams. Write only the fields you absolutely need. If you still need to reduce the file size, try cramming larger fields into smaller ones where possible (ie: double into int). Sure, you will lose some precision, but if file size is a real issue for you, you have to be willing to make some sacrifices.

TheArtOfWar's Bot class uses Data Streams, and the Reaction class crams two double fields (heading and speed) into two byte fields. The source is available at the RobocodeRepository. I should have used Zip Streams for further file size reduction, but I never got around to it -- Ray_Vermette

Using Zip Streams is demonstrated, with code, on the CompressedSerialization page. -- PEZ

Old Wiki: Saving/What

What info to Save

More than one opinion here-

Dave Mold says to collect acceleration and change in heading.

Plenty say to collect velocity and change in heading (these could be equivalent for good data)

Paul Evans says to collect what firing angle should have been used based on distance and last direction of motion.

A good circular aimer might collect only the velocity for several turns and the change in heading for several turns (to average them).

Other ideas? Is it worthwhile to save information about your opponent's offense? --Kawigi

This depends heavily on what aiming methods and movement system you are using. Heading change and velocity is good for playback when PatternMatching. 0.9.9.x versions of Marshmallow used to keep information on what dodging angles it had used and how they had worked. Is that what you mean by "opponent's offense"? -- PEZ

That's the general idea, although I was thinking in a more advanced sense - if you try and track their bullets, store the powers the opponent tends to use, and when you got hit, if it appears he used direct, linear, circular, pattern, statistical, or other aiming techniques. Just a thought that probably goes under DodgingBullets. -- Kawigi

Well, I think that might be a bit too much work for a rather slow learning process. Few bots rely on linear or circular aim alone, but instead use them if it seems you are moving in a particular pattern. If you have a good pattern matcher yourself you might use it to neutralise any pattern matcher the enemy might have by making sure you don't go where your own pattern matcher guesses you'll go. Same thing with statistical aim (which is what those 0.9.9.x version of Marshmallow tried to do). But you'll need something along the lines of AntiGravityMovement to do the "avoid" part of the plan. (Which is where Marshmallow failed since it doesn't use anti-grav...). Indeed, if it seems the enemy relies in HeadOnTargeting you might shift to a particular movement strategy where direct aim never hits. (Like moving like Walls or some such). -- PEZ

It's certainly easy to shift to a movement that beats HeadOnTargeting. One thing I toyed with was a bullet dodging technique that wasn't quite perfect, and I had it just print "AM I HIT?" every time it got hit by a defensive virtual bullet. Then it would print "I'M HIT" in onHitByBullet. Virtually all the time, the "I'M HIT" message came amidst a few "AM I HIT" messages. It seems like one could figure out what's hitting me most of the time when I get hit, and use that to my advantage. Pattern-matching yourself is a good idea, too, sometimes, I think I've noticed MogBot doing it. It may be even better to implement multiple pattern-matchers (an acceleration-change in heading one, a velocity-change in heading one, one that just looks for the frequency in changes in direction, etc), to have good defense against any of them (of course, after awhile, there's just a bullet everywhere.

The avoid part can be done without antigravity, antigravity is just a convenient way to do it that doesn't take much processor time if it's done right. SpareParts has a Bullet-dodging movement that works well in general (dodges just HeadOnTargeting, LinearTargeting, and CircularTargeting, both with velocity averaging and without). It just searches for the closest point that it can be at that doesn't intersect any "Virtual Lasers" that I create for each projected bullet. -- Kawigi

Yes, it is because avoiding direct aim is so easy I thought it might be worth it to implement a check for it and the antidote. And while few bots rely on circular or linear aim some does rely on direct aim. Try using direct aim only against Marshmallow and you might find that it is quite effective... The problem with pattern matching is that it is time consuming. Though you can of course record lots of parameters and empirically try to figure what works best. Your search for "safe" points is something like what I mean when I speak about WantedTerrain. -- PEZ

But collecting info doesn't cost anything so you could collect just about anything. Marshmallow certainly does... It's when it comes to using the info where you might pay a price (like with PatternMatching) and certainly when you want to save data between matches, which is where WhatToSaveBetweenRounds (and matches) comes in. -- PEZ



What to save Between Rounds

You can save pretty much everything and anything you collect. You shouldnt variable things between battles (positions, time, etc) but beyond that, keep all important info in static variables and they will stay between rounds.


What to save Between Battles

Ok, I am currently porting various guns from testbots into Raven and his Virtual gun array, but i'm struggling to decide what to store between battles. Although there will probably be more, the key guns i'm looking to port are PatternMatcher, GuessFactorTargetting, VirtualBullets. The question is do i store the stats for each of the individual guns between battles, the stats for the virtual guns, or both???

Also, how many bot's should i leave capacity to store?? -- Brainfade

That can be a tough question. My opinion is that you should probably at least store your VG stats and your info for guns that aren't particularly expensive to save (like your guess-factor gun probably). The problem that leaves you with is if you test a bot and you run long matches, the pattern-matcher might get pretty good, but then in real competition, it will remember that the PM was good but forget that it doesn't have much saved on the pattern-matcher. Something on FloodHT's todo list is adding a pattern-matcher that 'rates' the matches I get (either by how close they are or how long, depending on the nature of the PM) and then stores VG stats for each possible strength (so say I find a pattern of length 20, I'll use that instead of the Guess-factor gun, but if I find a match of length 4, I won't, regardless of how much pattern I know). -- Kawigi

Saving without zip

I'd really like it if someone wrote an article or comment about reading/saving objects without zip. :) I can't find or solve it. I've tried the following, but get an IOException when writing (and I guess loadData function won't work either):

	private static final String statisticsFile = "statistics.dat";
	private static StatisticalInfo statisticalInfo = 
		new StatisticalInfo();
	public static void loadDataFromFile(Portia b)
	{
		try
		{
			FileInputStream file = 
				new FileInputStream(b.getDataFile(statisticsFile));
			ObjectInputStream in = new ObjectInputStream(file);
			statisticalInfo = (StatisticalInfo)in.readObject();
			in.close();
		}
		catch (FileNotFoundException e)
		{
			System.out.println("FileNotFoundException");
		}
		catch (IOException e)
		{
			System.out.println("IOException");
		}	
		catch (ClassNotFoundException e)
		{		
			System.out.println("ClassNotFoundException");
		}
	}
	public static void saveDataToFile(Portia b)
	{
		try
		{
			RobocodeFileOutputStream rfos =
	            new RobocodeFileOutputStream(b.getDataFile(statisticsFile));
			ObjectOutputStream out = new ObjectOutputStream(rfos);
			out.writeObject(statisticalInfo);	
			out.flush();
			out.close();
		}
		catch (IOException e)
		{
			System.out.println("IOException");
		}	
	}
	private static class StatisticalInfo
	{
		public int totalNumberOfMatchesFought = 0;
		public int totalNumberOfMatchesTurnSkipped = 0;
		public int totalNumberOfMatchesException = 0;
		public int totalNumberOfRoundsFought = 0;
		public int totalNumberOfRoundsTurnSkipped = 0;
		public int totalNumberOfRoundsException = 0;
		public int totalNumberOfRounds1st = 0;
		public int totalNumberOfRounds2nd = 0;
		public int totalNumberOfRounds3rd = 0;
	}

--Positive 00:21, 29 August 2009 (UTC)

I haven't done much with writing objects, but I can't see what's wrong with your code above. I have some questions though:

  • Could you try doing e.printStackTrace() to get a more specific error message? That might give some clues.
  • Why don't you want to use zip? CassiusClay's Bee.java writes objects to file very similarly to what you do here, but it feeds through a GZIPOutputStream as well.
  • Writing raw bytes to a file is pretty simple, and will save you a lot of disk space compared to writing objects, I think. But obviously, if you aren't writing much, reading/writing objects is (or should be) much simpler...

--Voidious 01:20, 29 August 2009 (UTC)

Well, I figured that basically just writing a small array of ints to a file won't need compression, so it would be better to write it plainly. I followed your advice and did the e.printStackTrace();, turns out there is a NotSerializableException on StatisticalInfo. I added implements Serializable to private static class StatisticalInfo, and now it works. :) The file is 366 bytes though, and adds all kind of information, which was not exactly what I was looking for. I'll try writing raw bytes or ints to the file. Thanks for the tips! --Positive 01:42, 29 August 2009 (UTC)