<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7523569453469773247</id><updated>2012-01-30T10:39:45.405-08:00</updated><category term='Card Editor'/><category term='Combat'/><category term='Graduation Project'/><category term='Rules Enforcement'/><category term='Off-Topic'/><category term='Compiler'/><category term='Architecture'/><category term='Release'/><category term='Multiplayer'/><category term='Replication'/><category term='Philosophy'/><category term='Last Known Information'/><category term='Rules'/><category term='Java'/><category term='Robotics'/><category term='RMI'/><category term='Undo'/><category term='Programming'/><category term='Damage'/><category term='Abilities'/><category term='ANTLR'/><category term='Min-Max'/><category term='Event Handling'/><category term='Destruction'/><category term='Git'/><category term='AI'/><category term='Maven'/><category term='Proxy'/><category term='Cards'/><category term='Rule-Based'/><category term='Simultaneity'/><category term='Spell'/><category term='Grammar'/><category term='Magic'/><title type='text'>Laterna Magica: MtG programming</title><subtitle type='html'>I'm a twenty year old hobby programmer. I started playing Magic about 7 years ago I think. Unfortunately, not many of my friends play magic, and playing against the computer seems a good alternative.&lt;br&gt;
&lt;a href="http://www.slightlymagic.net/forum/viewforum.php?f=67"&gt;Forum&lt;/a&gt; - &lt;a href="http://code.google.com/p/laterna-magica/"&gt;Project@Google Code&lt;/a&gt;</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>61</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-1779854937031315603</id><published>2012-01-10T10:33:00.001-08:00</published><updated>2012-01-10T10:33:31.831-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Replication'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Entities and values</title><content type='html'>As so often in programming, one big question arose when I worked on my Undo/Replication framework: What is an entity, and what is a value?&lt;br /&gt;&lt;br /&gt;I use the word entity to make clear that I mean something different from objects. An object is something identified by an address pointer, whereas for a primitive value, its bytes in memory directly represent the value instead of an address. What I mean with entity/value is best explained by an example from databases:&lt;br /&gt;&lt;br /&gt;Suppose you have a database table storing the data of a company's employees. There is a surname, a prename, a birth date, and so on. Besides others, there's an address. The simplest approach is to handle each of these items as a table column: Surname and prename are strings, the birthdate uses a special date column type. The address can also be simply taken as a string, but there are other ways:&lt;br /&gt;&lt;br /&gt;An address is composed of a street name, a number, probably also an appartment number, country, state etc. It could be beneficial to store all these data in a table on its own, give it an ID (the database way of saying address), and only reference this ID in the employee table.&lt;br /&gt;&lt;br /&gt;Don't forget the third way! You could use all those columns, but put them directly into the employee table, so there would be prename, surname, birth date, street, stree no., appartment no., etc.&lt;br /&gt;&lt;br /&gt;What's the best? Of course, it depends! Variant one is easy to use, but lacks intrinsic validity checks: When the database doesn't know that something should be an (appartment) number, how should it check it's even a number? Variant two can handle a very specific case "better": two workers have the same address. In this case, the same ID can be referenced multiple times, so that both employees not only have equal addresses but also have the same one. In this particular case, the benefit is not too big, but there are use cases where it's important that something can be referenced. About the third, it really combines the downsides of the both above... I personally see no benefit in using it. It there are, I'm sorry I didn't see them.&lt;br /&gt;&lt;br /&gt;Now, what should have triggered your attention was the word "ID". As soon as something has an identity, it's an entity. Note how the prename is a String, which is an Object type in Java, but only a value. On the other hand, the address in case 2 has an identifier and is an entity.&lt;br /&gt;&lt;br /&gt;And now we're back: What I do is to assign IDs to the objects I want to replicate. The objects have values which can be changed, and I transmit changes like "property XY of object 01 changed from 'ab' to 'cd'."&lt;br /&gt;&lt;br /&gt;And now the big question: what about collections? Should it be "element 'ab' was added to list 01" (i.e. they are entities) or "element 'ab' was added to list XY of object 01?"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-1779854937031315603?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/1779854937031315603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=1779854937031315603' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1779854937031315603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1779854937031315603'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2012/01/entities-and-values.html' title='Entities and values'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3479680695137594721</id><published>2012-01-07T04:55:00.000-08:00</published><updated>2012-01-07T04:55:29.733-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Replication'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Replicating Game States - JGroups</title><content type='html'>So now I've got it. My goals in implementing Replication was to keep it out of the core functionality while still making it easy to use without worrying about replication yourself. How do you do that? By providing a general interface to other software (in my case, that's a listener) and implementing a specific one for the task at hand.&lt;br /&gt;&lt;br /&gt;So my History class, which stores all changes to the managed objects, has a listener for two possible events: A new modification is executed; and the "current" state is moved somewhere else (e.g. undoing something.) My replication listener does not support undo right now, because I haven't figured out how to best identify a state (which is where the program is going) yet, but that should be easy. The other thing, executing modifications, does work. That means, whenever a modification is executed locally, it is sent over the network so that the partner(s) can execute it too.&lt;br /&gt;&lt;br /&gt;The only downside is that the partners also "execute it locally", so I had to use a trick to suppress resending these received modifications. And it works pretty well:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//initialization code&lt;br /&gt;final History h = createHistory(createKey("test-history"));&lt;br /&gt;final JChannel channel = new JGroupsReplicationListener(h) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void receive(Message msg) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Modification m = (Modification) deserialize(msg.getBuffer());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(m instanceof Creation) id = ((Creation) m).getId();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super.receive(msg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}.getChannel();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//executing the actions (creating an object)&lt;br /&gt;h.pushHistoryForThread();&lt;br /&gt;try {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TestBean t = (TestBean) h.getObjectStore().get(Creation.create(new TestBean()));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t.setA(1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t.setB(1);&lt;br /&gt;} finally {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; h.popHistoryForThread();&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//printing the results&lt;br /&gt;System.out.printf("current state: %s%n", h.getObjectStore().get(id));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I made this test a GUI application so that I can control the timing on multiple virtual machines. The &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public void receive(Message msg)&lt;/span&gt;seen above is only needed for this Test, again. It's not necessary to do something like that to get the replication. The code in the middle looks exactly the same as the example last time, except I haven't hidden some of the details in a &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;setTest()&lt;/span&gt; method.&lt;br /&gt;&lt;br /&gt;Now what's probably the most interesting is how the networking works; let me tell you, I didn't write any! As hinted by me before, and in the title of course, is that I have tried JGroups to do this. In JGroups, you create Channels and let them join into a cluster; all channels in the cluster can receive and send messages, either to only only one recipient, or to all at the same time. JGroups provides a configurable protocol stack that takes care of reliability, synchronity and such things. For my tests, I have used a UDP-based protocol stack. For wide-area connections, a TCP-based approach is probably easier.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3479680695137594721?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3479680695137594721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3479680695137594721' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3479680695137594721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3479680695137594721'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2012/01/replicating-game-states-jgroups.html' title='Replicating Game States - JGroups'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-1214319251925162015</id><published>2012-01-05T12:13:00.000-08:00</published><updated>2012-01-05T12:15:25.093-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Replication'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Replicating Game States - Working code!</title><content type='html'>Before you get too excited - no, there's no multiplayer yet (well, who would have guessed that...). There is, however, working code for undoing and redoing actions (even with multiple, branching histories), and even for replicating the state on multiple virtual machines.&lt;br /&gt;&lt;br /&gt;The key to make such a library is that it looks like ordinary Java code from the outside, so that others can use your code without wondering why it looks so weird. Let's take a look:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//Test t = new Test(); is an attribute&lt;br /&gt;&lt;br /&gt;//replaced angle with square brackets because of HTML markup&lt;br /&gt;&lt;b&gt;List[StateStamp] states = new ArrayList[StateStamp]();&lt;/b&gt;&lt;br /&gt;&lt;span style="color: #674ea7;"&gt;&lt;b&gt;History h = createHistory(createKey("test"));&lt;/b&gt;&lt;br /&gt;&lt;b&gt;h.pushHistoryForThread();&lt;/b&gt;&lt;br /&gt;try {&lt;/span&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;states.add(h.getCurrentState()); // 0&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setTest(t);&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;states.add(h.getCurrentState()); // 1&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getTest().setA(1);&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;states.add(h.getCurrentState()); // 2&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getTest().setB(1);&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;states.add(h.getCurrentState()); // 3&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Deletion.delete(getTest());&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;states.add(h.getCurrentState()); // 4&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h.goToState(states.get(3));&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h.goToState(states.get(2));&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h.goToState(states.get(1));&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h.goToState(states.get(0));&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print();&lt;br /&gt;&lt;span style="color: #674ea7;"&gt;} finally {&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h.popHistoryForThread();&lt;/b&gt;&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's probably easy to to guess that &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;print();&lt;/span&gt; prints the current state of the test object. Besides that, the only code that looks different from normal operations is marked in bold. Most of it is only necessary for this test case: storing the previous states so that you can go back. Really mandatory is only the colored code (and the actual functional code of course, but that doesn't count.)&lt;br /&gt;&lt;br /&gt;Of course, a history for the modifications must be created. Then, the history is set for the thread: Code running in the thread (that is, everything in this method, and nothing more) can access the history without passing it as a parameter (which would make using the library awkward) or declaring it as a static variable somewhere (which is ugly from an architecture standpoint.) Everything after this is wrapped in try/finally, so that the last method is not skipped in case of an exception: this removes the association of the history with the current Thread.&lt;br /&gt;&lt;br /&gt;Of course, implementing &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;setTest()&lt;/span&gt;, &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;setA()&lt;/span&gt; etc. is  different from a usual simple setter. But the point here is that &lt;i&gt;using&lt;/i&gt;  the code is as simple as shown here. The result is this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Test@732A54F9[a=0, b=0] not in store&lt;br /&gt;Test@7A6D084B[a=0, b=0] in store&lt;br /&gt;Test@7A6D084B[a=1, b=0] in store&lt;br /&gt;Test@7A6D084B[a=1, b=1] in store&lt;br /&gt;Test@7A6D084B[a=1, b=1] not in store&lt;br /&gt;Test@15301ED8[a=1, b=1] in store&lt;br /&gt;Test@15301ED8[a=1, b=0] in store&lt;br /&gt;Test@15301ED8[a=0, b=0] in store&lt;br /&gt;Test@15301ED8[a=0, b=0] not in store&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You see here how the test object runs through different states up to its deletion, then goes back to its initial state. What's not shown here, but is working, is adding another branch to this history. Of course, the actual objects have only one state at a time, but the history can be switched arbitrarily between multiple histories:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-LnpJy_tAQYg/TwYBWOkkgSI/AAAAAAAAAHo/T5QcuatniFM/s1600/History.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-LnpJy_tAQYg/TwYBWOkkgSI/AAAAAAAAAHo/T5QcuatniFM/s1600/History.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;This is the actual test case I ran: I went back to the state after creating the test object, changed its a value, and then switched over to the state before, and then after, deleting the test again.&lt;br /&gt;&lt;br /&gt;I &lt;i&gt;have&lt;/i&gt; code for replication of states going, but it's not yet as clean as for undo: a lot of handling is necessary for the network, which should be transparent to the user. I'll show it once I'm finished, but in the meantime you can look &lt;a href="http://code.google.com/p/laterna-magica/source/browse/src/main/java/net/slightlymagic/objectTransactions/jgroups/Test.java?repo=object-transactions"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-1214319251925162015?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/1214319251925162015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=1214319251925162015' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1214319251925162015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1214319251925162015'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2012/01/replicating-game-states-working-code.html' title='Replicating Game States - Working code!'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-LnpJy_tAQYg/TwYBWOkkgSI/AAAAAAAAAHo/T5QcuatniFM/s72-c/History.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-848343106094705206</id><published>2011-12-31T19:37:00.000-08:00</published><updated>2011-12-31T19:37:44.183-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Magic'/><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><title type='text'>Quantum Mechanics and Magic AI</title><content type='html'>Okay, the title is kind of a stretch, but Quantum Mechanics sounds so much cooler than probability...&lt;br /&gt;&lt;br /&gt;So what do I want to talk about? One thought that I often have is that the computer has the advantage of perfect memory. If it once views your hand, it can perfectly memorize all the cards in it. It follows that it can say that as long as a card doesn't leave your hand, you &lt;i&gt;still&lt;/i&gt; have it in hand. But when the game progresses, things get more vague: You shuffle cards into your library without revealing them before. Here, probability comes into play.&lt;br /&gt;&lt;br /&gt;I assume that the computer knows my deck list for simplicity. (It could even use heuristics to guess what cards might be in your deck, but that only complicates the matter.) At the beginning of the game, before even drawing your opening hand, each card in the library has an equal chance of being one of the, say, 60 cards in your deck. For example, in your mono green deck, a card has a 24 in 60 chance of being a forest. These chances don't change as you draw seven cards, at least from the computer's point of view. Even so, the computer can say that the probability of you having a Giant Growth in hand is (# Giant Growth in deck)/(# cards in deck)*(# cards in hand), and it can have "fear" that you might play that card during combat. The greater the probability, the greater the fear.&lt;br /&gt;&lt;br /&gt;Now comes the "collapse of the wave function": the computer observes the cards in your hand. (You see, I can even use QM terminology here ;)) Suddenly, the probability of every of the cards in your hand becomes 100% for the card the AI has observed. Technically, as the hand is not a sorted zone, the AI should not remember which card is which. Let's say you have 4 different cards in hand, then the AI can assume that every of the cards has a 25% probability of being any of these cards.&lt;br /&gt;&lt;br /&gt;When you now shuffle one card back into your library, nothing really changes, except that there's only 3 cards for a total of 75% per previously observed card.&lt;br /&gt;&lt;br /&gt;I hope that it's clear what I'm saying. I have the feeling to make too many words about a simple concept, yet at the same time I feel that all this seems abstract and not very understandable... well, I should have made some images, but I'm too lazy...&lt;br /&gt;&lt;br /&gt;Let me end with this: Magic is a game of uncertainty, and luckily the computer has the capabilities to process these. When an AI can make decisions based on what it sees, why not on what it doesn't see? Assigning these possibilities is pretty simple; every card in the game has a total of 100% of being some card from the deck list, and every card in the deck list is represented to 100% among all cards in the game.&lt;br /&gt;The problem is to design the AI to use that information; it's often hard enough to process the known information, so even more the unknown. But in principle, there's no difference. And even if it is too hard, there are some shortcuts: If you have only one Morph card and play a Morph card, the AI knows which it is, even though it's face down. Such probability collapses can happen all the time, and it would be a waste to let them go unconsidered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-848343106094705206?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/848343106094705206/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=848343106094705206' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/848343106094705206'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/848343106094705206'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/12/quantum-mechanics-and-magic-ai.html' title='Quantum Mechanics and Magic AI'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-716655878005330758</id><published>2011-12-31T08:53:00.000-08:00</published><updated>2012-01-05T12:13:52.057-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Replication'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><category scheme='http://www.blogger.com/atom/ns#' term='Event Handling'/><title type='text'>Replicating Game States - revised</title><content type='html'>The &lt;a href="http://laterna--magica.blogspot.com/2010/12/distributed-objects-undo-and-proxies.html"&gt;last time&lt;/a&gt; I talked about my Undo system, which should also allow multiplayer through its structure of storing actions that can be transmitted over the network, was a long time ago. I don't want to directly talk about this today; you'll see.&lt;br /&gt;&lt;br /&gt;Especially one thought has gone through my mind in the last weeks: Replicating a state across the network properly is nothing specific to Magic - so why do I implement it in the Core of Laterna Magica? I thought about it, and there doesn't seem to be a specific reason for doing so. Another result of that thought was that there are probably complete libraries out there which do exactly what I want. Well, that's unfortunately not the case, so I'm stuck/blessed with implementing it myself.&lt;br /&gt;&lt;br /&gt;I haven't really gotten into development yet. I have a rough skeleton, but no really fancy functionality yet. But I'm having my thoughts. I think stepping away from the application's needs and looking at the general requirements of such a library help in developing a better piece of software. I haven't done this for my current undo system, which I find kind of disappointing. Normally, I don't have problems with spotting such generalizations.&lt;br /&gt;&lt;br /&gt;Okay, back to topic: what does a library for state replication need?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There needs to a notion of what has happened on a higher, application level: Transactions&lt;/li&gt;&lt;li&gt;Transactions consist of atomic modifications, which are the core of the state&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Optionally, they may also consist of other sub-transactions&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Transactions and modifications must be transmittable over the network. If the whole state is represented as a tree of subtransactions under a root-transaction, then there's of course the need to transmit unfinished transactions. Otherwise, it might be enough to transmit finished transactions. I think that the first is generally more flexible. It might be a good idea to give the user a chance to mark transactions as atomic so that it's only transmitted when it is finished.&lt;/li&gt;&lt;li&gt;Probably the most interesting requirement is that, of course, after transmitting the state, both/all states have to be the same!&lt;/li&gt;&lt;li&gt;The system should be able to detect that there are state conflicts and that a transmitted transaction can't be carried out locally.&lt;/li&gt;&lt;ul&gt;&lt;li&gt;When we're already speaking of it, it's probably nice (but also probably impossible, so see that as an utopic remark) to resolve/merge such conflicts.&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;Now guess what I have an example for! Of course for the "most interesting" one in my list! It might seem at first that by defining modification, and making them granular enough to catch all actions in a system, there is no way that states may get out of sync. That's unfortunately not true. Let's take a simple case. This case actually only has a theoretical problem, but it's easier to illustrate and serves&amp;nbsp; the purpose. Our state consists of two variables, foo and bar. The user may only set foo, and when he does so, the program automatically changes bar. Now consider this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-t7BBESms_V8/Tv9KGbUUg9I/AAAAAAAAAHc/XsJJBpD7LVg/s1600/StateReplication.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="216" src="http://1.bp.blogspot.com/-t7BBESms_V8/Tv9KGbUUg9I/AAAAAAAAAHc/XsJJBpD7LVg/s400/StateReplication.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;When the state replication system transmits these two modifications, the receiver does its own set operation on bar, because foo has changed. Then, the original set operation is transmitted, setting bar another time (fortunately to the same value).&lt;br /&gt;&lt;br /&gt;Until now, I thought that this particular case is just me wanting to be clean. Now that I wrote this, I even see more problems, real ones: If we're able to detect conflicts, then this shouldn't even run through, because the last modification should be the second but really is the third. And even if we don't: this second modification in State 2 needs to be transmitted to State 1. Even if that doesn't result in errors, which is likely, it will waste network bandwidth at least.&lt;br /&gt;&lt;br /&gt;Now for the real problem I spotted here before: Let's replace "bar = 1" with "put a token onto the battlefield". Setting a number multiple times is redundant, but creating an Object is not. There is a very definite difference between one and two tokens, you know!&lt;br /&gt;&lt;br /&gt;Now, let's solve these problems. There are basically two ways that can lead to this problem: a complex setter, and events.&lt;br /&gt;&lt;br /&gt;In the first case, the setter for foo includes code that calls the setter of bar. The problem here is that our replication library can't know that setting foo doesn't really do this: it actually sets foo and bar. So, refactor foo's setter so that it does only what it says, and not more. Hide this from the user by adding another method &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;doSomething()&lt;/span&gt; that does what he previously did by setting foo.&lt;br /&gt;&lt;br /&gt;Second is events. In this case, the change to foo fires an event  that leads to bar being changed as well. In this case, the simple answer  that is not so simple to implement is: don't fire events while  replicating the state. The more complex answer that is simpler to implement is that this is similar to the first case: Don't write events that fire on state changes and perform state changes. Instead, fire a new Event in &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;doSomething()&lt;/span&gt; that triggers the change. As the state replication won't call &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;doSomething()&lt;/span&gt;, everything is fine.&lt;br /&gt;&lt;br /&gt;Both solutions shown here require changes in the application, not in the library. This might seem bad, but it's actually logical: The library may sanely assume that variables change and objects are created, and that it may do that by itself as well. If the state does not fulfill the contract that it just contains values, but instead employs complex logic that the library can't predict, the task is impossible. The library needs some way to change values without triggering application logic.&lt;br /&gt;&lt;br /&gt;Thanks for staying wiht me, and a happy new year to all of you!&lt;br /&gt;&lt;ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-716655878005330758?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/716655878005330758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=716655878005330758' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/716655878005330758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/716655878005330758'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/12/replicating-game-states-revised.html' title='Replicating Game States - revised'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-t7BBESms_V8/Tv9KGbUUg9I/AAAAAAAAAHc/XsJJBpD7LVg/s72-c/StateReplication.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5501773756636546445</id><published>2011-12-30T12:24:00.000-08:00</published><updated>2011-12-30T12:24:49.254-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Maven'/><title type='text'>Java 5 compatibility is hard</title><content type='html'>From the times when I read the bug thread of &lt;a href="http://mtgrares.blogspot.com/"&gt;Forge&lt;/a&gt; (about a year ago or so), it seemed to me that a lot of users, especially those on Macs or at work, were still using Java 5. I have no idea how it is now, but I still try to code and compile Java 5 compatible for exactly that reason.&lt;br /&gt;&lt;br /&gt;Usually, it works out one way or another, but not today. I wanted to try JGroups (a story for another time, after I actually succeeded in it), but even the releases that are advertised (if you can call it that) as Java 5 compatible seem to be compiled in the Java 6 binary format. Since only part of the featureset I'm trying to implement depends on JGroups, I wanted to at least create compatible class files, even if using these features will fail on a Java 5 runtime.&lt;br /&gt;&lt;br /&gt;There are basically two inputs to the compiler: my own Java 5 compatible code, and the already compiled Java 6 compatible bytecode. My output of choice was Java 5 compatible bytecode. After all, this output does not contain anything not Java 5 compatible, so I thought I was good to go. Using a Java 5 compiler will of course fail, because it can't read the necessary classes, but Using a Java 6 compiler with compatibility settings should work...&lt;br /&gt;&lt;br /&gt;There are a few features new in Java 5, like generics, annotations, enums and for-each loops. It sounds reasonable that compiling Java 5 source code to Java 1.4 byte code won't work. Java 6 also has a new byte code format. It supports a new verification mechanism, and while &lt;a href="http://www.oracle.com/technetwork/java/javase/compatibility-137541.html#incompatibilities"&gt;the compatibility notes&lt;/a&gt; say that the compiler outputs this format only "by default", there is no way (I have found, at least) to change it: The most obvious way of setting the compiler arguments: &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;javac -source 1.6 -target 1.5&lt;/span&gt; gives an errror that &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;source release 1.6 requires target release 1.6&lt;/span&gt; - which exactly contradicts what is said in the compatibility notes.&lt;br /&gt;&lt;br /&gt;It turns out that the Java 6 compiler can take Java 6 bytecode input even if the source setting is 1.5. That there's an error for 1.6 + 1.5, even though the Java language hasn't changed, is kind of absurd in my opinion... well, better having an absurd solution than none at all...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5501773756636546445?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5501773756636546445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5501773756636546445' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5501773756636546445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5501773756636546445'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/12/java-5-compatibility-is-hard.html' title='Java 5 compatibility is hard'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-1422429753167438766</id><published>2011-12-26T15:58:00.000-08:00</published><updated>2011-12-26T15:58:23.094-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Maven'/><title type='text'>Getting more out of Maven</title><content type='html'>When I switched to Maven, I knew I wasn't using all of it's potential. I'm sure I still don't, but I've learned much nontheless. For the last months, I worked (for pay, that is) on another software project. The necessary steps to build the software were nontrivial, and I had the task and thus the time to research how to do it. Over the months I worked there, the project's POM has become a stably working thing, whihc indeed is nice when trying a relatively new technology. Speaking of new technologies, I finally managed to learn a little LaTeX, when we had to do the docs in the end... but that's another story. I wonder if I'll be able to ever bring that into a Magic context.&lt;br /&gt;&lt;br /&gt;Now that my internship/scholarship ended, I had a little time and wanted to clean up the messy POMs I previously created. My first step was to update my eclipse version. 3.7 Indigo comes with a new version of the Maven plugin. While I was sceptical during my internship, I found that it's pretty usable now. It even has some nice features that make it feel superior to the old one, e.g. the ability to jump to definitions of replacement variables, plugins, dependencies etc.; and the ability to view the "effective" POM. While the old version had that, I was never really sure it worked... could be that my POMs were just so simple that the physical and effective POMs were the same... Whatever, what I experienced was that viewing the effective POM of your project actually shows you how much configurations there are you could take a look at.&lt;br /&gt;&lt;br /&gt;I have the feeling that when one tries a tool to automate something, it first seems very nice and practical. Unfortunately, many tools show the "bad behavior" that when the ammount of things to manage grows, even that becomes confusing.&lt;br /&gt;The same thing was with Maven. The only feature I previously used was to declare a project's dependencies. As the Laterna project grew by separating parts of the software into different projects, there were now many components which had dependencies. Let's say I discover a bug in the Config file format project and increment its version number. If multiple modules depend on the file format, I have to change three things in total: the file format version number, and the two modules' dependency version numbers.&lt;br /&gt;As an alternative, I could try to have the dependency number only in one module, and since the second module has a dependency on the first (let's just assume that), it has an indirective dependency on the (now) new version. But that's also not a clean way of doing it; at least if the second really needs Config on its own. When the first module is changed so that it doesn't need config any more, this would mean I had to include it into the second then, and that's not too nice.&lt;br /&gt;&lt;br /&gt;Well, luckily, there's two features of Maven which do the job together. The first are the "management" sections: these declare configurations (such as versions) for dependencies, plugins and other things, without making them effective. Together with the second, this even sounds like something useful: parent projects.&lt;br /&gt;Every project can have a parent project from which it inherits its configurations. The dependencyManagement section allows me to specify dependency versions for every library in the parent POM without mandating that every child POM depends on all the libraries. The child POM can then add the identification of the library in the dependency section, and all the configuration from the management section applies to it.&lt;br /&gt;The same goes for plugins: I can specify that if a project uses the antlr plugin, it wants to invoke antlr's code generator so I don't have to duplicate that code over and over.&lt;br /&gt;&lt;br /&gt;So, I learned something new and exciting about Maven. I really like the design of it, how the predefined lifecycle is combined with the ability to add any plugin to do whatever job is necessary, and not only there's a way to do the necessary things, there's even a way to specify them in a clean, reusable manner.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-1422429753167438766?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/1422429753167438766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=1422429753167438766' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1422429753167438766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1422429753167438766'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/12/getting-more-out-of-maven.html' title='Getting more out of Maven'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7313355100101051556</id><published>2011-10-24T03:01:00.000-07:00</published><updated>2011-10-24T03:05:02.665-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='ANTLR'/><category scheme='http://www.blogger.com/atom/ns#' term='Grammar'/><title type='text'>The Oracle Parser</title><content type='html'>Besides the structural changes to my software, the probably most visible evolution was the creation of a grammar to parse card text. To test my works, I use &lt;a href="http://www.slightlymagic.net/forum/viewtopic.php?f=27&amp;amp;t=1347"&gt;Arch's Magic Data&lt;/a&gt;, Version 2011-10-01, from where I extracted the abilities and replaced card names by a "~".&lt;br /&gt;Based on that, it seems that there are currently 19710 abilities (including duplicates) in Magic, of which I'm able to parse 5661 abilities. Of these, 4282 are keyword abilities (I didn't add the following keyword abilities yet: Landwalk, Protection, Affinity, Bands with others, Offering, Splice, Typecycling). This makes a total of 1379 non-unique, non-keyword abilities I currently support. It sounded better before calculating the numbers for the post :S&lt;br /&gt;&lt;br /&gt;Below is a diagram showing the evolution of my parser; the builds are made on a new-feature-available basis, and I usually deleted the statistics of any build that broke something that worked before.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/-1YBlClTQgVU/TqUbNnlyV3I/AAAAAAAAAGY/cvl3T1fn0Ak/s1600/Abilities.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-1YBlClTQgVU/TqUbNnlyV3I/AAAAAAAAAGY/cvl3T1fn0Ak/s1600/Abilities.png" /&gt;&lt;/a&gt;&lt;br /&gt;For a rough guidance, the builds 1 through 13 were made on October 18&lt;sup&gt;th&lt;/sup&gt;, #14 to #19 was on 19&lt;sup&gt;th&lt;/sup&gt;, #20 to #28 was on 20&lt;sup&gt;th&lt;/sup&gt;, the others were on 21&lt;sup&gt;st&lt;/sup&gt; and 22&lt;sup&gt;nd&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;Between #10 and #17, you can see how I added the keyword abilities, sometimes only a few, sometime a lot.&lt;br /&gt;The next ten builds seem to have done little, although it is a total of 300 abilities that were now supported. The changes included generalizing costs and effects, so that both "&lt;i&gt;Discard a card: do something.&lt;/i&gt;" and "&lt;i&gt;Pay something: Discard a card.&lt;/i&gt;" worked; additional costs: "&lt;i&gt;As an additional cost to cast ~, pay something.&lt;/i&gt;"; generalizing object matching for effects like "&lt;i&gt;Counter target instant or sorcery spell.&lt;/i&gt;"; and even modal effects like "&lt;i&gt;Choose one — ~ deals 3 damage to target creature; or ~ deals 3 damage to target player.&lt;/i&gt;"&lt;br /&gt;The small cut of 103 abilities that follows was a bug that accepted things such as "&lt;i&gt;Counter target spell. You gain 3 life.&lt;/i&gt;", although gaining life was not yet supported: If there was a second sentence that could not be parsed, it was dropped without errors.&lt;br /&gt;Up to the present, I mostly extended object matching for things like "nonblack artifact creatures", generalized abilities (instead of only "Draw a card", I now also support "Target player draws two cards" and "each opponent draws X cards") and added easy new ones like life gain, life loss and paying life for a total of 257 newly supported abilites.&lt;br /&gt;&lt;br /&gt;But what is it worth to parse abilities without giving my program the opportunity to understand them? Remember, what I did is parse rules text, not (yet) support new cards/abilities in Laterna Magica. Out of the unstructured text format that is magic rules text, the parser creates a tree structure that is easier for software to work with. To show a few selected supported abilities:&lt;br /&gt;&lt;table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-xNdzlR5FbpA/TqU0o8vxL_I/AAAAAAAAAGg/wWqXrXeuieE/s1600/ability0.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-xNdzlR5FbpA/TqU0o8vxL_I/AAAAAAAAAGg/wWqXrXeuieE/s1600/ability0.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;"As an additional cost to cast ~, discard a card."&lt;br /&gt;The "you" shows which player it is who discards a card. An alternative could be "target player".&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-CUDb82yj-lE/TqU0pHMcsxI/AAAAAAAAAGk/jzI7zCn5ztU/s1600/ability1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-CUDb82yj-lE/TqU0pHMcsxI/AAAAAAAAAGk/jzI7zCn5ztU/s1600/ability1.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;"Choose one — Counter target red spell; or destroy target red permanent."&lt;br /&gt;This one is a "SPELL_EFFECT": not an ability, but a part of a spell's resolution effect.&lt;br /&gt;As you see, "Choose two" and "Choose X" are equally supported.&lt;br /&gt;The "counter" and "destroy" effects both have their own targets that must be red spells or red permanents, respectively. The logical "and" shows that both conditions are necessary.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-nMWa_hUXiiI/TqU0pvYaFvI/AAAAAAAAAGs/sGgEFDI3jmA/s1600/ability2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-nMWa_hUXiiI/TqU0pvYaFvI/AAAAAAAAAGs/sGgEFDI3jmA/s1600/ability2.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;"When ~ enters the battlefield, destroy target nonartifact, nonblack creature."&lt;br /&gt;&amp;nbsp;The trigger is actually not so nice yet. I will restructure this, so that it's not "ETB SELF" but "ChangeZone ANY Battlefield SELF" or something like that.&lt;br /&gt;The destroy effect, again, uses logical predicates, this time including negations.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-frZ_nCWpvnc/TqU0qLASD-I/AAAAAAAAAGw/XaFC9DYG2Xk/s1600/ability3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-frZ_nCWpvnc/TqU0qLASD-I/AAAAAAAAAGw/XaFC9DYG2Xk/s1600/ability3.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;"Suspend 4—{1}{U}"&lt;br /&gt;The keywords are all pretty similar. "KEYWORD" is a marker; neither "ACTIVATED_ABILITY" not "STATIC_ABILITY" nor anything else is universally applicable for keywords. The suspend keyword has a number of counters and a cost.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;That's it for now. I hope you get something out of this post!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7313355100101051556?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7313355100101051556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7313355100101051556' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7313355100101051556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7313355100101051556'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/10/oracle-parser.html' title='The Oracle Parser'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-1YBlClTQgVU/TqUbNnlyV3I/AAAAAAAAAGY/cvl3T1fn0Ak/s72-c/Abilities.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2502802721324208723</id><published>2011-10-21T03:16:00.000-07:00</published><updated>2011-10-21T03:34:28.936-07:00</updated><title type='text'>What has gone on - Part 2</title><content type='html'>Okay, I'm back, and as Google Code nowadays also supports Git, I stayed with it. The wretched old structure where LaternaMagica resided in the same SVN repository with diverse libs is now also gone, as google seems to support multiple repos per "project" now. So you can check out and commit changes to the different libraries without incrementing the revision number of the other projects. And for everyone too lazy to explore the multitude of individual projects, &lt;a href="http://code.google.com/p/laterna-magica/wiki/Projects"&gt;here&lt;/a&gt; is an overview.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2502802721324208723?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2502802721324208723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2502802721324208723' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2502802721324208723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2502802721324208723'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/10/what-has-gone-on-part-2.html' title='What has gone on - Part 2'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-6077008137719893756</id><published>2011-10-21T01:28:00.000-07:00</published><updated>2011-10-21T02:38:07.854-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Git'/><title type='text'>What has gone on - Part 1</title><content type='html'>A lot, actually! Ever since the beginning of August, I found some time again to work on Laterna Magica. In my time "away" I have grown fond of the &lt;a href="http://git-scm.com/"&gt;GIT&lt;/a&gt; version control system, using it together with &lt;a href="http://www.syntevo.com/smartgit/index.html"&gt;SmartGit&lt;/a&gt;.&lt;br /&gt;Two of the things I like about it: Branching is not only central to, but also easily done in GIT, and you quickly lose your fear of it. The second is that it is distributed, which was important because I have no private SVN server available where I work now. The nice extra of this is that many operations are just so much faster than with SVN, because branching, history, committing etc. are all local. The downside is that, even though I try to be a good kid and commit and document my changes, I am the only one to see them...&lt;br /&gt;Which has to be changed! I'm going to upload my projects now to a git SCM site and them come back to post a little about my changes!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-6077008137719893756?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/6077008137719893756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=6077008137719893756' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6077008137719893756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6077008137719893756'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/10/what-has-gone-on.html' title='What has gone on - Part 1'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5670423743469598481</id><published>2011-01-06T11:45:00.000-08:00</published><updated>2011-01-06T11:45:23.043-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule-Based'/><title type='text'>Forward and backward chaining</title><content type='html'>In the context of rule-based systems, programming means to create rules which are applied by the engine to a fact base. Forward and backward chaining describe the two possible ways how the engine may achieve this. I found a neat explanation &lt;a href="http://downloads.jboss.com/drools/docs/5.1.1.34858.FINAL/drools-expert/html/ch01.html#d0e29"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Forward chaining starts with the facts: If a fact which triggers a rule exists, the rule is activated and may in turn modify other facts, and so on. A possible rule could be "if the floor is dirty, clean it"; it specifies when to do it&lt;br /&gt;&lt;br /&gt;Backward chaining starts with a goal and tries to find the necessary rules to achieve it. The engine goes back through the chain of causality to find the first step. A backward changing rule could be "to achieve a tidy floor, clean it"; it specifies how the goal can be reached, but doesn't explicitly state when to do it&lt;br /&gt;&lt;br /&gt;Both mechanisms are of course constantly used by humans, but the reasoning seems to be very different, or otherwise most rules engines would support both.&lt;br /&gt;&lt;br /&gt;Forward chaining can be used for implementing the rules, like triggered abilities, and possibly state based actions. Even activated abilities could be handled by modelling them as a sort of triggered ability which "triggers" on playing it.&lt;br /&gt;Characteristic changing effects can be handled effectively, because a rules engine watches its facts and can determine whether it's necessary to recalculate characteristics.&lt;br /&gt;This is actually the main reason I want to use a rules engine. My current approach recalculates characteristics every time they are read, which... well, I'm repeating myself. By getting rid of this need, I hope to have easier access to the core engine from other parts, like AI, GUI and network.&lt;br /&gt;&lt;br /&gt;Backward chaining is interesting especially for the AI: Backwards chaining tries to find a way from a current state to a goal, which is exactly what's done in a game. Drools, the rules engine I'm most likely going to use, does not support backward chaining yet, but the project plans to support it in a future release. Since the AI will be modular, such a backwards chaining AI could be supported once it's possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5670423743469598481?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5670423743469598481/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5670423743469598481' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5670423743469598481'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5670423743469598481'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2011/01/forward-and-backward-chaining.html' title='Forward and backward chaining'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5387774844884126403</id><published>2010-12-26T09:32:00.000-08:00</published><updated>2010-12-30T16:19:33.836-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><category scheme='http://www.blogger.com/atom/ns#' term='Proxy'/><category scheme='http://www.blogger.com/atom/ns#' term='RMI'/><category scheme='http://www.blogger.com/atom/ns#' term='Multiplayer'/><title type='text'>Distributed objects, undo and proxies</title><content type='html'>I have mentioned a couple of times that the undo system should enable multiplayer games, because every action is encapsulated as an object which can be transmitted over the network and executed there.&lt;br /&gt;&lt;br /&gt;Well, that's only half of the story. For the action to do something, it needs references to the affected objects. This means that transmitting an action also means transmitting the dependent objects; in the worst case, the whole game-state is transmitted every time, because all of the objects are interdependent.&lt;br /&gt;&lt;br /&gt;And even so, the desired effect is not achieved: the action operates on the transmitted game state, not the one which belongs to the communication partner. The same is true for writing a game state to file, to restore it at a later time. I have been aware of this flaw for quite some time, but ignored it as a problem to the distant future. Now that I have to rewrite the characteristics system anyway, I might as well face this problem too. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;One possible solution is proxy objects: A proxy object acts as the communication link between a client and a provider. Proxy objects are commonly used for distributed systems, where the proxy implements the intricacies of accessing the provider over the network.&lt;br /&gt;My use would be similar; instead of passing objects directly to an action, only the proxy is passed. The proxy communicates with the actual object, but instead of transmitting it over the network, the proxy is attached to the other side's equivalent of the object. &lt;br /&gt;&lt;br /&gt;This approach has its own downsides, of course: Java has a built-in proxy mechanism which makes it relatively easy to implement transparent proxy classes. It requires to use interfaces for everything, which isn't automatically a bad thing. Designing the interface is moreso:&lt;br /&gt;&lt;br /&gt;RMI, Remote Method Invocation, uses proxy classes for transparent - well - remote method invocation. Because network communication can go wrong, this means that every method in those interfaces must declare RemoteException as a possible error. Declaring this exception seems very dirty, because it doesn't have anything to do with what the interface is about. Not declaring it takes me the (direct) possibility to use RMI for a central game state instead of duplicated game states. A workaround would be to use another proxy around the RMI proxy, which only throws an unchecked exception. This seems dirty again, but better than declaring protocol-specific exceptions in an application-level interface. &lt;br /&gt;&lt;br /&gt;"Re-attaching" a proxy to the game state on another machine is the next problem: somehow, the transmitted proxies must get access to the new gamestate on the receiver's side. I think that a custom ObjectInputStream which provides this information may cover that. &lt;br /&gt;&lt;br /&gt;The third, and probably hardest problem, is the question of object identity: How do I identify identical objects on both sides, so that the right object is attached at the receiver side? RMI's advantage in this regard is that it's not even necessary to do that, because objects are not duplicated. I have yet to find a solution for this problem, wish me luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5387774844884126403?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5387774844884126403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5387774844884126403' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5387774844884126403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5387774844884126403'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/12/distributed-objects-undo-and-proxies.html' title='Distributed objects, undo and proxies'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2357264284805881719</id><published>2010-11-18T14:48:00.000-08:00</published><updated>2010-11-18T14:50:59.020-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule-Based'/><title type='text'>Rule-Based Programming and Magic</title><content type='html'>Although I don't have time to work on Laterna Magica right now, I have the occasional thought on it. Before the big pause, I noticed a major flaw in my implementation of characteristics. The problem is that every read-operation on the characteristics refreshes the status. This status change means that only in-game operations may read characteristics; I mentioned a couple of times that, to achieve independence of game and GUI, the GUI must not change the game state.&lt;br /&gt;&lt;br /&gt;...and what does this mean in the context of rules-based programming? Let me first explain that term in greater detail: In the last months, we familiarized with the (unfortunately commercial) rule-based programming language "JESS", its interpreter is implemented in Java.&lt;br /&gt;A rule based system operates on a fact base. Facts can be roughly compared to variables or objects. The second important part is, of course, the rules. Rules operate on facts and may be activated whenever a fact changes. An activated rule is not immediately executed! Instead, a call to run() causes the engine to process the activated rules, which may in turn change facts and activate even more rules.&lt;br /&gt;&lt;br /&gt;And now combine the two thoughts: The reason for me to re-evaluate characteristics every time they are accessed was to make sure no out-of-date data is used. With a rule-based system keeping track of the game state, this becomes unnecessary, because the rule system would ensure that characteristics are refreshed when necessary. And without the need to do permanent refreshes, there's no problem with the GUI reading characterics.&lt;br /&gt;&lt;br /&gt;If anyone knows some free, Java-based rules engines, this would be a great input. I haven't done any research on this, and I won't do it until I come around programming LM again, but concrete user stories and recommendations are always great to hear.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2357264284805881719?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2357264284805881719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2357264284805881719' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2357264284805881719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2357264284805881719'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/11/rule-based-programming-and-magic.html' title='Rule-Based Programming and Magic'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-296337116597107218</id><published>2010-11-18T05:31:00.000-08:00</published><updated>2010-11-18T05:31:48.194-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Off-Topic'/><title type='text'>Handling Arrays in web services</title><content type='html'>I'm currently working on an assignment to implement a simple web service, and started using the following tutorial:&amp;nbsp;&lt;a href="http://www.theserverside.de/webservice-in-java/"&gt;http://www.theserverside.de/webservice-in-java/&lt;/a&gt; (German). I'm not sure if that's a general problem, but the auto-generated WSDL specification, and java's wsimport code generator handle arrays... strangely. So here is my ArrayHelper class. I was searching for something along its lines, but found nothing.&lt;br /&gt;&lt;br /&gt;The  problem: The code generator generates classes like StringArrayArray  instead of using java String[][] objects. A StringArrayArray object is  merely a wrapper around a List&amp;lt;StringArray&amp;gt;, and so on.&lt;br /&gt;&lt;br /&gt;The  solution: Use reflection to determine what type of wrapper you have to  deal with (check the generic parameter of the list) and recursively  convert the wrapped lists into real arrays.&lt;br /&gt;&lt;br /&gt;The file: is free to use and modify, comes without any kind of warranty, and is available &lt;a href="http://www.slightlymagic.net/forum/download/file.php?id=2516"&gt;here&lt;/a&gt;. It was actually tested with String[][]; I'd strongly suggest testing with primitive types before using it. please leave comments if any problems ocurr. The code is nearly 130 lines in total, 70 lines being comments (recursion, you know).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-296337116597107218?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/296337116597107218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=296337116597107218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/296337116597107218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/296337116597107218'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/11/handling-arrays-in-web-services.html' title='Handling Arrays in web services'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7646527092207624049</id><published>2010-10-31T07:32:00.000-07:00</published><updated>2010-10-31T07:33:20.512-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Robotics'/><category scheme='http://www.blogger.com/atom/ns#' term='Graduation Project'/><title type='text'>First successes in robotics</title><content type='html'>Two months in this school year have passed, and a lot has happened. Most of our project work up to now was just research; we evaluated the JADE framework for distributed agent systems, the rule based language JESS, and the Protégé Ontology API, which all are Java based and can therefore run on the CBC controller.&lt;br /&gt;&lt;br /&gt;On friday, &lt;a href="http://faculty-staff.ou.edu/M/David.P.Miller-1/"&gt;Dr. David Miller&lt;/a&gt; arrived at Vienna, and our teacher wanted to have a robot prototype to show. On wednesday, we met at university to build something out of lego... which proved to be difficult, because the collection of Lego parts was very limited, and we hardly managed to connect a single motor to the lego gears. So we met at my place the next day and built a robot out of &lt;a href="http://www.fischertechnik.de/en/"&gt;Fischertechnik&lt;/a&gt; which is, in my opinion, clearly superior to Lego. The result was this robot:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs114.snc4/36082_113643505364946_100001578909257_95835_5045730_n.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="382" src="http://sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs114.snc4/36082_113643505364946_100001578909257_95835_5045730_n.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Each of the tracks is powered by two motors, although that didn't seem to bring much of a performance gain. The thing on top, in case you didn't recognize it, is a camera, and the robot's objective was to follow a blue ball.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;We actually programmed the robot twice; once with C, which is natively available on the controller, and once with Java, which was installed on our controller at the university during summer. My part was the Java program, and I tried to add a few abstractions to later reuse the code.&lt;/div&gt;&lt;ul&gt;&lt;li&gt;As each of the tracks is powered by multiple motors, I added an abstraction for the motors, to combine multiple physical motors into one logical motor&lt;/li&gt;&lt;li&gt;Next was the drive and navigation. Our robot is tracked, so its steering uses the speed difference of the tracks. I tried to keep the theoretical possibility to use another type of steering, although some adaption is probably necessary.&lt;/li&gt;&lt;li&gt;Last but not least, the robot's construction may make it more convenient to wire it in a specific way. What actuators and sensors are on which pins can be configured, so that a new construction does not necessarily mean to change the program. Especially imagine how many times a motor may be accessed in the program, and what it would mean to change all the occurrences.&lt;/li&gt;&lt;/ul&gt;Now this is just foundation code, and the real logic, checking the camera and following the ball, was simply implemented in the main method in a loop. And that's the glorious result:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;object height="300" width="400"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://www.facebook.com/v/113642962031667" /&gt;&lt;embed src="http://www.facebook.com/v/113642962031667" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;For those of you that don't understand the quiet, german comments: We have adjusted the camera for our lighting, and it already got pretty dark. For the video, we turned on the lights, and suddenly, the robot stopped recognizing the ball. So, the first thing you see in the video is me turning off the lights again, and then you can hear my teacher commenting on it^^&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7646527092207624049?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7646527092207624049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7646527092207624049' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7646527092207624049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7646527092207624049'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/10/first-successes-in-robotics.html' title='First successes in robotics'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2536449615816439342</id><published>2010-10-02T13:26:00.000-07:00</published><updated>2010-10-03T11:00:46.491-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Graduation Project'/><title type='text'>Ontologies</title><content type='html'>One part of our project will be to build an ontology for our robot. An ontology is a model of the world, condensed into the relevant parts for a given problem. For example, our robot will have motors, digital and analog sensors, and so on. These would be represented in the ontology, but possibly more, like a simple model of the robot's surrounding: Hills, obstacles and so on.&lt;br /&gt;&lt;br /&gt;At school, we're using a really easy-to use Ontology editor called Protégé; it also has some nice visualization features. The result might be an "owl" file; a standard xml file for ontologies that can easily be accessed from a program.&lt;br /&gt;&lt;br /&gt;I'm just starting to learn this topic and not quite sure what the advantage of an ontology as opposed to e.g. an object graph is. Both model reality in some way, both are hierarchical and have properties, reference objects and so on.&lt;br /&gt;&lt;br /&gt;One of the differences is that an ontology also models the objects, the individual instances. For example, you might model a city's subway network. In a class hierarchy, you could only model things like Subway, Station and whatever is needed for the model. Creating the objects will either mean hardcoding them or writing some code that reads the objects from a file; the former is inflexible and the second is error-prone and redundant. An ontology can (and will) contain the explicit lines and stations of the city along with the definitions, and neither of the above is needed.&lt;br /&gt;&lt;br /&gt;The second diference is its position in context with reasoning. Reasoning is the main reason for the existence of ontologies, and therefore there are a few (hopefully good) APIs out there that make it easy to write statements like "how many stations are there on subway line 1?" or "should I go left or right around the obstacle to reach my target faster?"&lt;br /&gt;&lt;br /&gt;And this is where we get to rule-based programming. Pulling conclusions out of the ontology is half of the work of writing a rule. See you next time!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2536449615816439342?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2536449615816439342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2536449615816439342' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2536449615816439342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2536449615816439342'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/10/ontologies.html' title='Ontologies'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-8255031208486810178</id><published>2010-09-20T14:36:00.000-07:00</published><updated>2010-10-03T11:13:43.904-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Robotics'/><category scheme='http://www.blogger.com/atom/ns#' term='Graduation Project'/><category scheme='http://www.blogger.com/atom/ns#' term='Off-Topic'/><title type='text'>Delaying Laterna Magica: Graduation Project</title><content type='html'>School has totally got me now. We spent last week in Carinthia and focused completely on the project. It was a great experience and we laid much of the foundation work. Today, we got even more long-term assignments, and my impression is that the difference to our graduation project is not the amount of work, but that the teachers don't give us as much time...&lt;br /&gt;&lt;br /&gt;Okay, enough of that. What I really want to tell today is two things. Bad news are always first, so let's get it out of the way: I won't have much (if any) time to work on Laterna Magica in the near future.&lt;br /&gt;And the reason is already the good news: I have a veeery interesting graduation project together with 3 colleagues: We're building and programming a robot to attend a robot competition in the USA.&lt;br /&gt;&lt;br /&gt;Okay, to make my excitement clear, let's break this down:&lt;br /&gt;&lt;b&gt;We're building a robot&lt;/b&gt;&lt;br /&gt;I think this is pretty clear. The competition consists of a few challenges, and the robot has to reflect these hardware-wise to show optimal performance. The challenges are presented at the end of October; an employee of the competiton's organization visits us personally, because we'd be the first team from Europe to attend.&lt;br /&gt;&lt;b&gt;We're programming a robot&lt;/b&gt;&lt;br /&gt;This is probably the most interesting part of it. Our project is sponsored by TU Wien, Vienna's technical university, and our job is not just to program the robot, but to do so using a rule- and agent-based artificial intelligence.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Rule-based is basically a programming approach such as imperative or functional. It means to define a program in terms of condition-reaction pairs. It is supported by a framework which checks the conditions and execites the appropriate actions. While this might not seem spectacular, think about it: have you ever written or seen a program using this approach? Note that this is not another name for an event system; events also result in a reaction, but they only allow for reactions on one-time events (if some event happens), not to states (if some condition holds).&lt;br /&gt;Rule-based systems are good for modelling intelligent behavior. A human reacts to events because of the state-changes they cause, not the events themselves: If a window falls open, you close it because it's cold or loud outside, not because the window has opened.&lt;/li&gt;&lt;li&gt;Agent is just another word for actor. Remember how I wrote about the downloader; it's essentially the same. The only difference is that all this is now integrated into a rule system, which also changes the way how the agents exchange messages. Besides that, the concept of parallel processing stays. In our case, the focus lies on the independence of the actors to cooperatively reach a solution for a problem, and not on simpler multithreading by getting away from traditional synchronization.&lt;/li&gt;&lt;li&gt;Artificial intelligence always sounds good, but what does it really mean? Our parent project at TU Wien has a very specific goal: To build cooperative robots capable of disassembling Lego models. We won't get anywhere near disassembly, but the cooperation is still there, just in the software though. In the end, an artificial intelligence is always task-oriented, and the border where intelligence begins is not well-defined. The question is very interesting, but more philosiphical than technical, so maybe in a later post.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;...to attend a robot competition in the USA&lt;/b&gt;&lt;br /&gt;This is a goal we all really want to reach, but if we do will depend on how we perform. Our project is definitely complex, so if it doesn't work out too well, we won't go. I'm positive about it, though.&lt;br /&gt;The competition we would attend is BotBall, organized by the KISS Institute of Practical Robotics, and we have the honor to be invited directly to the final "Global Conference of Educational Robotics", without attending any of the qualification tournaments. (I hope you can read from all this how proud I am^^)&lt;br /&gt;&lt;br /&gt;So the last point to check for today is: This is definitely good news for me, but how is it for you? Well, I'll write about it, of course. Some things might be off-topic in terms of Magic, but artificial intelligence is definitely in, right? Be sure to check back in a few weeks, there are definitely interesting things to come!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-8255031208486810178?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/8255031208486810178/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=8255031208486810178' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/8255031208486810178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/8255031208486810178'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/09/delaying-laterna-magica.html' title='Delaying Laterna Magica: Graduation Project'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-4015192073202753255</id><published>2010-09-08T08:42:00.000-07:00</published><updated>2010-09-08T08:42:11.886-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Abilities'/><title type='text'>Static and triggered abilities</title><content type='html'>Laterna Magica already supports some core functionalities of Magic, but there's still much left. I don't talk about discard and such things; these are effects that just need to be coded, and then they will be there; just a new flavor of an existing thing. I also don't talk about very complicated effects. All cards currently supported in Laterna Magica use the original oracle wording (with the exception that "draw three cards" must be changed into "draw 3 cards"), which won't be possible for all abilities, but such abilities are still not what I'm talking about.&lt;br /&gt;&lt;br /&gt;I'm thinking about really new features, like static and triggered abilities, the ability to target, or text changing effects.&lt;br /&gt;Text changing effects are just very hard; they kind of break my concept of "one template, many instances" for abilities: The printed wording can be shared between cards, just its interpretation, the rules meaning, is card-specific.&lt;br /&gt;Targets are currently not my goal; I don't really want to do much UI at the moment, and this in inevitable with giving the player yet another possibility to make a choice.&lt;br /&gt;&lt;br /&gt;That neatly leaves behind the two thing I already stated in the title, and they have one thing in common which currently gives me a headache: Scope of influence.&lt;br /&gt;&lt;br /&gt;The scope of an ability means in what places and at what times it works. For spells and activated abilities it's comparable to legality, but the big difference is that the user initiates those abilities; static and triggered abilities are around at all times.&lt;br /&gt;&lt;br /&gt;There are two ways to handle the situation:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;keep all abilities at all times and ask them if they should apply on every single occasion&lt;/li&gt;&lt;li&gt;let the abilities themselves handle it; registering and unregistering using some events&lt;/li&gt;&lt;/ul&gt;I don't know why, but my initial though was to use the second. Both have good and bad points of course: The first can result in a performance problem, the second might break in strange corner cases.&lt;br /&gt;A more obvious, but still realistic example is controller changes: Imagine you get control of a Glorious Anthem, but it still pumps your opponent's creatures.&lt;br /&gt;But even the first approach has similar issues, namely abilities that didn't exist at the beginning of the game, for example on tokens or from Auras. If you register all abilities at the beginning of the game, those would be ignored.&lt;br /&gt;&lt;br /&gt;If you expected a definitive solution here, I'm sorry. I don't have one. There's no right way to do anything, just several ways with different consequences.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-4015192073202753255?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/4015192073202753255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=4015192073202753255' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/4015192073202753255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/4015192073202753255'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/09/static-and-triggered-abilities.html' title='Static and triggered abilities'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5088611517687875677</id><published>2010-09-06T14:52:00.000-07:00</published><updated>2010-09-06T14:52:33.216-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>Update</title><content type='html'>I was kindly brought to a windows incompatibility in my program, so I fixed this. Along with it came an update of the installation, which should make it much easier to update to future versions.&lt;br /&gt;&lt;br /&gt;You can see the result &lt;a href="http://www.slightlymagic.net/forum/viewtopic.php?f=67&amp;amp;t=3132"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5088611517687875677?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5088611517687875677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5088611517687875677' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5088611517687875677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5088611517687875677'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/09/update.html' title='Update'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5510861488916942369</id><published>2010-09-04T10:07:00.000-07:00</published><updated>2010-09-04T10:07:22.600-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>First release!</title><content type='html'>This is only a short post, but one that makes me proud nonetheless.&lt;br /&gt;&lt;br /&gt;This is the day where I put the first release version online. Don't expect too much; the basic rules including combat works, but there are currently nearly only vanilla creatures. Anyway, if you want to try it, or at least see what's there exactly, go to &lt;a href="http://www.slightlymagic.net/forum/viewtopic.php?f=67&amp;amp;t=3120"&gt;the forums&lt;/a&gt; and download it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5510861488916942369?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5510861488916942369/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5510861488916942369' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5510861488916942369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5510861488916942369'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/09/first-release.html' title='First release!'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3065872937096403617</id><published>2010-08-31T11:22:00.000-07:00</published><updated>2010-08-31T11:22:19.315-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>When you feel plainly dumb...</title><content type='html'>There are time when you work on a problem and nothing seems to help. You have checked everything, yet nothing works. And then you see the obvious answer. I had such a problem today. Until now, Laterna Magica only allowed you to pay costs in advance, first adding all the mana to the pool, then activating an ability or (more likely in LM) casting a spell.&lt;br /&gt;&lt;br /&gt;The rules allow it, however, to play mana abilities while playing spells/abilities. So I wrote the code: A piece in the cost to ask the player for input, a piece in the player to handle the GUI. and it didn't work. Clicking on a card was just not recognized.&lt;br /&gt;&lt;br /&gt;I first thought it could be that my legality checking code was not working while a spell is being cast, but I couldn't find any line in the code that even bothered about that. Next, I suspected that the asynchronous communication between game and GUI didn't work, and enabled a few debug statements, which in turn made clear that everything went like desired. So I thought to myself: where could I easily check if the action was successfully processed? Well, in the cost, where the player was asked in the first place.&lt;br /&gt;&lt;br /&gt;But I never got to writing a debug statement; the bug was clear and simple: While I retrieved the activate action, I didn't execute it. These are the times when you feel plainly dumb...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3065872937096403617?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3065872937096403617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3065872937096403617' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3065872937096403617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3065872937096403617'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/when-you-feel-plainly-dumb.html' title='When you feel plainly dumb...'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7968681513782698890</id><published>2010-08-28T05:13:00.000-07:00</published><updated>2010-08-28T05:35:32.386-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>New Downloader</title><content type='html'>Besides working on combat, I played a little more with Jetlang and created a downloader, which works asynchronously and can inform interested listeners over PropertyChangeEvents.&lt;br /&gt;Jetlang's concurrency approach is centered around the actors (who performs a task/where is it performed) and the tasks instead of around the data. Normal multithreading requires you to synchronize on the data you share, while an Actor framework doesn't share data but pass it around, so that only one thread "has" it at a time.&lt;br /&gt;This makes it easier to code, because it is how humans think: You don't worry that two people will write on the same piece of paper at the same time; you wait until the piece of paper is passed to you. That said, it still needs a lot of thinking: Who are the people in your program, what's the piece of paper, and when should you pass it around?&lt;br /&gt;&lt;br /&gt;Btw, I made a little comparison between the new downloader and the one used by &lt;a href="http://mtgrares.blogspot.com/"&gt;forge&lt;/a&gt; (admittedly, in which I also had a great part).&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Program &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; After 2 minutes&amp;nbsp; For 1500 images&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Laterna Magica &amp;nbsp;&amp;nbsp; 1500 images&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2:00 minutes&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Forge&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 200 images&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10:00 minutes&lt;/div&gt;&lt;br /&gt;I looked at the code for Forge and checked if it is roughly comparable to mine, and it was; otherwise I wouldn't have posted this. I changed two things, however: I increased the buffer size to 8 kB to match Laterna Magica, and I removed a print statement. These are pretty slow and shouldn't be there when measuring time.&lt;br /&gt;&lt;br /&gt;Laterna Magica's downloader uses 10 threads, which means that 10 pictures are downloaded at the same time. Even though it might seem at first that it shouldn't make much of a difference, because the total bandwidth stays the same, the numbers clearly say that download speed is five times better. The reason for that is the reduced overhead. Every picture needs the following steps to be done:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Connect to the server&lt;/li&gt;&lt;li&gt;Request the image&lt;/li&gt;&lt;li&gt;Receive the data, store it into a file&lt;/li&gt;&lt;/ol&gt;The time is lost between step 2 and 3. The first two steps has you upload data (the request), the third has you download data. Between that is only waiting. When using only a single thread, this waiting directly influences the download speed, because nothing else happens in that time. Using multiple threads, waiting only blocks one of the threads, leaving 9 downloads running in the meantime.&lt;br /&gt;&lt;br /&gt;Besides downloading card pictures, I adapted Laterna Editor to use the new downloader, which also shown its flexibility: Laterna Editor downloads HTML pages from Gatherer, but doesn't save them to files. Instead, it further processes them to compact card descriptions for Laterna Magica. While it took a little work, the downloader is not limited to Files but can download to any location, such as a string buffer in memory.&lt;br /&gt;&lt;br /&gt;The card downloader from Laterna Editor also shows how the task-centric approach helps with programming. This picture pretty much describes how the download process works:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_SUHKGhHw1Ys/THkBAn1YfqI/AAAAAAAAAFg/HPz9P0KqB5Y/CardDownloader.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_SUHKGhHw1Ys/THkBAn1YfqI/AAAAAAAAAFg/HPz9P0KqB5Y/CardDownloader.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Both Source and Destination are classes implemented by CardDownloader, but implement interfaces specified by Downloader. The source checks if the URL is a search or a single card, and also handles the automatic redirect if Gatherer only finds a single card for a search. The destination saves the HTML page to a string and waits for the download to finish, by registering a property change listener to the download job. If the download was successful, it sends the page to the search- or card processor, depending on the source. If it wasn't, it just discards the downloaded page.&lt;br /&gt;The search processor finds additional result pages (every page has at most 100 cards in the compact view, which is used by the downloader) and of course the individual cards. The download of those is again done by the same process. The search processor simply posts the DownloadJob to the downloader and that's it.&lt;br /&gt;The card processor finds the individual fields like name and mana cost, and stores them into a card. Once that's done, it posts the card to a Channel where the Gui can find it and in turn show the editor.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7968681513782698890?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7968681513782698890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7968681513782698890' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7968681513782698890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7968681513782698890'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/new-downloader.html' title='New Downloader'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SUHKGhHw1Ys/THkBAn1YfqI/AAAAAAAAAFg/HPz9P0KqB5Y/s72-c/CardDownloader.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7495812135572010709</id><published>2010-08-23T12:47:00.000-07:00</published><updated>2010-08-28T02:11:45.660-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Combat'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Combat and concurrency</title><content type='html'>It was on July 15th when I titled a blog post "Combat in sight?", but now it really is. Today, I committed a version to my project where players can attack and thus deal damage. Blocking is not possible yet, but that's more a matter of work and not of thinking. Players still can't loose the game, but that's another story...&lt;br /&gt;&lt;br /&gt;Now, for those of you not familiar with "concurrency" in programming, it means when multiple parts of a program run at the same time, "competing for the computer's resources" like CPU or RAM. Concurrency is hard for three reasons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;you have to control what happens when, because the classical "everything happens in a fixed sequence" is not true&lt;/li&gt;&lt;li&gt;speaking of "competing for resources", when two concurrent threads of execution access the same data, they can overwrite each other, messing things up&lt;/li&gt;&lt;li&gt;for this reason, locks and synchronizing have come up, which grant exclusive access to a resource to a single thread. This also means that threads have to wait for resources until they aren't locked any more. In the worst case, two threads can wait for each other.&lt;/li&gt;&lt;/ul&gt;Laterna Magica uses an approach that has two main threads; one for the game, and one for the user interface. While the UI thread is mandated by Java, I could have implemented LM to run on the same thread. The main reason I did not was to make LM independent of the GUI, meaning that the program can use different UIs and even run without a User Interface, say in a Computer-vs-Computer simulation or on a server (while the latter needs more preparation, I believe it will be possible someday).&lt;br /&gt;&lt;br /&gt;So there are two threads, which means every GUI interaction means synchronization. The first GUI interaction implemented was playing cards and activating abilities. The code to enable that was pretty wordy and hardly extensible:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;private boolean    ready;&lt;br /&gt;private PlayAction a;&lt;br /&gt;&lt;br /&gt;public GuiActor(Player player) {&lt;br /&gt;    super(player);&lt;br /&gt;}&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;public synchronized void putAction(PlayAction a) {&lt;br /&gt;    ready = true;&lt;br /&gt;    this.a = a;&lt;br /&gt;    notifyAll();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public synchronized PlayAction getAction() {&lt;br /&gt;    ready = false;&lt;br /&gt;    while(!ready)&lt;br /&gt;        try {&lt;br /&gt;            wait();&lt;br /&gt;        } catch(InterruptedException ex) {&lt;br /&gt;            ex.printStackTrace();&lt;br /&gt;        }&lt;br /&gt;    return a;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;On the surface, it doesn't look that bad, but it has several issues:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It is low-level: It handles all the synchronization stuff itself, as seen by &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;boolean ready&lt;/span&gt;, &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;notifyAll()&lt;/span&gt;, &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;synchronized&lt;/span&gt;, &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;while(!ready) ...&lt;/span&gt; This is all stuff that has to be repeated for every sort of user input: declare attacker, blocker, ...&lt;/li&gt;&lt;li&gt;It moves control away from the actor: putAction takes a PlayAction, which is the result of e.g. clicking on a card. How that click is associated to the PlayAction is not controlled by the actor which violates atomicity&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Translating click to PlayAction is specific to playing spells and not compatible with e.g. declaring attackers. This means that the translation, which is not atomic, has to be performed for every type of input&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Missing Encapsulation: The interpretation as a PlayAction is only valid when the player has priority, and this fact is not represented here: There is no representation of the fact whether the Actor is waiting for a PlayAction or for declaring an attacker&lt;/li&gt;&lt;/ul&gt;On the opposite, here is the new variant: &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class GuiMagicActor extends AbstractMagicActor {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public final GuiChannels&amp;nbsp;&amp;nbsp;&amp;nbsp; channels = new GuiChannels();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static &lt;t&gt; T getValue(Fiber f, Channel&lt;t&gt; ch, GuiActor a) {&lt;/t&gt;&lt;/t&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a.start();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; log.debug("Waiting for result...");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; T result = Parallel.getValue(f, ch);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; log.debug("received!");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a.dispose();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return result;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public PlayAction getAction() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return getValue(channels.fiber, channels.actions, new ActionActor(this));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This new version is based on the Jetlang concurrency framework, which works using channels and callbacks: Messages published to a channel are - asynchronously - forwarded to registered callbacks, which may then in turn publish new messages. The &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Parallel.getValue()&lt;/span&gt; method is used to get a value back from the channel synchonously, and the ActionActor encapsulates the state, in this case choosing a &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;PlayAction&lt;/span&gt; to perform:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class GuiChannels {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Channel for receiving {@link PlayAction}s to execute when the player has priority&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public final Channel&lt;playaction&gt;&amp;nbsp; actions&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = new MemoryChannel&lt;playaction&gt;();&lt;/playaction&gt;&lt;/playaction&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Channel for publishing {@link MagicObject}s when the user clicks on them&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public final Channel&lt;magicobject&gt; objects&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = new MemoryChannel&lt;magicobject&gt;();&lt;/magicobject&gt;&lt;/magicobject&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Channel for publishing {@link Player}s when the user clicks on them&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public final Channel&lt;player&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; players&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = new MemoryChannel&lt;player&gt;();&lt;/player&gt;&lt;/player&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Channel for publishing when the user clicks "pass priority"&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public final Channel&lt;void&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; passPriority = new MemoryChannel&lt;void&gt;();&lt;/void&gt;&lt;/void&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public final Fiber&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fiber;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public GuiChannels() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PoolFiberFactory f = new PoolFiberFactory(Executors.newCachedThreadPool());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fiber = start(f.create());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Fiber start(Fiber f) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; f.start();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return f;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class ActionActor extends GuiActor {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ActionActor(GuiMagicActor actor) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(actor);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void start() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; disposables.add(actor.channels.objects.subscribe(actor.channels.fiber, new CardCallback()));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; disposables.add(actor.channels.passPriority.subscribe(actor.channels.fiber, new PassPriorityCallback()));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private class CardCallback implements Callback&lt;magicobject&gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void onMessage(MagicObject c) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; log.debug("Received: " + c);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PlayAction a = GuiUtil.getActionOptional(actor.getPlayer(), c);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(a != null) actor.channels.actions.publish(a);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private class PassPriorityCallback implements Callback&lt;void&gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void onMessage(Void v) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; log.debug("Received pass priority");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; actor.channels.actions.publish(null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&amp;nbsp;&lt;/void&gt;&lt;/magicobject&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ActionActor&lt;/span&gt; receives Messages through the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;objects&lt;/span&gt; and &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;passPriority&lt;/span&gt; channels and reacts specific to its task.&lt;br /&gt;&lt;br /&gt;Hope you liked it!&lt;br /&gt;&lt;ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7495812135572010709?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7495812135572010709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7495812135572010709' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7495812135572010709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7495812135572010709'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/combat-and-concurrency.html' title='Combat and concurrency'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2377068388089159821</id><published>2010-08-21T10:15:00.000-07:00</published><updated>2010-08-21T10:15:18.661-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Scala: Functional programming &amp; DSL</title><content type='html'>I first learned about Scala in forge's comment to &lt;a href="http://mwars.blogspot.com/2010/03/new-message-system.html"&gt;this post&lt;/a&gt;, and the &lt;a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?site_id=1&amp;amp;contentarea_by=Java&amp;amp;sort_by=Date&amp;amp;sort_order=1&amp;amp;start=1&amp;amp;end=16&amp;amp;topic_by=-1&amp;amp;product_by=&amp;amp;type_by=All%20Types&amp;amp;show_abstract=true&amp;amp;search_by=The%20busy%20Java%20developer%27s%20guide%20to%20Scala&amp;amp;industry_by=-1"&gt;tutorial&lt;/a&gt; he mentioned is really great. Just for experimenting, I extended the Calculator example for Variables, Functions and exponentiation. The result is really extensible and took me just 140 lines of code, which really impressed me. So I'm showing you the result:&lt;br /&gt;&lt;br /&gt;AST.scala (Scala doesn't require Files to represent the class names, and AST (abstract syntax tree) fits the classes in this file)&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;package net.slightlymagic.calc {&lt;br /&gt;&amp;nbsp; abstract class Expr&lt;br /&gt;&amp;nbsp; case class Variable(name: String) extends Expr {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; override def toString = name&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; case class Number(value: Double) extends Expr {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; override def toString = "" + value&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; case class UnaryOp(name: String, arg: Expr) extends Expr {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; override def toString = name + arg&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; case class BinaryOp(name: String, left: Expr, right: Expr) extends Expr {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; override def toString = "(" + left + name + right + ")"&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; case class Function(name: String, args: List[Expr]) extends Expr {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; override def toString = name + args.mkString("(", ", ", ")")&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: inherit;"&gt;Calc.scala&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;package net.slightlymagic.calc {&lt;br /&gt;&amp;nbsp; object Calc {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def simplify(e: Expr): Expr = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def simpArgs(e: Expr) = e match {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp(op, left, right) =&amp;gt; BinaryOp(op, simplify(left), simplify(right))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case UnaryOp(op, operand) =&amp;gt; UnaryOp(op, simplify(operand))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case Function(op, operands) =&amp;gt; Function(op, operands.map((x) =&amp;gt; simplify(x)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case x =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def simpTop(e: Expr) = e match {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case UnaryOp("-", UnaryOp("-", x)) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("-", x, Number(0)) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("-", Number(0), x) =&amp;gt; UnaryOp("-", x)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("-", x1, x2) if x1 == x2 =&amp;gt; Number(0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case UnaryOp("+", x) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("+", x, Number(0)) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("+", Number(0), x) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("*", x, Number(1)) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("*", Number(1), x) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("*", x, Number(0)) =&amp;gt; Number(0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("*", Number(0), x) =&amp;gt; Number(0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("/", x, Number(1)) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("/", x1, x2) if x1 == x2 =&amp;gt; Number(1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("^", x, Number(1)) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("^", x, Number(0)) if x != Number(0) =&amp;gt; Number(1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("^", x1, UnaryOp("-", x2)) =&amp;gt; BinaryOp("/", Number(1), BinaryOp("^", x1, x2))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case e =&amp;gt; e&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; simpTop(simpArgs(e))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def evaluate(expr: Expr, variables: Map[String, Double], functions: Map[String, (List[Double]) =&amp;gt; Double]): Double = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; expr match {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case Number(x) =&amp;gt; x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case UnaryOp("-", x) =&amp;gt; -evaluate(x, variables, functions)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("+", x1, x2) =&amp;gt; evaluate(x1, variables, functions) + evaluate(x2, variables, functions)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("-", x1, x2) =&amp;gt; evaluate(x1, variables, functions) - evaluate(x2, variables, functions)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("*", x1, x2) =&amp;gt; evaluate(x1, variables, functions) * evaluate(x2, variables, functions)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("/", x1, x2) =&amp;gt; evaluate(x1, variables, functions) / evaluate(x2, variables, functions)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case BinaryOp("^", x1, x2) =&amp;gt; Math.pow(evaluate(x1, variables, functions), evaluate(x2, variables, functions))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case Variable(x) =&amp;gt; variables(x)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case Function(x, args) =&amp;gt; functions(x)(args.map((x) =&amp;gt; evaluate(x, variables, functions)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def parse(text : String) = CalcParser.parse(text).get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; val standardVars =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Map(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "E"&amp;nbsp; -&amp;gt; Math.E,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Pi" -&amp;gt; Math.Pi&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; val standardFuns =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Map(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "sin"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.sin(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "cos"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.cos(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "tan"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.tan(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "cot"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; 1/Math.tan(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "asin" -&amp;gt; {x:List[Double] =&amp;gt; Math.asin(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "acos" -&amp;gt; {x:List[Double] =&amp;gt; Math.acos(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "atan" -&amp;gt; {x:List[Double] =&amp;gt; Math.atan(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "acot" -&amp;gt; {x:List[Double] =&amp;gt; Math.atan(1/x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "exp"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.exp(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "log"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.log(x(0))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "min"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.min(x(0), x(1))},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "max"&amp;nbsp; -&amp;gt; {x:List[Double] =&amp;gt; Math.max(x(0), x(1))}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def evaluate(expr: String, variables: Map[String, Double], functions: Map[String, (List[Double]) =&amp;gt; Double]): Double = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; evaluate(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; simplify(parse(expr)), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(variables == null) standardVars else standardVars ++ variables,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(functions == null) standardFuns else standardFuns ++ functions&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; import scala.util.parsing.combinator._&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; object CalcParser extends JavaTokenParsers {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def expr: Parser[Expr] =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (term ~ "+" ~ term) ^^ { case lhs~plus~rhs =&amp;gt; BinaryOp("+", lhs, rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (term ~ "-" ~ term) ^^ { case lhs~minus~rhs =&amp;gt; BinaryOp("-", lhs, rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; term &lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def term: Parser[Expr] =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (factor ~ "*" ~ factor) ^^ { case lhs~times~rhs =&amp;gt; BinaryOp("*", lhs, rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (factor ~ "/" ~ factor) ^^ { case lhs~div~rhs =&amp;gt; BinaryOp("/", lhs, rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; factor&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def factor : Parser[Expr] =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (exp ~ "^" ~ exp) ^^ { case lhs~exp~rhs =&amp;gt; BinaryOp("^", lhs, rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; exp&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def exp : Parser[Expr] =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("+" ~ expr) ^^ { case plus~rhs =&amp;gt; UnaryOp("+", rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("-" ~ expr) ^^ { case minus~rhs =&amp;gt; UnaryOp("-", rhs) } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "(" ~&amp;gt; expr &amp;lt;~ ")" |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ident ~ params ^^ {case name~params =&amp;gt; Function(name, params)} |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ident ^^ {case name =&amp;gt; Variable(name)} |&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; floatingPointNumber ^^ {x =&amp;gt; Number(x.toFloat) }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def params : Parser[List[Expr]] =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "(" ~&amp;gt; repsep(expr, ",") &amp;lt;~ ")"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def parse(text : String) = parseAll(expr, text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Wow! I don't want to repeat the tutorial, which is really great, but I want to mention a few features that really improved the way this code is written.&lt;br /&gt;&lt;br /&gt;First of all, Scala is a mixture of functional and object oriented: &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Map[String, (List[Double]) =&amp;gt; Double]&lt;/span&gt; (Scala uses square brackets for generic types) means a Map of Strings and Functions, which take a list of Doubles as a parameter and yields a Double as the result. To get the same result as standardFuns in the Calc object... Yes, object. This basically means that all members are static. On the opposite, normal classes can't have static members, which doesn't really matter because you can have class and object of the same name. To get the same result as standardFuns using Java, you'd have to define a common interface and several anonymous classes, which means a lot of duplication. Using anonymous functions and the fact that a function can be passed as a parameter, something you don't get from nonfuntional languages, you avoid most of the redundant code. No interface definition, just a single line per anonymous function.&lt;br /&gt;&lt;br /&gt;The second thing that you can write method calls in an "unusual way": &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;a.plus(b)&lt;/span&gt; is the same as &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;a plus b&lt;/span&gt;. Together with the fact that you can name methods, e.g. "+", you basically have overriding operators. This means not much more than you can write short, concise code. But that is very important; just look at the preceding paragraph. The parser package of Scala uses such "Operators" to write short, expressive text parsing code.&lt;br /&gt;&lt;br /&gt;Lastly, pattern matching. This is a great feature that is really hard to imitate using Java. It uses case classes, as seen in AST.scala, and the match/case construct. For example:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;expr match {&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; case BinaryOp(x1, op, x2) =&amp;gt; doSomething()&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; case BinaryOp(Number(x1), "+", Number(x2) =&amp;gt; doSomethingElse() &lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/div&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;doSomething()&lt;/span&gt; is only executed if the case matches, and at the same time assigns the three variables x1, op, x2. You can build more complex constructs, as the second example shows, including nesting Objects and using constant expressions to narrow down matching cases. This works as long as you use case classes.&lt;br /&gt;&lt;br /&gt;And now comes another important part: Scala compiles to Java byte code, which means that you can code a part in scala. For example, the simple Calculator DSL was way easier to write in Scala than it would be in Java. Once it's packed as a jar, you don't even notice that the code was not written in traditional Java, and integrates nicely into the other parts. (Except you'd have a hard time to call "operator methods" or use API that uses Functions as parameters. Consider that for your public, plain-old-java accessible API)&lt;br /&gt;The only downside is that Eclipse integration is not great so far. Maven does a good task in still compiling your Scala code, but the Scala plugin is not very stable, so I use the plain text editor. It works for small things like the Calculator, but I miss obvious features like import organizing, content assist, automatic building on every change, syntax &amp;amp; error highlighting, parentheses matching, folding...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2377068388089159821?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2377068388089159821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2377068388089159821' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2377068388089159821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2377068388089159821'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/scala-functional-programming-dsl.html' title='Scala: Functional programming &amp; DSL'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2830138533229823803</id><published>2010-08-19T05:51:00.000-07:00</published><updated>2010-08-19T05:51:27.458-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Performance of Software</title><content type='html'>I was reading a little about scala these days and think that it's a cool language that would probably make my life a lot easier. For example, the CardCompiler is hard to use; you have to go through a few hoops to implement new effect parsers. I think that I am on the right way, but there is much room for improvement in terms of usability.&lt;br /&gt;&lt;br /&gt;But today I don't want to talk about Scala, but software performance. One point with Scala is that it largely uses immutable objects, putting copying over mutating existing objects. The first thought when you hear this is probably how much memory this would waste and that it will also need more processing power all the data has to be copied.&lt;br /&gt;&lt;br /&gt;However, performance doesn't mean to use as little resources as possible. It means to use the resources you have the best possible. And nowadays, the resource we have but use the least are multiple processors.&lt;br /&gt;&lt;br /&gt;Modern processors work on the edge of physical possibilities. Circuits are not thicker than a few atoms, and currencies are so small that you can almost measure the number of electrons flowing. At this scale, quantum effects matter, and this means randomness. That's about the reason why (semiconductor) processors don't get faster any more and increasing speed does only occur by parralelism.&lt;br /&gt;&lt;br /&gt;Writing software that works parallel, however, is hard, because multiple threads can access the same resource at the same time and thus wreak havoc. Which leads back to immutable objects: Data corruption can only occur when writing. when every access is read-only or creates a copy, nothing bad can happen.&lt;br /&gt;&lt;br /&gt;This leaves one question: How bad is copying really? Well, every copy you don't need any more can be garbage collected, so memory should not really be a concern. Memory size is also not as restricted as processor speed, so there should be enough potential to support memory-heavy rather than processor-heavy software.&lt;br /&gt;Garbage collection itself, however, uses the processor. It finds objects that are no longer accessed by any thread; that means that it is not constrained to work in the program's normal flow and can work in its own thread. As the garbage collector is not really interested in the objects, it can even be spread to multiple threads if necessary.&lt;br /&gt;&lt;br /&gt;Copying itself uses processing power, and can take some time for large objects. Usually, the only type of large objects are arrays. There are really not many possibilities: boolean, byte, short, char, int, float, long, double, Object. All these take up to 8 bytes to store. All that stays are the indexes of arrays, which can go into several millions. If we stay with immutable objects, this is really the only bottleneck we have to deal with, because you don't need to copy the array's content objects, as they can't change.&lt;br /&gt;&lt;br /&gt;I guess this wasn't all that interesting to read, but I really wanted to speak this out: parallelism is the future of software, parallelism is hard, so any attempt to make it easier, for example by using immutable objects, is a step into the right direction&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2830138533229823803?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2830138533229823803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2830138533229823803' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2830138533229823803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2830138533229823803'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/performance-of-software.html' title='Performance of Software'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5100404339523628964</id><published>2010-08-16T17:35:00.000-07:00</published><updated>2010-08-17T01:25:24.083-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>To URI or not to URI</title><content type='html'>I'm not an English native speaker, so don't blame me if this Shakespeare reference is not recognizable. Maybe "URI or not URI" would fit better for pronunciation...&lt;br /&gt;&lt;br /&gt;Anyway, I'll brighten up the situation for anyone who doesn't know URIs: "URI" stands for Uniform Resource Identifier, and the "not to URI" part of the title refers to "URL", Uniform Resource Locator. Seriously, I don't know what the difference between them is supposed to be, just that they behave pretty differently in Java.&lt;br /&gt;(And for anyone who thinks, "Well, so you do know the difference?" No, the Java classes are an implementation of a specification that I don't understand, and not even really know. I know, however, how the implementation behaves. If this behavior follows the original specification, which was not made for the Java language but presumably for the internet, I don't know.)&lt;br /&gt;&lt;br /&gt;So, from my point of view, both URIs and URLs are meant to identify some sort of resource, like a network drive of some web site.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In Java, you can directly connect to a URL and read from and write to it. You can't do that for a URI, but you can convert between both forms (if the resource is legal in both representations).&lt;/li&gt;&lt;li&gt;The equals() and hashCode() methods of URL perform a name space lookup, making them very slow. URLs are therefore poor choices for map keys, for example for caching downloaded files.&lt;/li&gt;&lt;/ul&gt;So, it seems like a tied match. The first one is "obvious", you can see it in the API that URI can't directly open connections. equals() and hashCode() being slow is so subtle that there is an entry in FindBugs, an Eclipse plugin that helps to find common programming mistakes, which is how I learned about it.&lt;br /&gt;&lt;br /&gt;The third difference I've experienced so far is very subtle and unexpected, and it is the real reason for this post. wow, about time to get to the point...&lt;br /&gt;&lt;br /&gt;You can represent a file inside a Jar (or Zip) archive using a URL or URI; both work. You can also "resolve" a relative URL/URI against another one to get an absolute one. But the combination of both only works with URL!&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;jar:file:/path/to/archive.jar!/path/inside/archive/&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;jar:file:/path/to/archive.jar!/path/inside/archive.txt&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;/div&gt;&lt;br /&gt;are both legitimate URIs/URLs. Say, you want to resolve "hello.txt" against these, you expect:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;jar:file:/path/to/archive.jar!/path/inside/archive/hello.txt&lt;/span&gt;&lt;/div&gt;&lt;span style="font-family: 'Courier New',Courier,monospace; font-size: x-small;"&gt;jar:file:/path/to/archive.jar!/path/inside/hello.txt&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;however, this works only with URLs. A URI won't be able to resolve and will just return "hello.txt"&lt;br /&gt;&lt;br /&gt;I said that I recently reimplemented TreeProperties, and I did it based on URIs. As you might guess, resolving paths relative to some context is pretty important for a tree structure. Finding the bug was very annoying, because reading from a Jar file, or even multiple Jar files, is not something I have years of experience with. Fixing the bug was way easier once I sorted out its origin.&lt;br /&gt;&lt;br /&gt;I'd like to end this post with another Shakespeare quote:&lt;br /&gt;&lt;br /&gt;"But none come to mind. I hope I have at least fooled you with the quotation marks. Just be aware or the differences between Uniform Resource Locators and Identifiers"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5100404339523628964?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5100404339523628964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5100404339523628964' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5100404339523628964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5100404339523628964'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/to-uri-or-not-to-uri.html' title='To URI or not to URI'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5561730052652362902</id><published>2010-08-13T16:31:00.000-07:00</published><updated>2010-08-13T16:41:34.970-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Migrating to Maven</title><content type='html'>This week, I've finally done what I wanted to do for a very long time: I migrated my project to Maven. Warning: Non-programmers will probably be hopelessly bored.&lt;br /&gt;&lt;br /&gt;Maven is a build system with many capabilities that are probably beyond my current recognition. For me, it means essentially these things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Managing dependencies to external libraries, such as Dom4J, an XML API, and also to the different projects that belong to LaternaMagica, like TreeProperties.&lt;/li&gt;&lt;ul&gt;&lt;li&gt;For an Open Source project, this also means that I don't have to care that others have all the required libraries, because they can find and download them on-demand. Having the right configurations in the development environment is not that important this way. As a downside, it makes Maven pretty much a requirement for working with my code, but that's just like English is required to read my blog: it's good to know English, no matter what.&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Automatic testing and generation of source, binary and documentation archives, or "snapshots"&lt;/li&gt;&lt;/ul&gt;I said I wanted to do this for a long time. If you're asking why I haven't done it earlier... well, I was dumb. I couldn't get my Eclipse installation working with the Maven plugin. The problem was... that my eclipse installation was owned by root (the administrator, for all the poor Windows users out there^^) and installing the plugin simply didn't do anything as I didn't have the rights.&lt;br /&gt;&lt;br /&gt;So finally, everything is alright. I have totally rewritten TreeProperties and Utils, and also greatly reworked LaternaDoc, which finally does what it should. More on those another time ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5561730052652362902?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5561730052652362902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5561730052652362902' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5561730052652362902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5561730052652362902'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/migrating-to-maven.html' title='Migrating to Maven'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3151600407819277183</id><published>2010-08-08T05:55:00.000-07:00</published><updated>2010-08-13T16:37:21.024-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><category scheme='http://www.blogger.com/atom/ns#' term='Last Known Information'/><category scheme='http://www.blogger.com/atom/ns#' term='Rules Enforcement'/><title type='text'>Last Known Information with Undo</title><content type='html'>Last Known Information is one of the hardest parts of the Magic rules to implement. It is important when handling effects that use a card that changed zones, for example Persist ("When this permanent is put into a graveyard from the battlefield,  if it had no -1/-1 counters on it, return it to  the battlefield under its owner's  control with a -1/-1 counter on it.") or Swords to Plowshares ("Exile target creature. Its controller gains life equal to its power."). These effects happen when the card is already in the graveyard/exile, but need information of the card when it was on the battlefield. For us, this seems very natural, but from a programming point of view, we have to save the relevant values long enough so that interested effects can look it up.&lt;br /&gt;The two main questions of this concept are already in the previous sentence:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;What values are "relevant"?&lt;/li&gt;&lt;li&gt;What is "long enough"?&lt;/li&gt;&lt;/ul&gt;An this is where Undo steps in! Why store the values separately when we can travel back in time and look at the original ones? with that approach, there is no chance that we need a value that was not saved - because we look at the original.&lt;br /&gt;Well, when we talk about time travel, of course there are problems: We have to get back to the future after looking up the needed information, but this is only possible if we didn't change history: The game will discard any actions that lie in the future if we change the past. Unfortunately, even simply looking up the power of a creature will change the past, because it results in evaluating continuous effects etc.&lt;br /&gt;Well, I have another science fiction reference up my sleeve to save the day: If we must not change history, "simply" do it in a parallel universe: Create an exact copy of the game up to the point in the past we need to look up the information, then use it in our regular space-time continuum.&lt;br /&gt;&lt;br /&gt;So much for the theory of time travel and parallel universes, the reality is a bit more complex, though. I think I can eventually work it out so that this is possible. This is also the way I expect a potential AI to work: Try out multiple variants in a parallel universe and do the best in the real universe.&lt;br /&gt;But right now, only regular time travel is possible, with the limits set by changing history. The problem is that I can't transfer actions between games yet: If you tap a creature, I can't copy that action and execute it in another game, because the action stores the permanent that was tapped, which is exclusively associated with that game. The solution probably needs hashing or another type of identifier associated with each part of the game.&lt;br /&gt;&lt;br /&gt;I hope you enjoyed the short Science-Fiction sequence. I definitely did, and I would never have guessed to talk about Time Travel and Alternate Universes as a serious metaphor for a programming problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3151600407819277183?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3151600407819277183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3151600407819277183' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3151600407819277183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3151600407819277183'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/last-known-information-with-undo.html' title='Last Known Information with Undo'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5808540403423950578</id><published>2010-08-03T14:28:00.000-07:00</published><updated>2010-08-08T14:12:12.731-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Simultaneity'/><category scheme='http://www.blogger.com/atom/ns#' term='Rules Enforcement'/><title type='text'>Simultaneity: Warp World</title><content type='html'>The first time I mentioned simultaneity is with combat. It was just a coincidence that this was the first time, but it is very reasonable, because simultaneity happens very often during combat: Technically, creatures become attacking creatures simultaneously, then the same happens with blockers, damage etc.&lt;br /&gt;&lt;br /&gt;Simultaneity means multiple things, and I'm not sure if I have a full understanding of these. I'll take the example of Warp World, which is a classic example for discussing timing:&lt;br /&gt;&lt;br /&gt;Each player shuffles all permanents he or she owns into his or her  library, then reveals that many cards from the top of his or her  library. Each player puts all artifact, creature, and land cards  revealed this way onto the battlefield, then does the same for  enchantment cards, then puts all cards revealed this way that weren't  put onto the battlefield on the bottom of his or her library.&lt;br /&gt;&lt;br /&gt;First, all permanents are shuffled into their respective owner's library, triggering from leaving play. All these abilities see all the cards leaving play, meaning that an ability like "leaves the battlefield" sees all the permanents. There is no variant where that ability leaves early and isn't active when the permanents leave play.&lt;br /&gt;&lt;br /&gt;The "then"s in the card's text neatly separate the parts that happen simultaneously. All "enters the battlefield" abilities of cards trigger for cards that enter the battlefield at the same time as them; it seems like the abilities are active just a little earlier than they are in play... it's a little awkward in my opinion.&lt;br /&gt;&lt;br /&gt;now hold on - Auras with "Enchant Enchantment" won't make it on the battlefield, because Enchant is not a triggered ability. The Comprehensive rules don't specify a wording for Enchant but rather give it extra rules. It feels, however, like a replacement effect, just like "As ... enters the battlefield" (which don't use the stack at all - they are effects generated by static abilities).&lt;br /&gt;&lt;br /&gt;Lands with additional costs, like the painlands, are worded like this, because if it was a triggered ability, you could play the mana ability in response to the cost. So, just like "Enchant Enchantment" enchantments (^^) don't make it, lands like "As ... comes into play, return a land you control to your hand. If you don't, put ... into your graveyard instead." won't, too. Champion, however, is a triggered ability, so your champions can survive. you can even enchant the creatures you wish to exile, because the triggered abilities won't resolve or even be put onto the stack until Warp World finished resolving.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;One last thing: after resolving the spells, all the abilities are put onto the stack, in an order depending on player seating and player choice. you can put abilities from entering the battlefield on the stack earlier that those from leaving the battlefield, even though these happened in another sequence...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5808540403423950578?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5808540403423950578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5808540403423950578' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5808540403423950578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5808540403423950578'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/08/simultaneity-warp-world.html' title='Simultaneity: Warp World'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2842832361677638948</id><published>2010-07-30T23:40:00.000-07:00</published><updated>2010-07-30T23:40:22.845-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Damage'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Damage</title><content type='html'>I just re-read my post about destruction and realised I should show what it took me to implement damage. This might not be my most interesting post, as it is just code, but maybe some of you are interested. Get ready... there are about 200 lines of code coming for you!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public abstract class DamageEvent extends ReplaceableEvent {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private MagicObject source;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ammount;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private boolean&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; combat, preventable;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DamageEvent(MagicObject affected, MagicObject source, int ammount, boolean combat, boolean preventable) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this(you(ofInstance(affected)), source, ammount, combat, preventable);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DamageEvent(Player affected, MagicObject source, int ammount, boolean combat, boolean preventable) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this(ofInstance(affected), source, ammount, combat, preventable);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DamageEvent(Supplier&lt;player&gt; affected, MagicObject source, int ammount, boolean combat, boolean preventable) {&lt;/player&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(affected);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.source = source;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.ammount = ammount;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.combat = combat;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.preventable = preventable;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MagicObject getSource() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return source;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * This method can be used by replacement effects that prevent or increase damage.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setAmmount(int ammount) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.ammount = ammount;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int getAmmount() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return ammount;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public boolean isCombatDamage() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return combat;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public boolean isPreventable() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return preventable;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class DamagePlayerEvent extends DamageEvent {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Player p;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DamagePlayerEvent(Player p, MagicObject source, int ammount, boolean combat, boolean preventable) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(p, source, ammount, combat, preventable);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.p = p;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public Player getPlayer() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return p;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected boolean execute0() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CompoundEdit ed = new CompoundEdit(getGame(), true, "Deal damage");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //118.3a. Damage dealt to a player causes that player to lose that much life.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; getPlayer().getLifeTotal().loseLife(getAmmount());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ed.end();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class DamagePermanentEvent extends DamageEvent {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static final Predicate&lt;magicobject&gt; legalAffected&amp;nbsp; = and(isIn(ofInstance(BATTLEFIELD)),&lt;/magicobject&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; card(or(CREATURE, PLANESWALKER)));&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static final Predicate&lt;magicobject&gt; isPlaneswalker = card(has(PLANESWALKER));&lt;/magicobject&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //TODO implement wither and lifelink&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static final Predicate&lt;magicobject&gt; hasWither&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = alwaysFalse();&lt;/magicobject&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static final Predicate&lt;magicobject&gt; hasLifelink&amp;nbsp;&amp;nbsp;&amp;nbsp; = alwaysFalse();&lt;/magicobject&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private CardObject&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; permanent;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DamagePermanentEvent(CardObject affected, MagicObject source, int ammount, boolean combat, boolean preventable) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(affected, source, ammount, combat, preventable);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(!legalAffected.apply(affected)) throw new IllegalArgumentException(&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "affected must be a creature or planeswalker permanent: " + affected);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; permanent = affected;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public CardObject getPermanent() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return permanent;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected boolean execute0() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CompoundEdit ed = new CompoundEdit(getGame(), true, "Deal damage");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //118.3b. Damage dealt to a planeswalker causes that many loyalty counters to be removed from that&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // planeswalker.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(isPlaneswalker.apply(getPermanent())) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //TODO remove counters&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //118.3c. Damage dealt to a creature by a source with wither causes that many -1/-1 counters to be put on&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // that creature.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else if(hasWither.apply(getSource())) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //TODO add counters&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //118.3d. Damage dealt to a creature by a source without wither causes that much damage to be marked on&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // that creature.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; getPermanent().setMarkedDamage(getPermanent().getMarkedDamage() + getAmmount());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //118.3e. Damage dealt to an object or player by a source with lifelink causes that source's controller to&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // gain that much life, in addition to the damage's other results.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(hasLifelink.apply(getSource())) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; getSource().getController().getLifeTotal().gainLife(getAmmount());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ed.end();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I don't really like this code, because it has the effects of damage hard coded, but I think it's fine for now. extracting these effects later if it's necessary shouldn't be too hard, as the different effects of damage will only be mentioned here, so I can safely delay this task.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2842832361677638948?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2842832361677638948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2842832361677638948' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2842832361677638948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2842832361677638948'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/07/damage.html' title='Damage'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-1717847386093308574</id><published>2010-07-23T07:40:00.000-07:00</published><updated>2010-07-23T07:44:29.636-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Cards'/><category scheme='http://www.blogger.com/atom/ns#' term='Card Editor'/><title type='text'>Trying out the card editor</title><content type='html'>I just tried out my card editor to download "a few" cards; all the vanilla creatures and old (non-pain) dual lands. If I haven't done anything wrong in my gatherer search, there are 177 vanilla creatures. it's in fact not so easy to search for textless cards; I had to use the regular expressing "m/^$/" ("m/.../" is the gatherer identifier for regexes, "^$" essentially means "the beginning of the text immediately followed by the end")&lt;br /&gt;&lt;br /&gt;by the way, I haven't done a full fledged form for gatherer searches; I did the search on the web site and pasted the search URL into my program. takes me much less time^^&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;there were two problems: first, my download just discontinued after 89 cards. Looking at what card that was, it was Little Girl from Unhinged with a half mana symbol. Second, even after refining my search to exclude Un-cards, my program only wound up downloading 100 cards. my workaround for that problem was simply to download the other cards by reverting the sort order^^&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;so, putting things short, I now have over 200 cards implemented, including 177 vanilla creatures, 10 classic dual lands, 10 SHM/EVE dual lands, 5 basic lands, Wrath of God, Damnation and Llanowar Elves. It's really time for combat^^&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-1717847386093308574?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/1717847386093308574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=1717847386093308574' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1717847386093308574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1717847386093308574'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/07/trying-out-card-editor.html' title='Trying out the card editor'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7317849024031473958</id><published>2010-07-22T11:46:00.000-07:00</published><updated>2010-07-23T07:39:36.403-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Event Handling'/><title type='text'>Event Handling</title><content type='html'>If you recall what I wrote a long time ago about challenges in programming, event handling would be one of the easy, boring parts. The point of event handling is to inform parts of your program when something happens in another part. This is espacially important for the user interface, as you always want to see what's currently going on, but in the context of Magic, there is another very important reason for event handling: Triggered abilities.&lt;br /&gt;&lt;br /&gt;If you now think that the User Interface and Triggered abilities can share the event handling system, therefore effectively reducing the ammount of programming needed for each of them, I fear you'll be disappointed. The reason for this is the undo system. While it enables things like handling illegal actions, the most direct interpretation of what it does prevents me from sharing one event system: Undo means that every action and event can happen in two directions in time.&lt;br /&gt;When you attack with a creature, it becomes tapped. An ability could trigger from that tapping. If you now determine that the creature isn't allowed to attack, everything's undone and the creature is untapped again. You want to see the creature untapped in the user interface, but you don't want abilities trigger from it untapping.&lt;br /&gt;&lt;br /&gt;Of course the event handling functions after the same principle, but the events are duplicated - in some sense. There's no real 1:1 mapping between GUI and game events, because the GUI is satisfied by state-changes, while the game needs more high level events: The gui doesn't care if you've drawn a card or just put it into your hand. It just needs to know that your library has become smaller and your hand grew larger.&lt;br /&gt;&lt;br /&gt;Fortunately, Java has some built in functionality for handling these low-level events, namely PropertyChangeSupport. It allows you to keep track of properties and notify the listeners whenever a change occurs. Well, you still have to call the listeners on a change, but the major work, managing all the listeners and what properties they are interested in, is done for you.&lt;br /&gt;Along with adding PropertyChangeSupport to Laterna Magica, I have restructured a lot of code. Previously, every class implemented undo support for its properties itself. This is now separated into an extra class, which also allows to use PropertyChangeSupport more easily:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class EditableProperty&lt;t&gt; extends AbstractGameContent {&lt;/t&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private EditablePropertyChangeSupport s;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private String&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private T&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; value;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public EditableProperty(Game game, EditablePropertyChangeSupport s, String name) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this(game, s, name, null);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public EditableProperty(Game game, EditablePropertyChangeSupport s, String name, T initialValue) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(game);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.s = s;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.name = name;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; value = initialValue;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setValue(T value) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new SetValueEdit(value).execute();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public T getValue() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return value;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String toString() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return valueOf(getValue());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private class SetValueEdit extends Edit {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static final long serialVersionUID = 93955529563844615L;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private T&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; oldValue, newValue;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public SetValueEdit(T newValue) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(EditableProperty.this.getGame());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.newValue = newValue;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected void execute() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; oldValue = value;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; value = newValue;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(s != null) s.firePropertyChange(name, oldValue, newValue);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected void rollback() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; value = oldValue;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(s != null) s.firePropertyChange(name, newValue, oldValue);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String toString() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return "Set " + s.getSourceBean() + "'s " + name + " to " + newValue;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7317849024031473958?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7317849024031473958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7317849024031473958' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7317849024031473958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7317849024031473958'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/07/event-handling.html' title='Event Handling'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2068799048910362024</id><published>2010-07-15T12:04:00.000-07:00</published><updated>2010-07-15T12:04:31.047-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Combat'/><title type='text'>Combat in sight?</title><content type='html'>Both destruction and damage are more or less completed concepts by now, and at this point I'm doing combat. What does this mean?&lt;br /&gt;Every turn, you and your opponent can arrange your creatures and planeswalkers in a more or less complex relationship&lt;br /&gt;&lt;ul&gt;&lt;li&gt;some creatures attack a player or planeswalker&lt;/li&gt;&lt;li&gt;other creatures block those attackers. A single attacker may be blocked by multiple blockers and vice-versa (granted you have the right abilities around)&lt;/li&gt;&lt;li&gt;all these attackers and blockers have a damage assignment order&lt;/li&gt;&lt;li&gt;in the end, they all deal damage according to it, and they do so simultaneously. my ideas of simultaneity are... well, limited.&lt;/li&gt;&lt;/ul&gt;so, that's only the rules part. to let the player take all of the many required actions, the Actor class needs a bunch new input methods. And the GUI needs a way to control, display and update what's going on. If you think that's a lot to do, you're right ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2068799048910362024?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2068799048910362024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2068799048910362024' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2068799048910362024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2068799048910362024'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/07/combat-in-sight.html' title='Combat in sight?'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5672936982692970341</id><published>2010-07-01T08:53:00.000-07:00</published><updated>2010-07-15T12:05:08.490-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Destruction'/><title type='text'>Destruction</title><content type='html'>The last one and half a month were really sparse on working on Laterna Magica. It was the countdown of my school year and I was busy with a lot of stuff. There were loads of very work-intensive homeworks, and my year's project was also due. Anyone with experience on projects knows that half of the work is done in the two weeks before the end date and the other half is done in the two weeks after ;)&lt;br /&gt;&lt;br /&gt;I can tell you, I didn't find a single minute to work on my program, but today I finally got back to it. I don't know if I can work on it regularly again, but I will try.&lt;br /&gt;&lt;br /&gt;Before my big break, I told you that one of my next steps will be combat, but combat is a big thing and it takes many parts to work. One of these is damage.&lt;br /&gt;&lt;br /&gt;Until very recent, damage was a simple concept: Players and creatures can receive damage. When a player receives damage, he looses life; when a creature receives enough damage, it is destroyed. However, two recent changes turned things around a bit: Planeswalkers, but more importantly Wither and the accompanying changes of Lifelink etc. To make things worse, damage has also another special concept found (nearly) nowhere else in the game: sources.&lt;br /&gt;&lt;br /&gt;I suspect damage to be very annoying to program, but happily there's another thing to be done before damage really makes sense... Which happens to be what I've done today: Destruction, which was in fact so easy that I can fit it here without worrying that it will overload my post:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class DestroyPermanentEvent extends ReplaceableEvent {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private MagicObject o;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public DestroyPermanentEvent(MagicObject o) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(o);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.o = o;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected boolean execute0() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(o.getZone().getType() != Zones.BATTLEFIELD) return false;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; o.setZone(o.getOwner().getGraveyard());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;execute0() is very straightforward, which is possible because "destroy" only has one meaning, as opposed to damage, which would make a decision based on the receiver's type and both source's and receiver's abilities. Making DestroyPermanentEvent a ReplaceableEvent easily takes care of Regeneration and indestructibility. The only thing I'm not sure about is "... can't be regenerated".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5672936982692970341?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5672936982692970341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5672936982692970341' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5672936982692970341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5672936982692970341'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/07/last-one-and-half-month-were-really.html' title='Destruction'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-6516916193253900390</id><published>2010-05-12T05:23:00.000-07:00</published><updated>2010-07-22T15:24:57.089-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Cards'/><category scheme='http://www.blogger.com/atom/ns#' term='Card Editor'/><title type='text'>The card editor</title><content type='html'>The last weeks had no updates in my main project, but there's a good reason for that. I worked on a card editor that lets you download gatherer card descriptions, edit them and lets you test if the card is correct.&lt;br /&gt;&lt;br /&gt;I think I'll explain a bit more how Laterna Magica handles cards. A fundamental thought in my system was extensibility. I'm currently using a text format, but Laterna Magica allows you to use any format, even multiple ones at the same time. So, if I sometime see a reason to use e.g. XML, I can still use the old cards while developing cards for the new one.&lt;br /&gt;&lt;br /&gt;A similar thought is true for the text format itself. It is key-value based, and every key is handled by its own class. If a line is "name Llanowar Elves", then the class for "name" gets the input "Llanowar Elves" and sets the name accordingly. If the line is "text {T}: Add {G} to your mana pool.", the "text" handler tries to find an ability parser that can parse the line. Even if I can't represent an ability, I can make a class specifically for that ability and add a line handler that applies an arbitrary class to the card, like "apply laterna.magica.cards.DestroyAllCreatures".&lt;br /&gt;&lt;br /&gt;The output of the card parser is a CardTemplate object. All the CardTemplates created are stored in a zip file, so you can think of the card parser like a compiler: There's input of different types, like plain text and XML, but the parser(s) convert it into a uniform format that the program can work with (serialized java objects).&lt;br /&gt;And here, my editor comes into play. After creating a card, the editor can directly feed it into the parser, which implicitly checks if all the lines in the file are correct. The card parser will ignore lines that can't be parsed (like "Dryad Arbor is green." in my last post), but the card creator (e.g. you^^) won't: You can see the parser's warning and correct the lines.&lt;br /&gt;&lt;br /&gt;Basic editing, opening, saving and even downloading already works in the editor, but it still needs a few tweaks before it really has a benefit over normal text editors (which you will be more familiar with for sure):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Compiling cards or creating the zip file doesn't work yet&lt;/li&gt;&lt;li&gt;The card downloader does not convert a card's name, I would like to have "Shock deals two damage to target creature or player." as "~ deals two damage to target creature or player."&lt;br /&gt;Maybe even more complex transformations like "target((zone/battlefield &amp;amp; type/creature) | player)" ;)&lt;br /&gt;(that's just speculation. i have no idea how to do targets or even target parsing)&lt;/li&gt;&lt;li&gt;There is a file system tree with all the cards, but currently you can't open a file by double-clicking it&lt;/li&gt;&lt;li&gt;"Save As" should be possible by dragging an opened file (they are shown in a list) onto a folder in the tree.&lt;/li&gt;&lt;li&gt;I want to display already opened/modified files with a different icon&lt;/li&gt;&lt;/ul&gt;Okay, the last one is just bells and whistles, but the others will really influence the usability. I have already created the shadowmoor hybrid lands, which was lots of copy/paste and changing names and colors. Although these cards are not parseable yet as-is, downloading the cards would have made it easier. It's not in the GUI yet, - currently, you can only download cards by multiverse-ID or card name -   but the download functionality can use all of the strength of gatherer searching, so downloading exactly those ten hybrid lands shouldn't be too hard.&lt;br /&gt;&lt;br /&gt;This editor is like a pet to me. I know I have better things to do - combat, namely - but right now I want to get this working.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-6516916193253900390?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/6516916193253900390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=6516916193253900390' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6516916193253900390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6516916193253900390'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/05/card-editor.html' title='The card editor'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7152734335569293675</id><published>2010-05-08T16:10:00.000-07:00</published><updated>2010-05-08T16:10:07.107-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Cards'/><title type='text'>Implementing some cards</title><content type='html'>Okay, I think I've spent enough time on how abilities work in my problem, but it's definitely easier when viewing some real cards.&lt;br /&gt;&lt;br /&gt;In reality, I haven't done much of the work yet that would make making cards interesting - no targeting, no damage, no destruction or sacrificing - it's essentially creating and spending mana right now.&lt;br /&gt;&lt;br /&gt;Before I come to the interesting cards which can't be done yet, I'll show you what already works in this post. I have done a simple parser for cards. In fact, it's quite extensible and can be configured to plug-in different file formats. The only file format by now is a plain text format, for which in turn different line-parsers can be plugged in.&lt;br /&gt;&lt;br /&gt;And now to the cards:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;name&amp;nbsp;&amp;nbsp;&amp;nbsp; Forest&lt;br /&gt;super&amp;nbsp;&amp;nbsp; basic&lt;br /&gt;type&amp;nbsp;&amp;nbsp;&amp;nbsp; land - forest&lt;/div&gt;&lt;br /&gt;The basic land types have the mana producing ability implied, which are also parsed as any other mana ability. I'm not sure how this will work with adding and removing basic land types, but it's like that for now.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;name&amp;nbsp;&amp;nbsp;&amp;nbsp; Graven Cairns&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;type&amp;nbsp;&amp;nbsp;&amp;nbsp; land&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;text&amp;nbsp;&amp;nbsp;&amp;nbsp; {T}: Add {1} to your mana pool.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;text&amp;nbsp;&amp;nbsp;&amp;nbsp; {B/R}, {T}: Add {B}{B} to your mana pool.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;text&amp;nbsp;&amp;nbsp;&amp;nbsp; {B/R}, {T}: Add {B}{R} to your mana pool.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;text&amp;nbsp;&amp;nbsp;&amp;nbsp; {B/R}, {T}: Add {R}{R} to your mana pool.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Right now, there are only activated abilities. Costs and Effect are parsed separately, and every cost is parsed on its own. By now, any combination of mana costs and Tap or untap costs are working. The only Effect is "Add ... to your mana pool." Things like "{B}{B}, {B}{R} or {R}{R}" are not possible by now, neither are hybrid symbols (they only work for costs), so I write it as three abilities.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;name&amp;nbsp;&amp;nbsp;&amp;nbsp; Dryad Arbor&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;color&amp;nbsp;&amp;nbsp; green&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;type&amp;nbsp;&amp;nbsp;&amp;nbsp; land - forest&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;type&amp;nbsp;&amp;nbsp;&amp;nbsp; creature - dryad&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;pt&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; 1/1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;text&amp;nbsp;&amp;nbsp;&amp;nbsp; (Dryad Arbor isn't a spell, it's affected by summoning sickness, and it has "{T}: Add {G} to your mana pool.")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;text&amp;nbsp;&amp;nbsp;&amp;nbsp; Dryad Arbor is green.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here you can see that every type is specified on its own line only with its corresponding subtypes. The two "text" lines here are actually nonfunctional. The "Dryad Arbor is green." Ability is actually implemented by the "color" line, and the mana ability comes from the forest subtype.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7152734335569293675?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7152734335569293675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7152734335569293675' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7152734335569293675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7152734335569293675'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/05/implementing-some-cards.html' title='Implementing some cards'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3121154580237660280</id><published>2010-05-05T05:41:00.000-07:00</published><updated>2010-05-05T05:41:26.370-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Abilities'/><title type='text'>Implementing activated abilities: Divide and conquer</title><content type='html'>Last time, I showed the technical concept behind abilities (and spells) in Laterna. Today, I want to explain the PlayInformation interface from another perspective.&lt;br /&gt;&lt;br /&gt;I hope I have given a good impression of why I use the approach I do: Abilities using a function to create PlayInformation objects which do the work. But why is PlayInformation so "big"? Why does it do all the work from paying costs over targeting to dispatching the effect?&lt;br /&gt;&lt;br /&gt;The reason is a simple one: Because it's easier this way. Imagine a "complex" ability: "{X}: ~ Deals X damage to target creature." What this means is that the variable X is mentioned in both cost and effect, as most of the time, and this gives two possibilities:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Make Cost and Effect both interfaces and define some kind of communication, likely using another object between them, or&lt;/li&gt;&lt;li&gt;only use that object from "in between" and make the communication between cost and effect an implementation detail.&lt;/li&gt;&lt;/ul&gt;The process of playing a spell/ability is a complex one, and putting it into a single class enables more compact code to be written for individual abilities.&lt;br /&gt;&lt;br /&gt;Now there's only the question from last time left: How can I still define cost and effect separately, giving me the ability to reuse them?&lt;br /&gt;&lt;br /&gt;Delegation is a key concept in OO programming: If a class gets too big, try to find some separated piece of work that it does, and transfer it to a new class. Then, let the original class &lt;i&gt;use&lt;/i&gt; the new one. An example in LaternaMagica is the CardCharacteristics class: Cards have characteristics that can modified by various effects. But instead of implementing the characteristics in the MagicObject class, every MagicObject object &lt;i&gt;has&lt;/i&gt; a CardCharacteristics object. That does this work.&lt;br /&gt;&lt;br /&gt;In that case, it's defined in the interface. With PlayAbility, it isn't. It's an implementation detail of AbstractPlayInformation, which implements PlayInformation. The AbstractPlayInformation class allows you to combine multiple PlayInformation objects into a single, big PlayInformation that carries out your instructions in order.&lt;br /&gt;If you have a cantrip spell, just add in a DrawCardInformation at the end!&lt;br /&gt;If you have a complicated activated ability, implement the effect, but use standard cost Informations and combine them with the AbstractPlayInformation.&lt;br /&gt;&lt;br /&gt;Oh, and don't forget to add an information that removes the spell/ability from the stack...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3121154580237660280?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3121154580237660280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3121154580237660280' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3121154580237660280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3121154580237660280'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/05/implementing-activated-abilities-divide.html' title='Implementing activated abilities: Divide and conquer'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-1601194785968084459</id><published>2010-05-01T06:22:00.000-07:00</published><updated>2010-05-02T14:37:19.723-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Abilities'/><title type='text'>Implementing activated abilities: Getting the effect to the game</title><content type='html'>There are two paragraphs in my previous post that I want to further examine:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ActivatedAbility has a method getPlayInformation() which takes an ActivateAction and returns a PlayInformation.&lt;/li&gt;&lt;li&gt;The PlayInformation is the real worker of abilities and implements modes, targeting, costs, effects etc. It depends on the game, so an activated ability can only create the PlayInformation if a game is provided.&lt;/li&gt;&lt;/ul&gt;First, let me show you the ActivatedAbility and PlayInformation interfaces, so you can see what I'm talking about:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public interface ActivatedAbility extends NonStaticAbility {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public boolean isLegal(ActivateAction a);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public PlayInformation getPlayInformation(ActivateAction a);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //inherited from NonStaticAbility&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //public boolean isManaAbility();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public interface PlayInformation extends GameContent {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Returns the action that is used in playing.&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public PlayAction getAction();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Returns the object that was played using this play information.&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MagicObject getObject();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void makeChoices();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void chooseTargets();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void distributeEffect();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * This action is only defined for activated abilities and spells, but not for triggered abilities.&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public GameAction getCost();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public GameAction getEffect();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //inherited from GameContent&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //public Game getGame();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the Activated ability interface is pretty small; its job is primarily to create PlayInformation objects. PlayInformation does the whole work.&lt;br /&gt;&lt;br /&gt;This means especially two things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Although it would seem intuitive, to make a new activated ability, you don't implement ActivatedAbility but PlayInformation. This may be confusing for the first time, but you can get used to it. But:&lt;/li&gt;&lt;li&gt;You still have to implement the getPlayInformation() method to return your new PlayInformation. And this is what really bugged me. An ActivatedAbility class would usually exactly follow a template, and only replace the PlayInformation.&lt;/li&gt;&lt;/ul&gt;So I thought about what I could do to save me from creating all these redundant classes. The first step was to extract the job "create a PlayInformation for an ActivateAction" out of the ActivatedAbility class. As luck would have it, the Google Collect API, which I was already using, has the right interface for that: Function (simplified):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public interface Function&amp;lt;F, T&amp;gt; {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; T apply(F from);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Which led me to the ActivatedAbilityImpl class:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class ActivatedAbilityImpl extends NonStaticAbilityImpl implements ActivatedAbility {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static final long&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; serialVersionUID = -8987886570576715112L;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Function&amp;lt;? super ActivateAction, ? extends PlayInformation&amp;gt; object;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Predicate&amp;lt;? super ActivateAction&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; legal;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ActivatedAbilityImpl(boolean manaAbility, Function&amp;lt;? super ActivateAction, ? extends PlayInformation&amp;gt; object, Predicate&amp;lt;? super ActivateAction&amp;gt; legal, String text) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(manaAbility, text);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.object = object;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.legal = legal;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /**&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Returns if casting the spell using the specified CastAction is legal.&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public boolean isLegal(ActivateAction a) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return legal.apply(a);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public PlayInformation getPlayInformation(ActivateAction a) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return object.apply(a);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;(Predicate is also from Google Collect and represents a true/false decision about an object)&lt;br /&gt;&lt;br /&gt;But that step doesn't really solve the problem. Now I don't have to implement ActivatedAbility, but instead Function. The real solution only came when diving deep into Java's pool of features: Reflection. Reflection means to analyze a program's parts, like, classes, methods, constructors and so on while it's running. So my idea was to make a Function which creates a new object of any class you want:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;class B {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public B(A a) {}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Function&amp;lt;A, B&amp;gt; f = new MyFunction&amp;lt;A, B&amp;gt;(A.class, B.class);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;B b = f.apply(new A());&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...and creating a new class has transformed into creating an object! However, there is still a problem: what if your PlayInformation needs configuration? Your PlayInformation might Implement the ability "&amp;lt;mana cost&amp;gt;: Tap target permanent." How do you set the mana cost? The ActivateAction, which is the function's input, can't provide it. What you need is a per-function configuration in addition to the per-apply parameter:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;class B {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public B(C c, A a) {}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;C config = new C();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Function&amp;lt;A, B&amp;gt; f = new MyFunction&amp;lt;A, B, C&amp;gt;(C.class, config, A.class, B.class);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;B b = f.apply(new A());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//b is equal to&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//new B(config, new A());&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And that's exactly how I ended up, and I'm quite satisfied with my work. You can look at the source code at &lt;a href="http://code.google.com/p/laterna-magica/source/browse/trunk/laterna/src/laterna/magica/util/FactoryFunction.java"&gt;FactoryFunction.java&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And the last note for today: PlayInformation has many things to do, among them providing the ability's (or spell's, PlayInformation is used for both) cost and effect. By Implementing both in the same class, you take yourself the opportunity to reuse costs in other abilities. Next time, I'll show you how to mix-and-match these.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-1601194785968084459?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/1601194785968084459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=1601194785968084459' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1601194785968084459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1601194785968084459'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/05/implementing-activated-abilities.html' title='Implementing activated abilities: Getting the effect to the game'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-6479348658858352629</id><published>2010-04-29T14:13:00.000-07:00</published><updated>2010-04-29T14:13:35.773-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Magic'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Cards - Two views on the same thing</title><content type='html'>I would really like to know if you can imagine of what two views I'm going to talk about. I guess it's more obvious for people who have thought about how to structure a magic application. If you like, leave a comment and state what's your background.&lt;br /&gt;&lt;br /&gt;When you open a booster pack, how do you read cards? They have symbols and text, and of course you can read them and determine what they do. However, the card can't do anything, because you're not playing a game.&lt;br /&gt;During a game, the card has a complex context that influences how you read it; For example, its color may have changed.&lt;br /&gt;&lt;br /&gt;Humans can read a card outside the game and inside the game, but it's hard for a computer. The difference between these two views is very important and currently makes me some problems. A card outside the game (template), like the one from the booster, is a constant thing - it can't change between different copies. This means that multipe cards inside the game can share the same template, but those are then individuals. They have the same template, but that's it.&lt;br /&gt;The same thought can be applied to abilities and spells, I think. Abilities and spells have another, even harder problem: Their effects. While cards just sit there, spells and abilities can directly influence the game, and what influence that is depends on how the spell/ability was played.&lt;br /&gt;&lt;br /&gt;So what's the solution to the problem? Well, in a nutshell, the data structure for cards, spells and abilities is duplicated.&lt;br /&gt;&lt;br /&gt;Outside of the game is CardTemplate and CardParts, inside the game is CardObject and CardCharacteristics. A CardCharacteristics is assigned to a CardObject is responsible for providing the characteristics, respecting effects and the layer system, but also taking into account the values "printed" on a CardParts object.&lt;br /&gt;&lt;br /&gt;Outside the game is ActivatedAbility, inside the game is ActivateAction, PlayInformation and AbilityObject.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ActivatedAbility has a method getPlayInformation() which takes an ActivateAction and returns a PlayInformation.&lt;/li&gt;&lt;li&gt;The PlayInformation is the real worker of abilities and implements modes, targeting, costs, effects etc. It depends on the game, so an activated ability can only create the PlayInformation if a game is provided.&lt;/li&gt;&lt;li&gt;And to make things easier, the activate action has some additional infos like who played the ability and what card the ability is on (like I said, ActivatedAbility is outside the game, so it doesn't know which cards it is on. the ActivateAction knows).&lt;/li&gt;&lt;/ul&gt;All that happens under the surface. The ActivateAction retrieves the PlayInformation and does all the necessary things, like putting an AbilityObject onto the stack etc.&lt;br /&gt;&lt;br /&gt;I hope that you had a chance to look into my program; I know that this is very hard to imagine without directly looking at the code, So I won't try to go into more detail unless you wish so ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-6479348658858352629?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/6479348658858352629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=6479348658858352629' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6479348658858352629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6479348658858352629'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/04/cards-two-views-on-same-thing.html' title='Cards - Two views on the same thing'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2088734355423773755</id><published>2010-04-26T05:48:00.000-07:00</published><updated>2010-04-26T05:48:17.043-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Hardcoded vs Softcoded</title><content type='html'>From a user's perspective, a program's job is to do work, look good etc. If you want to look into a program, and very deep into it, you will notice that all a program does is to move data around. Data and code are two very important things in programming, because the code does the work, and only with data there is work.&lt;br /&gt;&lt;br /&gt;But the borders can become unclear when you think of it. Data and code do both lie on your hard disk, or in memory when a program runs, so where's the difference? Well, code is &lt;i&gt;data that represents&lt;/i&gt; some kind of work.&lt;br /&gt;&lt;br /&gt;Magic is all about doing work. "You lose one life" is a piece of work. After the work is done, I might have lost the game. Still, I wouldn't like to have that effect around in my code - in my &lt;i&gt;Java&lt;/i&gt; code, at least.&lt;br /&gt;&lt;br /&gt;Hardcoding and softcoding is all about how work is represented.&lt;br /&gt;Hardcoding essentially means "compiled". For example, the five colors and the card types are hardcoded into laterna - to change one, you need the source files and a Java compiler to generate a runnable program from it once again.&lt;br /&gt;Softcoding, on the other hand, means "interpreted". "You lose one life", which might be a spell's effect, could be written in a text file. That file is read when the program is started, and will do its work, although it never was java code.&lt;br /&gt;&lt;br /&gt;So softcoding is the better way to do work in all situations? Clearly no. Enabling something to be softcoded needs massive effort. Not only do you need to parse the file and get the important information from it, you also need to translate it into a form the program can handle. For Magic cards, this is very complex, since the work is done much later than the card is read, And a card might do different things everytime you use it. I'll talk about it in more detail the next time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2088734355423773755?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2088734355423773755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2088734355423773755' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2088734355423773755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2088734355423773755'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/04/hardcoded-vs-softcoded.html' title='Hardcoded vs Softcoded'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-860726383979075605</id><published>2010-04-19T14:45:00.000-07:00</published><updated>2010-04-19T14:45:30.781-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Spell'/><title type='text'>Spells</title><content type='html'>I fear that my blog evolves into a redundant commit log of my project, but I really try to write interesting and original.&lt;br /&gt;&lt;br /&gt;First, let me say I'm steadily making progress. Now there's a mana pool and you can play mana abilities. Lands do untap and mana is removed properly. I have Llanowar Elves, my first nonland card, which you can play. It's put on the stack and then resolves. You don't pay costs for it yet...&lt;br /&gt;&lt;br /&gt;And that's exactly my topic: What does it take to play a spell? There are several reasons why I started with lands, but the most important is that playin&lt;span style="font-family: inherit;"&gt;g a land is a relatively simple thing: "&lt;/span&gt;To play a land, a player puts that land onto the battlefield from the zone it was in".&lt;br /&gt;&lt;br /&gt;Playing a land is a single line in a Subpoint of the Subpoint 114.2a of "Special Actions" in the comprehensive rules. "Casting Spells" is Section 601, and 601.2 describes the steps necessary to do so, a to h, each one much longer than 114.2a.&lt;br /&gt;&lt;br /&gt;The best known steps for playing spells are choosing targets and paying costs. Targets are a somewhat advanced, and I will rest a little before implementing them, but costs are a really essential part. The hard part is not costs per se, but paying them. Just for paying a mana cost, the player must choose how to pay for hybrid or variable symbols and then choose which mana to use.&lt;br /&gt;&lt;br /&gt;Now that I know that playing spells works in general, I can try to fill in the details. When I have costs, and can therefore play simple spells rules-compliant, I might take a break to do some cards, improve the GUI, or implement combat... And swoosh! A game isn't that far away any more!&lt;br /&gt;&lt;br /&gt;PS: There's one cost working already: The tap cost ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-860726383979075605?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/860726383979075605/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=860726383979075605' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/860726383979075605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/860726383979075605'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/04/spells.html' title='Spells'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-8660551030585088848</id><published>2010-04-11T09:23:00.000-07:00</published><updated>2010-04-11T09:23:35.412-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Finally...</title><content type='html'>I'm glad to announce the first "playable" version of Laterna Magica. Features:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A rather simple GUI &lt;/li&gt;&lt;li&gt;Drawing a card during your draw step&lt;/li&gt;&lt;li&gt;Playing Lands&lt;/li&gt;&lt;li&gt;An opponent who does... nothing&lt;/li&gt;&lt;/ul&gt;Before I present you some screenshots, let me explain the "glue" I've talked about the last time, especially the controller.&lt;br /&gt;&lt;br /&gt;The class Player in laterna magica is more or less a data structure and can only perform tasks that don't need decisions - like loosing life, drawing a card or discarding a card at random.&lt;br /&gt;Discarding a chosen card is different, because it needs knowledge, which is something the Player class should &lt;i&gt;not&lt;/i&gt; have. Different players make different decisions, and in different ways.&lt;br /&gt;&lt;br /&gt;The solution is the Actor class. Whenever some decision is needed, like which card to discard, the actor is asked for that decision. The actor may implement an AI algorithm for choosing, it may get the choice over the network, or, most naturally, depend on user input from the GUI.&lt;br /&gt;&lt;br /&gt;Although separating actor from player was a clear choice for me, I don't know how that class will look in the end. Magic is a game with tons of choices - there's not only "discard a card" but also "discard two cards", "discard a creature card", "discard a card of each color"... you know what I mean, this can quickly get messy.&lt;br /&gt;For now, there's basically only "You have priority. What do you want to do?", so that problem is still far away.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And now screenies! When you start the program, you get a GUI with an empty board, like this:&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_SUHKGhHw1Ys/S8HvElUy6DI/AAAAAAAAAEg/RinTMXJvbFc/s1600/Magica0.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="253" src="http://4.bp.blogspot.com/_SUHKGhHw1Ys/S8HvElUy6DI/AAAAAAAAAEg/RinTMXJvbFc/s640/Magica0.png" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;But it doesn't stay for long, it will quickly populate your game with cards. You can watch how your library grows and you draw cards!&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_SUHKGhHw1Ys/S8Hv4RFpXZI/AAAAAAAAAEo/oaMdBASNbtQ/s1600/Magica1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_SUHKGhHw1Ys/S8Hv4RFpXZI/AAAAAAAAAEo/oaMdBASNbtQ/s640/Magica1.png" /&gt;&lt;/a&gt;&lt;/div&gt;After passing priority twice, It will be your main, and you can play one of your (many) Plains:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_SUHKGhHw1Ys/S8HwpBTGKQI/AAAAAAAAAEw/WyihElc7Gl8/s1600/Magica2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_SUHKGhHw1Ys/S8HwpBTGKQI/AAAAAAAAAEw/WyihElc7Gl8/s640/Magica2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_SUHKGhHw1Ys/S8HwvdjsAsI/AAAAAAAAAE4/etFxi-LGYOI/s1600/Magica3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_SUHKGhHw1Ys/S8HwvdjsAsI/AAAAAAAAAE4/etFxi-LGYOI/s640/Magica3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_SUHKGhHw1Ys/S8Hw2q2ZShI/AAAAAAAAAFA/ZHuuQQteOeA/s1600/Magica4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_SUHKGhHw1Ys/S8Hw2q2ZShI/AAAAAAAAAFA/ZHuuQQteOeA/s640/Magica4.png" /&gt;&lt;/a&gt;&lt;/div&gt;Looks great? Look at that, during your next turn, you can do it again^^&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_SUHKGhHw1Ys/S8Hxdj8H91I/AAAAAAAAAFI/aXR-Dsmli5Q/s1600/Magica5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_SUHKGhHw1Ys/S8Hxdj8H91I/AAAAAAAAAFI/aXR-Dsmli5Q/s640/Magica5.png" /&gt;&lt;/a&gt;&lt;/div&gt;What you have just seen demonstrates some important things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Laterna Magica has many events. Playing a land doesn't explicitly remove it from the panel. Instead, the panel listens for cards entering/exiting the zone (and later also controller changes) to keep track of what cards are to be shown. When the land is played, the rules engine moves it, and the GUI listens to the rules engine. Similar things are true for the zone size labels, the life total labels and the turn progress label.&lt;/li&gt;&lt;li&gt;And because swing supports it, the game loop (passing priority back and forth, basically) can run in an extra thread. Whenever the player "freak" has priority, the GameLoop queries the Actor for what to do. That actor is a GuiActor and waits for the user to either click "Pass priority" or a Plains card. Therefore, the GUI thread is not blocked when the game loop is working.&lt;/li&gt;&lt;li&gt;The GUI has a utility for finding all the actions possible for clicking a card. Currently, there's only playing a land, but it will also check for playing spells or activated abilities, unmorphing etc. in the future. That Utility also checks if the action is legal (timing, only one land per turn etc.)&lt;/li&gt;&lt;li&gt;what aparently doesn't work is discarding excess cards at end of turn. While it's easy, I've chosen to show you what I have as soon as drawing and playing a land works.&lt;/li&gt;&lt;li&gt;If you would click yourself all the way through until a library is empty, you would notice that the game doesn't end with that. Although the state based action does exist, the loseGame() method of player is a No-Op.&lt;/li&gt;&lt;/ul&gt;okay, enough for now. Have fun playing some real games ;) &lt;br /&gt;&lt;ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-8660551030585088848?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/8660551030585088848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=8660551030585088848' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/8660551030585088848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/8660551030585088848'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/04/finally.html' title='Finally...'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SUHKGhHw1Ys/S8HvElUy6DI/AAAAAAAAAEg/RinTMXJvbFc/s72-c/Magica0.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3696128146144805021</id><published>2010-03-30T09:44:00.000-07:00</published><updated>2010-03-30T14:53:25.626-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Getting a runnable program</title><content type='html'>It's a nice thing to get everything right the first time. I think I have many things working already, among them are characteristic-setting effects, replacement effects, skipping turns, taking extra turns, and more than two players.&lt;br /&gt;&lt;br /&gt;But what I don't have is a game, which is rather disappointing. Sometimes, I think "okay I want to really try it out, a real-world situation. just add in the glue that's missing and make a simple GUI", and then I realize how much of the glue is missing.&lt;br /&gt;&lt;br /&gt;Even though I have effects, I have neither spells nor abilities. I can't play cards or abilities. I have (vanilla) creatures, but no combat. I haven't even tested zones, not to mention the Stack.&lt;br /&gt;&lt;br /&gt;Okay, now that I finished whining, let me say that I'm still on this. I'm not giving up. The last few days, I focused on the tight core of magic. Turn order is done, all with phases, steps and priority. Next comes the zones, excluding the Stack for now. When I'm done with that, I'll try to program playing a land, which is drastically different from playing spells or abilities.&lt;br /&gt;&lt;br /&gt;This should be enough "core engine" to let me finally play a game. Draw a card, play a land or not, pass priority back and forth until it's the opponent's turn (myself again, no AI yet), and so forth.&lt;br /&gt;Of course, there are needs besides the core engine. Most obviously, a GUI. The other two that come to my mind are decks, which i have started already, and a "controller". LaternaMagica has a Player class, which handles life points and so on, but there's no way yet to to make decisions. But that's another story ;)&lt;br /&gt;&lt;br /&gt;Quick follow-up: an important thing I've forgotten for my to-do list is state-based actions. it would be too bad if a player doesn't die when he has no life (however that should happen without creatures or spells...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3696128146144805021?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3696128146144805021/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3696128146144805021' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3696128146144805021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3696128146144805021'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/03/getting-runnable-program.html' title='Getting a runnable program'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2065245063677047326</id><published>2010-03-26T07:45:00.000-07:00</published><updated>2010-03-26T07:45:42.522-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Undo ... and replacement effects!</title><content type='html'>I finally got time to work on Laterna Magica again. It wasn't that exciting, just adding undo support to all the classes that already existed. While I browsed the classes and looked for actions that I hadn't revised yet, I found the LifeTotal class. Its job is easy: store the number of life points for a player, and whenever it changes, inform listeners.&lt;br /&gt;&lt;br /&gt;...except that there are replacement effects. Only because I want to change life points, that doesn't mean it happens! what could be done?&lt;br /&gt;&lt;br /&gt;Surprisingly, the solution looks very similar to my undo engine: Every modification is encapsulated into an edit and stored in the engine. If you want to undo something, the undo engine knows what happened, and every single edit knows how to revert itself. For the caller, it's only &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;addListener(myListener)&lt;/span&gt;, but inside there's &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;new AddListenerEdit(myListener).execute()&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Similarly, the replacement engine is the authority for performing replaceable actions, like gaining life. Calling &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gainLife(amount)&lt;/span&gt; really means &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;new LifeEvent(amount).execute()&lt;/span&gt;.&lt;br /&gt;Execute uses the replacement engine to apply all replacement effects. Those may replace the event with another event, until all are through. The resulting event is then really executed (the implementation method is called &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;execute0&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Edit&lt;/span&gt; and &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ReplaceableEvent&lt;/span&gt; are not connected. Both have an &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;execute&lt;/span&gt; method, but that's more or less coincidence. Logically, there is a connection. Look at &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;execute0&lt;/span&gt; from LifeEvent:&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public void execute0() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ((LifeTotalImpl) getPlayer().getLifeTotal()).fireLifeEvent(this);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//...and from LifeTotalImpl&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;void fireLifeEvent(LifeEvent e) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CompoundEdit ed = new CompoundEdit(getGame(), true, "Adjust " + getPlayer() + "'s life by "&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; + (e.isGained()? "+":"-") + e.getAmount());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; new AdjustLifeEdit(e.getChange()).execute();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Guaranteed to return a non-null array&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Object[] l = listeners.getListenerList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Process the listeners last to first, notifying&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // those that are interested in this event&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; boolean gain = e.isGained();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i = l.length - 2; i &amp;gt;= 0; i -= 2) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(l[i] == LifeListener.class) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(gain) ((LifeListener) l[i + 1]).lifeGained(e);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else ((LifeListener) l[i + 1]).lifeLost(e);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ed.end();&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;Two things are of importance here: First, changing the life is done through an edit, so it can be undone. Second, informing the listeners is encapsulated ina compound edit, so that everything related to this event is grouped together.&lt;br /&gt;&lt;br /&gt;I'm quite proud about this piece of code; I expected replacement effects to be very hard, but as it turns out, if there are no flaws in this concept, they should be quite easy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2065245063677047326?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2065245063677047326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2065245063677047326' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2065245063677047326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2065245063677047326'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/03/undo-and-replacement-effects.html' title='Undo ... and replacement effects!'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2781503874759114720</id><published>2010-03-24T13:40:00.000-07:00</published><updated>2010-03-30T09:12:16.490-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Progress: LaternaCommon</title><content type='html'>The second thing I've done in the last months is LaternaCommon. It is more or less a collection of useful utilities:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It provides a single, application-wide TreeProperties object, loaded from a configurable location&lt;/li&gt;&lt;li&gt;Configures log4j from a properties file, either configured directly, or by naming a property which stores the location&lt;/li&gt;&lt;li&gt;Provides an error-handling dialog for uncaught exceptions or to invoke manually&lt;/li&gt;&lt;li&gt;last but not least, it has a tool that can automatically unzip a file inside the jar. this is very useful to init an application on its first launch.&lt;br /&gt;With this utility, you store a regular zip file inside the jar, and configure a destination directory and a check file on the file system. If the check file is not found on launch, the zip file is unpacked to the specified directory. Any number of zip files can be specified this way.&lt;/li&gt;&lt;/ul&gt;I really like how these features all integrate: if the properties don't exist, unpack them. then, configure logging using the properties. last, use the logging utilities for error handling.&lt;br /&gt;You can use this, too, for free. However, I haven't yet uploaded a jar'ed version of it.&lt;br /&gt;&lt;br /&gt;Okay, that's for now, and don't worry, in the last few days I resumed development of the core program!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2781503874759114720?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2781503874759114720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2781503874759114720' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2781503874759114720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2781503874759114720'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/03/progress-laternacommon.html' title='Progress: LaternaCommon'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5091075103715333494</id><published>2010-03-20T16:00:00.000-07:00</published><updated>2010-03-30T09:12:16.491-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Progress: TreeProperties</title><content type='html'>I just realized that I didn't post anything for about two months. Well, I did do something, and I want to keep you up.&lt;br /&gt;&lt;br /&gt;I have to say that code versioning systems like SVN are a great thing. Besides having a backup on the internet, I'm able to trace back my actions in the last two months. You can look &lt;a href="http://code.google.com/p/laterna-magica/updates/list" linkindex="17"&gt;here&lt;/a&gt; anytime to see what I was coding recently.&lt;br /&gt;&lt;br /&gt;My task was to rework the TreeProperties system that i have come up with sometimes. The point behind it is that you can store properties in simple text files and, for example, implement localization this way. Well, java has something like that built in, but my Library allows much more, the biggest pro being that properties can be split over several files and keys are hierarchical. You can read more about - and download - it &lt;a href="http://code.google.com/p/laterna-magica/wiki/TreeProperties" linkindex="18"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Then was a long pause, I took a vacation and went skiing. After that, work went on, and I added many features, fixed bugs etc. In a nutshell, what the library is able to do is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Store properties in a tree structure of multiple files&lt;br /&gt;A file can reference another file, which is then parsed in the same way. Thus, any structure of files may be used to realize the needed properties.&lt;/li&gt;&lt;li&gt;Use hierarchical property-keys, like "/lang/en/save"&lt;br /&gt;The namespace mechanism is independent of the file structure, i.e. not bound to a directory structure. When a file is referenced, it inherits its referrer's namespace. For example, if you reference a file with the key "/lang/en", its "save" property has the full key "/lang/en/save". However, if you prefer, you can reference the file with the empty key and literally use "/lang/en/save" in the included file for the same result.&lt;/li&gt;&lt;li&gt;Support for many data types&lt;br /&gt;Unlike the java Properties class, which only supports unparsed string values, TreeProperties supports many data types out of the box - no more casting and parsing when accessing properties! Data types include all of the java primitive types, String, BigInteger and BigDecimal, Date and Calendar, File, Path and URL. The mechanism of data types is easily extendable. It requires implementing one interface and registering the type - that's it!&lt;br /&gt;Special attention goes to the Path type - it adds an abstraction to regular java files that allows to use system properties to be substituted in the path. for example, the values "~/file.txt" or "${user.home}/file.txt", interpreted using the path type, both represent the file "file.txt" in the executing user's home directory. Including files (see above) uses the path type to parse the include, therefore the included file may be personalized for each user, allowing him to set preferences without annoying other users.&lt;/li&gt;&lt;li&gt;Simple access to the API&lt;br /&gt;Most needs don't go beyond reading and writing existing properties. Therefore there's a class for exactly that - it has convenience methods that return the standard types already cast, allows default values, and supports localization. The only reason to read into the API is if you need to store new properties.&lt;/li&gt;&lt;li&gt;Easy GUI&lt;br /&gt;TreeProperties is a background utility that makes a developer's life way easier. However, now and then there's a case when it steps into the foreground. If you want to show a GUI to the user to let him edit the values, don't build it yourself. It's already there! Setting up a GUI with labels and input components to allow the user to edit properties requires a single method call. what you get is a fully layouted swing panel, ready to add to your GUI. The input components are adjusted for the data type - (formatted) text fields, checkboxes and dropdown fields - and initialized to the current value. Changes to the inputs write to the properties object and are therefore available immediately.&lt;/li&gt;&lt;/ul&gt;I hope you enjoyed the insight and my work - TreeProperties is open source and can be downloaded as a single jar file. More infos about file formats and usage &lt;a href="http://code.google.com/p/laterna-magica/wiki/TreeProperties" linkindex="19"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Next time is about the new sibling of the LaternaMagica project, LaternaCommon, which adds even another level of convenience for the developer in the areas of logging, error handling and deployment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5091075103715333494?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5091075103715333494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5091075103715333494' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5091075103715333494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5091075103715333494'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/03/progress-treeproperties.html' title='Progress: TreeProperties'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-806205788281010379</id><published>2010-01-24T08:37:00.000-08:00</published><updated>2010-03-30T09:12:47.270-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Implementing Undo: A working approach</title><content type='html'>When I realized that the need for an extra parameter really hurts the usability, I had to rethink the way of storing moves. Additionally, moves should be executed immediately, and not simply stored and scheduled for later execution, to make the semantics feel more natural.&lt;br /&gt;By the way, I changed the&amp;nbsp; name from Move to Edit, since that is the classic name for undoable stuff. Not that it would matter too much...&lt;br /&gt;&lt;br /&gt;I didn't want to give up the tree, but needed a way to get the parent without using a parameter. Gladly, I found a solution that makes it pretty easy to create the right hierarchy. In a nutshell, the node that is currently appended to is stored in the controller, and when a node is created, it automatically finds the controller that belongs to the game.&lt;br /&gt;This way, I was able to keep my tree structure and only had to do minimal changes. First, I had to change the Edit constructor to add itself to the controller. Second, the controller needed some notion of the current parent node.&lt;br /&gt;&lt;br /&gt;Well, there were some problems on the way... I wanted to automatically execute edits at creation. the problem is the order of initialization. Imagine this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;abstract class Edit {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public Move() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //add to the controller...&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; //then: &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; execute();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public abstract void execute();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public abstract void rollback();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;class MyEdit extends Edit {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private Object importantValue;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MyEdit(Object importantValue) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.importantValue = importantValue;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public abstract void execute() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //do something with importantValue&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public abstract void rollback() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //undo something with importantValue&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;execute is called in the Edit Constructor, which is called at the beginning of initialization, before importantValue is set! Although it's logical that the superclass must be initialized first - starting with Object, which reserves memory on the heap to store atributes in the first place - this leads to the requirement for some annoying workarounds. For this, it's just to move the execute out of the constructor - it is essentially done after constructing any move. I will show another workaround later - for the random number generator - until then, my new code is online; i had a hard time with subclipse, but everything's alright now. feel free to peek into laterna.magica.edit, and laterna.magica.timestamp; yet the only classes to use this new development&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-806205788281010379?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/806205788281010379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=806205788281010379' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/806205788281010379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/806205788281010379'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/01/implementing-undo-working-approach.html' title='Implementing Undo: A working approach'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3520151045700279028</id><published>2010-01-16T15:01:00.000-08:00</published><updated>2010-03-30T09:12:47.271-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Implementing Undo: Trial and Error</title><content type='html'>I'm working eagerly on undo, and have a few thoughts to share on it. first, let me say that it's &lt;i&gt;really&lt;/i&gt; not easy. so many thing can go wrong, since undo should mean "create a state as if the last action had never occured". it's easy to forget one of the implicit things that happen when you change something. if it's an event listener, or updating some kind of cache, there are so many possibilities.&lt;br /&gt;&lt;br /&gt;the first thing to do when you start a programming task is deciding your goals. for undo, I had two goals of great importance&lt;br /&gt;&lt;ul&gt;&lt;li&gt;it must be possible to divide big actions into several small ones. ideally, every action is so small that it only changes one value. in other words, an action should be simple enough that you can find almost all bugs&lt;/li&gt;&lt;li&gt;it should be easy to use the system. magic has many cards, and if the majority of card code is related to the undo system, that can't be good. coding cards should be easy, ideally fun, so that many of them get implemented.&lt;/li&gt;&lt;/ul&gt;these don't really sound very compatible. be very precise and document every action, but don't bother me with it...&lt;br /&gt;&lt;br /&gt;the second should be looking for help and reference material, but I just skipped over to the third one: error ;) &lt;br /&gt;&lt;br /&gt;my first thought was a tree of actions with a controller actually executing of them. the controller checks that actions are executed in proper sequence. the work would be to create and append the action, and then let the controller execute it. the parent action is passed as a parameter to the modifying method so it knows where to plug the action into the tree:&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;//from laterna.magica.timestamp.Timestamp&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;private TimestampFactory f;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;private int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; timestamp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;public void updateTimestamp(Move m) {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //the constructor plugs the move into the tree&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //with m as its parent&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Move parent = new CompoundMove(m, true);&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //plugs in the move which creates the next timestamp&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //for the factory&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; f.nextTimestamp(parent);&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //plugs in the move which updates this Timestamp's value&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; new UpdateTimestampMove(parent);&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;}&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;private class UpdateTimestampMove extends Move {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; before;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public UpdateTimestampMove(Move parent) {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Move does the tree stuff&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(parent);&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; before = timestamp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void make() {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; timestamp = f.getCurrentTimestamp();&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void undo() {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; timestamp = before;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;}&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;//from laterna.magica.timestamp.TimestampFactory&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;private int currentTimestamp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;public int getCurrentTimestamp() {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return currentTimestamp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;}&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;public void nextTimestamp(Move m) {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; new NextTimestampMove(m);&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;}&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;private class NextTimestampMove extends Move {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; currentTimestamp;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public NextTimestampMove(Move parent) {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(parent);&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; currentTimestamp = TimestampFactory.this.currentTimestamp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void make() {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Set the timestamp after that of this move as current&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TimestampFactory.this.currentTimestamp = currentTimestamp + 1;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void undo() {&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Resets to the current timestamp of this move&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TimestampFactory.this.currentTimestamp = currentTimestamp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;}&lt;/span&gt;&lt;br /&gt;You know what? a call to updateTimestamp() doesn't even really update the timestamp! and even better, it isn't correct just because of that. the line&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: x-small;"&gt;currentTimestamp = TimestampFactory.this.currentTimestamp;&lt;/span&gt;&lt;br /&gt;is executed when the move is constructed. so, if two such moves are constructed before any of them was executed, they bear the same value.&lt;br /&gt;&lt;br /&gt;this is known as "lost update": you expect something to be executed in the order read-compute-write-read-compute-write, but really it's read-read-compute-compute-write-write, or something similar. both compute steps can't take into account the other computation, respectively, and the first write step is plainly overwritten.&lt;br /&gt;&lt;br /&gt;this is exactly the problem with the code: what you expect (having the new timestamp stored after the call) ist not what the code does, and such misunderstandings are a major source of bugs. additionally, the second point is almost ignored. this layout requires an additional parameter in every method that does the slightest modification, and a manual "execute" in the controller to do the actual work.&lt;br /&gt;&lt;br /&gt;impressing how lines fly if you really have something to talk about! i will continue with this story very soon, until then here's a quick lines-of-code count with cloc.pl:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;http://cloc.sourceforge.net v 1.08&amp;nbsp; T=1.0 s (138.0 files/s, 8966.0 lines/s)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;-------------------------------------------------------------------------------&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Language&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; files&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; blank&amp;nbsp;&amp;nbsp; comment&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; code&amp;nbsp;&amp;nbsp;&amp;nbsp; scale&amp;nbsp;&amp;nbsp; 3rd gen. equiv&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;-------------------------------------------------------------------------------&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Java&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 138&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1497&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3126&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4343 x&amp;nbsp;&amp;nbsp; 1.36 =&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5906.48&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Impressing, there are alone 1.5k EMPTY lines in my program ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3520151045700279028?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3520151045700279028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3520151045700279028' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3520151045700279028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3520151045700279028'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/01/implementing-undo-trial-and-error.html' title='Implementing Undo: Trial and Error'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2963586389759204107</id><published>2010-01-07T02:26:00.000-08:00</published><updated>2010-03-30T09:13:20.320-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Architecture'/><title type='text'>Complex systems</title><content type='html'>&lt;a href="http://laterna--magica.blogspot.com/2009/12/program-architecture.html" linkindex="620"&gt;Previously&lt;/a&gt;, I said that business applications were not interesting from a programming perspective. Well, I think this is true in general, but there's probably one big exception: Enterprise Ressource Planning systems. These are pretty complex, allow plugins and customization to a big extent.&lt;br /&gt;Another thing that identifies sophisticated systems is the modelling of not only data but also actions in the software. This is very important in ERP systems, so that decisions are traceable, and the output of employees or departments is visible.&lt;br /&gt;&lt;br /&gt;This came to my mind during today's Business Information Management class. It may be a little off-topic, but it's good to see that there's interesting software around besides games.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2963586389759204107?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2963586389759204107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2963586389759204107' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2963586389759204107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2963586389759204107'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/01/complex-systems.html' title='Complex systems'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-4321867314079972927</id><published>2010-01-04T12:14:00.000-08:00</published><updated>2010-03-30T09:12:47.271-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Pros of Undo</title><content type='html'>The following is an interesting idea, and it's not one of mine. I can't find the source at the moment, but it was somewhere in the &lt;a href="http://slightlymagic.net/" linkindex="379"&gt;slightlymagic.net&lt;/a&gt; forums.&lt;br /&gt;&lt;br /&gt;The idea is that Undo implies the recording of each action during a game, and that this information can be used for many different things: Storm counts the number of spells played during the current turn, War Elemental is sacrificed if no opponent was damaged during the turn.&lt;br /&gt;&lt;br /&gt;But the most interesting thing is last known information. This is the concept that if a card changes zones, its state directly before it left the zone is used for effects that referenced it earlier. For example &lt;a href="http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=5151" linkindex="380"&gt;Fling&lt;/a&gt;: This card requires you to sacrifice a creature, and then takes its power. However, the power is not taken from the graveyard, but from the last time the creature was on the battlefield: If it was equipped, the modified power is taken, even though its power is back to normal in the graveyard.&lt;br /&gt;Last known information is used more often than that: Every ability that triggers from an object leaving the battlefield may require it.&lt;br /&gt;&lt;br /&gt;However, there's a difference to Storm and such: Storm only looks into the past, on what happened, but last known information requires you to actually go back, to see the whole game state at that time, so that you can check the card's characteristics. I'm not entirely sure if Undo is suitable for that, but I will definitely think about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-4321867314079972927?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/4321867314079972927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=4321867314079972927' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/4321867314079972927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/4321867314079972927'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2010/01/pros-of-undo.html' title='Pros of Undo'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3535722700870303123</id><published>2009-12-19T04:40:00.000-08:00</published><updated>2009-12-19T04:40:10.098-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Philosophy'/><title type='text'>Intelligence</title><content type='html'>today is nothing about programming, just philosophic thoughts about the concept of Intelligence.&lt;br /&gt;It's hard define intelligence, but one thing that for sure sets us apart from computers (currently) is our extreme potential of learning. No matter if it's the complex rules, or the uncounted combos and strategies, we play magic without any problems. To get a computer to play magic, we need complex programs and have to manually teach it what is good and what bad play.&lt;br /&gt;But intelligence is nothing bound only to the brain. The brain is a very complex structure of neurons that, every one on its own, only does basic tasks. however, the sum is much greater than its parts.&lt;br /&gt;&lt;br /&gt;we're able to let computers perform basic operations, so we should also be able, in principle, to develop a really intelligent program.&lt;br /&gt;A nice example is &lt;a href="http://en.wikipedia.org/wiki/Conway%27s_game_of_life" linkindex="220"&gt;Conway's Game of Life&lt;/a&gt;, a cellular automaton with a very simple set of rules. For every state, the next "generation" can be calculated by applying the rules. By this, it's possible to encode programs into the pattern. In principle, this game is mighty enough to do any calculation - even intelligence!&lt;br /&gt;And now, we look at that gigantic pattern as it evolves and changes. It's not able to communicate with us directly, but with the right understanding of what the movements mean, we could "read its mind"... maybe it's thinking about its creator?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3535722700870303123?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3535722700870303123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3535722700870303123' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3535722700870303123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3535722700870303123'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/12/intelligence.html' title='Intelligence'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7899203228296537503</id><published>2009-12-17T13:02:00.000-08:00</published><updated>2010-03-30T09:12:47.272-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Undo'/><title type='text'>Undo</title><content type='html'>Imagine we are in such a good mood that we forget all our sorrows, make big business, and one day we notice that our earth isn't very well. We better had implemented undo so that the tropic rain forest is up and fossil oil is down again...&lt;br /&gt;&lt;br /&gt;that's one of the hard parts with undo: the consequences of your action must be undoable. the second is not to forget anything.&lt;br /&gt;&lt;br /&gt;What are the consequences of playing a card? you tap your lands, pay the costs, and put the spell on the stack. so to reverse that, is it enough to take the card back and untap your lands? in very certain cases, maybe, but not generally. a single triggered ability might crash your concept. there might even be abilities that triggered from untapping lands, because of the undo!&lt;br /&gt;&lt;br /&gt;the second point is the other side of the same medal. if you view a triggered ability not as the consequence of playing a spell, but a different thing, the matter goes from "undoing all consequences" to "not overseeing other actions caused by playing the spell". while it effectively means the same - really going back to that same state - it's good to see the different aspects&lt;br /&gt;&lt;br /&gt;in my opinion, the deeper you look into the game, the easier it is not to forget an action. if you have looked into the comprehensive rules a few times, you can notice how detailed everything is, timestamps, priority and so on. if we don't only look at huge things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;play a card, triggering an ability&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;but at the small ones:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;tap the land for mana&lt;/li&gt;&lt;li&gt;an ability triggers&lt;/li&gt;&lt;li&gt; the ability is put on the stack&lt;/li&gt;&lt;li&gt;you receive priority again&lt;/li&gt;&lt;/ul&gt;all these are very small actions that are easily undone, and multiple actions are grouped into bigger ones, like playing a spell (and everything that happened in between), declaring attackers, a full phase, and turns. This way, you can quickly choose what you want to undo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7899203228296537503?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7899203228296537503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7899203228296537503' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7899203228296537503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7899203228296537503'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/12/undo.html' title='Undo'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3987505374577788552</id><published>2009-12-12T04:29:00.000-08:00</published><updated>2009-12-12T11:22:23.719-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Architecture'/><title type='text'>Program Architecture</title><content type='html'>A program's architecture is very important. Like with a real house, it literally prevents the program from breakng down. If you don't make enough preparations (walls not solid enough), your program might not be able to have the features you want (a billard table). If you want to place a billard table in your house, you better make sure it can bear one.&lt;br /&gt;&lt;br /&gt;A very common architecture in enterprise- and web-applications is the Multitier-Architecture. in this architecture, there are multiple tiers that communicate with each other and add abstractions. a very popular separation of tiers is the following (from bottom to top)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Data tier&lt;br /&gt;This tier is not much more than a database server, responsible for storing and retrieving data.&lt;/li&gt;&lt;li&gt;Logic tier&lt;br /&gt;The logic tier communicates with the data tier and processes the information. For example, it could sum up the prices of a shopping cart.&lt;/li&gt;&lt;li&gt;Presentation tier&lt;br /&gt;This is where the user comes into play. The presentation tier doesn't know the data tier directly, it only calls the logic tier to get structured information. It then transforms that information into something the user might like, like an HTML-Page.&lt;/li&gt;&lt;/ul&gt;Enterprise applications are - don't be offended - pretty dumb in my mind. Most of the time, all you do is retrieving data, apply some calculation, and pass it to the user. there's not the "art" in it that makes programming interesting - complex algorithms and data structures; the sort of thing you get your success moments from.&lt;br /&gt;On the other hand, games are by definition complex. It's what makes the game fun - easy to scope, but deep in possibilities. Look at Settlers of Catan, you know what I mean.&lt;br /&gt;&lt;br /&gt;So, this "primitive" multitier architecture is perfect for business applications, but not for a game like magic. I have, however, some architecture in mind:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_SUHKGhHw1Ys/SyOCpTQoNBI/AAAAAAAAAEY/xVevUJAoyA0/s1600-h/Magica+Architecture.png" imageanchor="1" linkindex="15" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_SUHKGhHw1Ys/SyOCpTQoNBI/AAAAAAAAAEY/xVevUJAoyA0/s640/Magica+Architecture.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;All of these parts are fairly complicated:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The game state includes all of the cards with their abilities, the zones, turns and phases, the players and even a random number generator.&lt;br /&gt;The random number generator should make it possible have two games being absolutely identical, which allows for duplicating a game for network play, saving, replaying and so on. The generator also has to support the "Undo" i described.&lt;/li&gt;&lt;li&gt;The rules engine is the classic of a complicated thing. I don't think it's possible to fully separate this from the game state. For example you control a Glorious Anthemn. Is the +1/+1 bonus part of the game state or of the rules? However, other parts can clearly be separated: A player's starting life and hand size in Vanguard, or alternate winning conditions, and the ability to play the general in Elder Dragon Highlander&lt;/li&gt;&lt;li&gt;The payer abstraction in the next stage is different from the one in the game state. the state's player's purpose is to store his information, like life, cards and so on. In this stage, a player's purpose is to make decisions. The way of making a decision is of course different for the three types of players.&lt;/li&gt;&lt;ul&gt;&lt;li&gt;You yourself want to see the game state in a nice GUI.&lt;/li&gt;&lt;li&gt;The AI "only" needs to access the game state and rate its possibilities&lt;/li&gt;&lt;li&gt;A network player has his own GUI at his side of the connection, but what you see is only the decision he sends to you, which is enough because the game states are identical on both sides.&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;i hope you enjoyed that insight! bye!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3987505374577788552?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3987505374577788552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3987505374577788552' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3987505374577788552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3987505374577788552'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/12/program-architecture.html' title='Program Architecture'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SUHKGhHw1Ys/SyOCpTQoNBI/AAAAAAAAAEY/xVevUJAoyA0/s72-c/Magica+Architecture.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-5107690663286283969</id><published>2009-11-28T16:40:00.000-08:00</published><updated>2009-11-28T16:40:43.550-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Min-Max'/><title type='text'>Creating a state tree</title><content type='html'>I'm back at min-max for a short question: How do you search the possible sequences of moves in a game?&lt;br /&gt;&lt;br /&gt;well, there are two ways: one is, you copy the game state to try different plays. this guarantees you that you don't accidetially manipulate the game, even though you only wanted to test some moves for the computer. it has two downsides: one, you have to take some preparations so that your game state is copyable. magic is a complicated game, and it's easy to forget things. two, it takes up space. while this sounds not too much a problem nowadays, java by default starts with 32MB of memory. I did a test, and that didn't even fit the full game tree of the way easier game of quarto, which I presented earlier.&lt;br /&gt;&lt;br /&gt;the second way is to enable undo. you work with the original game, make moves and undo them after running the evaluation function. memory is not the problem here, but the first one stays. an undo function is very complicated to implement, because magic has so many facettes.&lt;br /&gt;&lt;br /&gt;I think that the second aproach is the better one. While it may take more work, you will want an undo function at some point, and in addition it implicitly allows some cool stuff: given the initial game state (the order of libraries), you can duplicate a game by applying the same actions to that state. this allows for storing a game state as a file, replaying awesome matches, and easily implement gaming over the network by simply transmitting an action.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-5107690663286283969?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/5107690663286283969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=5107690663286283969' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5107690663286283969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/5107690663286283969'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/creating-state-tree.html' title='Creating a state tree'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-1288176386947379152</id><published>2009-11-23T14:36:00.000-08:00</published><updated>2009-11-23T14:36:06.943-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Magic'/><category scheme='http://www.blogger.com/atom/ns#' term='Rules'/><title type='text'>html-Comprehensive Rules</title><content type='html'>To be precise, the title is a little mis-leading, but it is effectively that.&lt;br /&gt;One part of my program is a parser for the comprehensive rules. It converts the text-version that you can download from Wizards into an xml-File, which is formatted as html with xslt. You can view it like any other html page in your browser.&lt;br /&gt;My file has a very convenient features opposed to those directly from wizards: Links. Everywhere there's a cross reference between rules, you have a hyperlink that lets you directly jump to it. My next version should also have images for the mana- and tap symbols.&lt;br /&gt;&lt;br /&gt;You can download the result directly &lt;a href="http://code.google.com/p/laterna-magica/source/browse/trunk/laterna/cr/MagicCompRules091005.xml" linkindex="196"&gt;&lt;/a&gt;here: &lt;a href="http://code.google.com/p/laterna-magica/source/browse/trunk/laterna/cr/MagicCompRules091005.xml" linkindex="197"&gt;xml&lt;/a&gt; and &lt;a href="http://code.google.com/p/laterna-magica/source/browse/trunk/laterna/cr/MagicCompRules.xsl" linkindex="198"&gt;xsl&lt;/a&gt;, both are needed for proper display.&lt;br /&gt;&lt;br /&gt;If you want to run the program yourself, you have to take some preparations (you may just ignore this if you're not into programming):&lt;br /&gt;&lt;br /&gt;laterna and treeProperties (both on the &lt;a href="http://code.google.com/p/laterna-magica/source/browse/#svn/trunk/" linkindex="199"&gt;SVN&lt;/a&gt;) are two distinct projects, you will need both. Additionally, I use &lt;a href="http://www.jdom.org/" linkindex="200"&gt;jdom&lt;/a&gt; for creating the xml. You have to configure laterna's build path to contain the both.&lt;br /&gt;&lt;br /&gt;I hope you enjoy it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-1288176386947379152?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/1288176386947379152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=1288176386947379152' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1288176386947379152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/1288176386947379152'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/html-comprehensive-rules.html' title='html-Comprehensive Rules'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7270561923809647486</id><published>2009-11-20T09:04:00.000-08:00</published><updated>2009-11-20T09:04:56.124-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rules Enforcement'/><title type='text'>Code online!</title><content type='html'>I don't have anything runnable, but I understand that talking about something you can't touch doesn't draw much interest. Well, "touching" over the internet is a vague concept, and code isn't physical, but putting my code online is as close as it can get. you can find it at &lt;a href="http://code.google.com/p/laterna-magica/" linkindex="337"&gt;http://code.google.com/p/laterna-magica/&lt;/a&gt;, and download it via SVN. The code is located in svn/trunk/laterna.&lt;br /&gt;&lt;br /&gt;Well, as i said, it's not a runnable game or something, but you can still see something:&lt;br /&gt;&lt;b&gt;magica.card.utils.LaternaMagica&lt;/b&gt; shows a JTextPane that is filled with a String including mana symbols.&lt;br /&gt;&lt;b&gt;magica.card.impl.&lt;/b&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;b&gt;&lt;span class="sp"&gt;&lt;/span&gt;MagicObjectImpl&lt;/b&gt; features the layer system. It applies some &lt;span style="font-family: inherit; font-size: small;"&gt;effects to a simplified Llanowar Elves (just a 1/1 Elf for G):&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//nothing runs without a game!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Game g = new GameImpl();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;The matcher defines a set of cards to be affected&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Matcher&lt;magicobject&gt; m1 = getCardMatcher(getMatcher(ENCHANTMENT));&lt;/magicobject&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;/* Then, an effect is registered in the game with that matcher.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;* In sum, this represents a static ability saying,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;*&amp;nbsp; "All enchantments are green."&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;* Well, not exactly. It's more like,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;*&amp;nbsp; "All enchantment cards in&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; all zones are green."&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;*/&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;g.getGlobalEffects().getEffects().put(new ColorChangingEffectImpl(g, ADDING, GREEN), m1);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;/*&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;* This creates a Llanowar elves card (currently,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;* &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CardTemplateImpl implements a Green 1/1 Elf)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;*/&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;MagicObject card = new MagicObjectImpl(g, new CardTemplateImpl());&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//Here are some effects - should be obvious&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//The order matters here - the effects get timestamps&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CharacteristicEffect e1 = new PTSwitchingEffectImpl(g);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CharacteristicEffect e2 = new PTChangingEffectImpl(g, 0, 2);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CharacteristicEffect e3 = new TypeChangingEffectImpl(g, ADDING, ARTIFACT);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CharacteristicEffect e4 = new TypeChangingEffectImpl(g, SETTING, ENCHANTMENT);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CharacteristicEffect e5 = new OverridingCharacteristicEffectImpl&lt;manasequence&gt;(g, L3, MANA_COST,&lt;/manasequence&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ManaFactoryImpl.INSTANCE.parseSequence("{R/W}"));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//All these effects are added to the card. &lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;card.getEffects().add(e1);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;card.getEffects().add(e2);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;card.getEffects().add(e3);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;card.getEffects().add(e4);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;card.getEffects().add(e5);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;//now, the result is printed out &lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CardCharacteristics c = card.getCharacteristics().get(0);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;System.out.printf("%s - %s%n", c.getName(), c.getManaCost());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;System.out.println(c.getColorCharacteristic());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;System.out.printf("%s %s - %s%n", c.getSuperTypeCharacteristic(), c.getTypeCharacteristic(),&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c.getSubTypeCharacteristic());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;System.out.printf("%d/%d%n", c.getPower(), c.getToughness());&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;Running this will give you the following output:&lt;/span&gt;&lt;br /&gt;&lt;span class="ifClosed" id="crumb_links"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Llanowar Elves - {R/W}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;+[White, Red, Green]&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;+[] +[Enchantment] - +[]&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;0/0&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;The card's name is Llanowar Elves, and it costs R/W. Red and white come from it's mana cost, and it's green again because it's an enchantment. Because It's not a creature, if doesn't have the "Elf" subtype, and also no P/T&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7270561923809647486?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7270561923809647486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7270561923809647486' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7270561923809647486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7270561923809647486'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/code-online.html' title='Code online!'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-2467045948738060934</id><published>2009-11-12T02:37:00.000-08:00</published><updated>2009-11-12T02:37:02.846-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Magic'/><category scheme='http://www.blogger.com/atom/ns#' term='Rules Enforcement'/><title type='text'>Traps in the rules system</title><content type='html'>I think you can divide the rules in several complexity stages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The "dumb" parts, like damage assignment order. These are things that aren't too hard, but you have to do them. When assigning blockers, you have to store a collection of the blockers anyway, letting the user sort it is not much more work. Another thing is mulligan, this is a check at the beginning of the game, and you just do it or not.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The challenging parts. I consider the layering system of Magic is such thing. The layering system is quite complex: Effects that modify different characteristics are applied after each other, so you have to store the basic characteristics (as printed on the cards) and the effects that modify it. it's not enough to store the results, because of cases like:&lt;br /&gt;some 1/1 creature&lt;br /&gt;attach Bonesplitter (+2/+0) --&amp;gt; 3/1&lt;br /&gt;switch P/T --&amp;gt; 1/3&lt;br /&gt;unattach bonesplitter --&amp;gt; -1/3&lt;br /&gt;well, this is obviously wrong. you have to do a bunch of work (not dumb work, though) to get this right, but it's not undoable&lt;/li&gt;&lt;li&gt;The very hard parts. I think text-changing and replacement effects fall under this category. text changing effects require a very good structure for storing card text, that makes it possible to change any interesting part. well it may be simple to change "Goblin" into "Elf", but think about changes like "Choose one" into "Choose two", that don't only affect what the outcome of the effect is, but actually what happens.&lt;br /&gt;Replacement effects have a similar problem as Triggered ability. Both need an event system that makes it possible to track changes to the game, but replacement effect don't only listen to events, they also negate those events on the fly.&lt;/li&gt;&lt;/ul&gt;obviously, the second category is the most fun to program. i actually have a working layer system, it took me around 3,500 lines of code in about 40 classes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-2467045948738060934?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/2467045948738060934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=2467045948738060934' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2467045948738060934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/2467045948738060934'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/traps-in-rules-system.html' title='Traps in the rules system'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-7806351017118249750</id><published>2009-11-09T14:01:00.000-08:00</published><updated>2009-11-09T14:01:59.775-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Min-Max'/><title type='text'>Performance of min-max</title><content type='html'>The min-max algorithm builds a tree of all the possible moves game states and then determines the optimal play. this sounds very expensive, and it is. to emphasize this look at this (I don't optimize anything like symmetric or rotated board positions, and don't terminate before the board is full):&lt;br /&gt;&lt;br /&gt;tic-tac-toe, from my previous example, has one type of pieces per player and nine fields. the first move can therefore be one of nine possibilities. The second move has only eight fields free, and so on.&lt;br /&gt;the total number of games is therefore 9*8*...*2*1 = 9! = 362880&lt;br /&gt;&lt;br /&gt;Quarto is a "little" more complicated:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;there are sixteen pieces with four characteristics:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;small/tall&lt;/li&gt;&lt;li&gt;light/dark&lt;/li&gt;&lt;li&gt;hollow/massive&lt;/li&gt;&lt;li&gt;square/round&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;there are sixteen fields, arranged in a square&lt;/li&gt;&lt;li&gt;for his move, a player may take any piece and place it anywhere on the board&lt;/li&gt;&lt;li&gt;a player wins if his move creates a line of four pieces that share at least one characteristic. Lines can be in any direction, including diagonally&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;the first player can choose between 16 pieces and 16 positions, creating 16² possible first moves. The second player can choose one of the fifteen pieces and one of the fifteen fields, creating 15² possible second moves for every first move.&lt;br /&gt;the total number of games is therefore (16!)² = 437763136697395052544000000 ~ 437*10²⁴&lt;br /&gt;i had to use a dedicated mathematical program to calculate this, because regular java 64 bit longs weren't enough! &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;you see, creating every possible move is almost impossible for a game like quarto, and Magic doesn't even have a finite game tree. (for example, if both players have necropotence or something, both players can indefinitely long pass the turn). so how can you apply min-max to magic?&lt;br /&gt;by introducing a reward function: instead of looking forward until the end of a game and saying -1, 0 or 1, you evaluate a board state and assign it some value between -1 and 1.&lt;br /&gt;&lt;br /&gt;This is the real art: The reward function will directly influence your AI's performance. The better it evaluates the board, the more accurate the AI will behave.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-7806351017118249750?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/7806351017118249750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=7806351017118249750' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7806351017118249750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/7806351017118249750'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/performance-of-min-max.html' title='Performance of min-max'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-6557918589909604090</id><published>2009-11-09T07:05:00.000-08:00</published><updated>2009-11-09T12:00:50.152-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Min-Max'/><title type='text'>The Min-Max Algorithm</title><content type='html'>I like to say "AI is an interesting topic, as long as you don't have to implement it". I think this is very true, because AI is obviously very interesting, but implementing some "intelligent" algorithm may be anything between annoying, boring, monotonic and challenging, hard.&lt;br /&gt;&lt;br /&gt;The Min-Max algorithm builds on this though: Every player takes the move that is the most beneficial to him, and every game state is measurable somehow. The easiest move to measure is one that ends the game: The winning player has a "reward" of 1 (maximum) and the losing player has a reward of -1 (minimum).&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_SUHKGhHw1Ys/Svgs3R1pvDI/AAAAAAAAAEQ/Y3FMXMY4Vf8/s1600-h/TicTacToe+Tree.png" imageanchor="1" linkindex="15" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_SUHKGhHw1Ys/Svgs3R1pvDI/AAAAAAAAAEQ/Y3FMXMY4Vf8/s640/TicTacToe+Tree.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;You see here the end of a tic tac toe game. Green (and the one yellow) are your (circle's) turns, orange is the opponent.&lt;br /&gt;&lt;br /&gt;First, you start building the decision tree from the initial state to the final states. Then, you determine the outcomes of the final states. Most of them are draws in this example, meaning no reward for anybody. Two of them are losses and are assigned rewards of -1.&lt;br /&gt;&lt;br /&gt;Now, you go back through the tree to the predecessors of the bottommost states. Since you made the move, you maximize your reward. For example, the maximum reward for the yellow node's predecessor is 0, so that node is assigned 0, too.&lt;br /&gt;&lt;br /&gt;The next time, it's your opponent's turn. Stepping up one node again, that node has two predecessors, one with 0 and one with -1. Your opponent minimizes your reward, so the node is assigned the minimum of possible child nodes, -1.&lt;br /&gt;&lt;br /&gt;We finally reached the original turn where you have three choices, one leading to a draw and two to a loss. You maximize your own reward and choose the option that leads to a draw, which was the final decision made by the min-max algorithm. At this time you know that the game will be a draw if the opponent has the same thoughts as you. It's tic tac toe after all!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;that's the basic thought behind min-max, but i guess you see how this is not applicable to Magic. Check back for the details!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-6557918589909604090?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/6557918589909604090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=6557918589909604090' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6557918589909604090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6557918589909604090'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/min-max-algorithm.html' title='The Min-Max Algorithm'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SUHKGhHw1Ys/Svgs3R1pvDI/AAAAAAAAAEQ/Y3FMXMY4Vf8/s72-c/TicTacToe+Tree.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-3215989592600523588</id><published>2009-11-08T06:42:00.000-08:00</published><updated>2009-11-08T12:41:45.372-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Magic'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>What can a Magic Program do?</title><content type='html'>When you do a program, you have to ask yourself what you want from it - at least in Open Source, in commercial projects you tend to as others what they want from it...&lt;br /&gt;my ambitions usually go very high, and it's not very different with Laterna Magica (that's how I named my project). I basically replace the question "What should it do?" with "What can it do?".&lt;br /&gt;I want to list some features that a Magic program can provide:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Rules Enforcement&lt;br /&gt;This is one of the two features that are the most interesting - and most difficult - features.&lt;br /&gt;Rules enforcement means that you have to teach the computer what a player may do. It's hard enough to teach a human to play Magic, and humans are at least intelligent.&lt;br /&gt;Implementing a rules system usually means to describe what the parts (cards, players, but also turns and combat) of the game are, what the possible moves and their outcomes are, and what moves are legal for a game state (the rules).&lt;/li&gt;&lt;li&gt;Human vs Human&lt;br /&gt;Such a game is imaginable without rules enforcement. It's like paper magic, you make sure yourself that no one is cheating. examples for multiplayer Magic programs without rules enforcement are Apprentice and MagicWorkStation. This is relatively easy to implement, because you can skip the very huge rules enforcement part and can focus on a usable user interface and the network aspects. an upside to the user is that you can take shortcuts easily, a downside is that you may accidentally make illegal moves.&lt;/li&gt;&lt;li&gt;Singleplayer Magic &amp;amp; AI&lt;br /&gt;Singleplayer games are harder, because it requires the computer not only to enforce the rules, but also to implement an Artificial Intelligence to make decisions itself, which is likely even harder than rules enforcement itself.&lt;/li&gt;&lt;li&gt;Multiplayer games&lt;br /&gt;Games with more than two players are a challenge for several reasons: you're likely to do it over network, so you have to have a server which maintains game state, or connect every player with each other. Additionally, a user interface that shows three players is hard.&lt;/li&gt;&lt;li&gt;Good User Interface&lt;br /&gt;"Last but not least": having a good interface is very important, because no matter how correct your rules implementation or AI is, in the end it's about playing a game, and that has to be fun after all. Maybe you're interested in &lt;a href="http://www.youtube.com/watch?v=hXVrSMkOT4g" linkindex="16"&gt;This youtube video&lt;/a&gt; about the new MTGO interface. Wizards seems to have taken other Magic programs into account when designing this; some of the features seem similar to Incantus.&lt;br /&gt;I list it last here because it's far away from the other topics: my other four points are "low level" (although ideally the AI is also independent from the rest), and in a perfect world you could build a user interface on top of a program that was developed with no thought about it.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=hXVrSMkOT4g" linkindex="17"&gt;&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-3215989592600523588?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/3215989592600523588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=3215989592600523588' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3215989592600523588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/3215989592600523588'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/what-can-magic-program-do.html' title='What can a Magic Program do?'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7523569453469773247.post-6154730397731561229</id><published>2009-11-07T01:52:00.000-08:00</published><updated>2009-11-07T02:13:56.923-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Magic'/><title type='text'>Welcome in the complicated word of Magic: the Gathering!</title><content type='html'>Magic is a very deep and interesting game. It has several thousand different cards that make every game a unique experience, and hundreds of pages of rules that describe in detail how to manage all those different cards.&lt;br /&gt;&lt;br /&gt;while this sounds intimidating for some people, i like the idea of such a well-defined system, and that's why Magic attracts so many programmers, like me.&lt;br /&gt;&lt;br /&gt;I hope you enjoy my posts, and please leave comments if you feel like it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7523569453469773247-6154730397731561229?l=laterna--magica.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://laterna--magica.blogspot.com/feeds/6154730397731561229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7523569453469773247&amp;postID=6154730397731561229' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6154730397731561229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7523569453469773247/posts/default/6154730397731561229'/><link rel='alternate' type='text/html' href='http://laterna--magica.blogspot.com/2009/11/my-first-post.html' title='Welcome in the complicated word of Magic: the Gathering!'/><author><name>Silly Freak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
