Sunday, May 23, 2021

Physical prototype.

 Some playtesters voiced their concern that physical unit token will be unwieldy to use or impossible to implement. So I designed a two layer tile that is easy to handle even if it holds multiple tokens.



The health markers are flippable 1/2 and 3/6 tokens. This way it is possible to represent 4 health unit with 2 tokens and each point of damage taken with 1 flip or token removal. Units up to 9 health can be represented with 2 tokens. I may need to work on visual representation, perhaps make 3/6 token physically bigger.

Sunday, November 29, 2020

Robotic factory - first round of changes

 Following the first virtual playtest I made some changes.

I swapped some of the cards in starter deck for cards with extra use cost and stats. This should make the choice of starting army more varied.

In hotseat I also tried to salvage the one deck system by providing extra stored research at start and making expensive cards cheaper by adding use costs. I will need to play more with this system.

Hotseat testing showed that the original shield is probably too powerful. I will need to either change the rules or make it more expensive or make distortion weapon cheaper. The power level of ballistic missile also did not feel right.

Feedback after first round of changes felt more positive. One common concern was that division for armour and shields is not fun and also requires keeping too much state in assembly area. Also it was recommended to get rid of any duplicates in starting deck.

Another suggestion was to start with body pre-deployed to mitigate first shuffle randomness. This one felt useful so I am keeping it.

Also game needs a way to keep important cards in hand. I consider a rule to allow keeping 2 cards in hand. You draw two less next turn and gain 1 stored research.

Thursday, November 5, 2020

Robotic factory - first virtual playtest

 I joined virtual playtesting discord group some time ago and last Sunday I finally got my prototype in tabletop simulator to testable state. I had 2 players listen to the original version of rules and play a few turns and got a few more ideas for rule adjustment to try.


One conclusion was the need to speed up the pace of the game. Also it confirmed my suspicion that advanced deck must be split. Suggestion for guaranteed supply of mobility utilities was raised. But perhaps the main lessons was about organizing playtests.

One playtester was obviously eager to explain his choices as he played and followed what I would call the intended strategy. The second player was more silent and played strategy that was intended to be suboptimal.

During the feedback phase the second playtester told me that my attempts to suggest a strategy almost every turn were unwelcome, borderline rude. This was a surprise to me. I was asking "have you considered preparing to invade neutral territory earlier?" as a prompt to share his decision process. He felt it I was coercing people to play the game in less fun way. Lesson for me, if I want playtesters to share decision process in real time I must ask this explicitly. But maybe big part of the reason second playtester was so annoyed is that his idea of ignoring army for a few turns and just teching to build a robot with massive firepower (which he obviously saw as more fun) simply did not work.

Also I must learn to be optimistic. Originally I asked for 150 minutes, than I doubted that my prototype is mature enough to play several turns and changed it to showcase and play one turn with 70 minutes. At the end prototype was mature enough to be played for several turns and lengthy feedback phase so people who initially committed for 70 minutes had to stay for 150.

I also received much useful advice on TTS on editing player hand zones, on using thick cards instead of tokens whenever possible, on manipulating grids and snappoints.

And once again let me thank my playtesters for providing valuable feedback and boosting my confidence.

Saturday, September 12, 2020

Robotic factory

 In previous post I mentioned that most of the cards in Robotic Factory are components used to build robots. Some cards are not. Such utility cards can be used for immediate benefit. Like all other cards after use it goes to discard pile.

Space bender is the transport of Robotic Factory. Having actual transports that carry other units would be too tricky to implement in cardboard so here is your transport.

If you are having problems with fragile yet very long range artillery ballistic missile may help.

Stay tuned for next post where I explain robot construction zone.

Saturday, September 5, 2020

Robotic factory: key deck-building concepts

In my previous post I explained the basics of how deck-building games work and how I arrived to idea of the game I am designing. For now under absence of better name I call it "Robotic Factory".


Disclaimer: the card art on this page, more specifically absence of it, is temporarily and WIP. All icons on cards are taken from https://game-icons.net/ which provides thousands of images under CC-BY 3.0 license.

You and your rival land on mysterious planet and take control of precursor robotic factory. The inner workings of the factory are too complex for your understanding. You understand that it creates components from a list of schematics deep in machine's memory. By disassembling the produce you can learn more about the machine and add new schematics scattered around the planet to machine's memory.

You start with a deck of 10 basic cards. Every turn you draw 5. The two card types are component and consumable. Both types can be scrapped for research points. Components can be used to build robots to fight for you. Consumables provide immediate effect on your deck, enemy deck or battlefield. Both types of card move to discard pile after use. If you have no cards to draw reshuffle discard pile.

You have 2 assembly beds in robotic factory. Each may contain a robot under construction. To start construction place a part of body type on assembly bed. To do so discard the card and adjust the sliders in production area. As long as robot has enough power during your turn you can deploy it in your controlled area (more about it in future post). There are some restrictions, each robot may have at most 2 weapons or one special system.

Here are some examples (click to enlarge the explanation sheet). You can put a basic reactor and basic laser on basic rover consuming 5 space and reactor provides just enough power. However to put another laser you will need to put another reactor. Later in the game you may have advanced reactor providing 5 energy so you can put 2 lasers in rover with just one reactor. Or perhaps get an advanced mech body with integrated reactor.

In future post I will explain how production bed is implemented in cardboard.

As usual your feedback is welcome.





Robotic Factory: deck builder board game

Since I first discovered Dominion (BGG) I am huge fan of deck building games. I am also a fan of tactical wargames like Massive Assault. When I discovered Cogmind I soon became obsessed with the idea of creating a board game that combines elements of deck building and tactical wargames and building robots from parts.

Let me use Dominion to briefly explain how deck building games work. You start with a small deck of basic cards. Every turn you draw a few cards. You can use your cards in different ways. Some cards (like Silver shown here as an example) generate gold that allows you to buy other cards. When you buy a card it goes to your discard pile. The cards you played also go to discard pile. When there are no more cards in your deck and you need to draw you shuffle your discard pile and use it as your draw deck. The key idea is your deck
grows as you play and with each reshufle the cards in your hand become more powerful.

Some cards allow you to attack the opponent, for example inserting harmful cards to his deck. Some cards provide victory points but do not provide any benefit before the end of game and only waste a slot in your hand.

Seriously if you love games and do not know what is a deck-builder make yourself a service and watch some youtube.

There are many games, including digital, that followed suit. Ascension and Star Realms are examples of deck building brought to it's purest form. Slay the Spire has strong deck-building elements. Even some tavern brawl modes of Hearthstone are deck-builders. Digital games provided fancy mechanics that are impossible in cardboard form. But it is Dominion that started it all.

Not much can be said about tactical wargames. You carefully choose your army composition to respond to enemy army and terrain. You carefully maneuver your army to gain territory with minimal loses. There was a post (in Russian only) from developer, who is among founders of wargaming.net, that original prototype of Massive Assault was "analog" board game which gives me hope that my idea of doing this in cardboard is not doomed.

Cogmind is a clever roguelike videogame where you build your robotic hero by placing destructible parts into very limited slots. And you cant just slap all the fancy big guns and fire them at once because usable have weight that slows you down heat generation that requires cooling (and you will melt without it) and require energy .


The idea of building units from parts is not new in board-games. For example Eclipse has starships with components and you can not fire your big gun if energy requirements are not satisfied.

In future blog entries I will show how my game is supposed to work and how I expect to balance it.

Friday, October 25, 2013

Legal system for software developers.

Hello there. You as geeky software developer want to know the philosophy behind legal system? Let me explain it all to you.

To make it easier let's say that menacing Code of Laws is simply "code". Code is logic expressed formally. Suppose certain borrower refuses to return money to the lender. We as society need a set of formal rules that let us resolve the conflict. Just like our software needs code to resolve conflict if certain image is too big for its page. Sure enough there are many variations in each case.

Depending on your location your local laws may represent a codebase with pieces from 500 years ago and layers of patches on top of it. As developer you know how much misery can be brought by 20 years old software that can not be replaced. Here we talk almost 2 orders of magnitude more.

Such code is mission critical contributing to fear of change and especially fear of removal. You'd rather have 30 corner cases than one generalization. You'd rather keep obsolete pieces just in case.
And sure enough there are many cross-references and inter-dependencies all over the code.

The lawmakers are like legacy system developers while practicing lawyers are administrators in the field. The administrators know all the quirks and hacks of the system, are aware of inner workings that could drive simple people mad. Developers hate the current state of the system, have vague memories of the rationale behind part of the system and once hoped to change it for the best. Sure enough some developers are ready to put a backdoor into the system if the pay is right. I believe that while some of the law-makers are lazy or evil most of them are not, at least on the West. They are simply shocked by the pile of legacy rules. Remember the first time you as developer were asked to maintain a module in a 10-million-lines of code project and realized that your module affects 20% of the entire system?

Voters give power to law-makers in hopes for greater human friendliness and greater efficiency of the system. Usually such hopes are not fulfilled. Customers that pay for legacy software upgrades are rarely fully satisfied.

Software administrators are often seen as people who do no real job and get too much easy money, same applies to lawyers. The legal agreements are similar to scripts written by administrators, ugly, seems to work, may have side effects and you can't work and live without them.

Usually no one is guilty. Nor the lawyer, nor your candidate, nor you administrator, nor the programmer. That's how the world works. It's a consequence of incredible complexity of the task system aims to achieve. It takes a genius to improve such a system.

Please leave your thoughts in a comment.

Wednesday, May 29, 2013

C++11: copy and decorate idiom

I love to generalize. Some would say "over-generalize". For example if you ask me: "What is Query Language?" I will answer: "A way to create predicates from textual data at runtime". I know, over-generalizations are bad.

Recently I looked at the GOTW#4 solution. To my shame I did not know why and how operator + should be implemented in terms of operator+=. However (once I understood it) I really like the guideline: "Prefer passing a read-only parameter by value if you’re going to make a copy of the parameter anyway, because it enables move from rvalue arguments." In fact I liked it so much that I call it "Copy and Decorate" idiom. I think it is a nice mnemonic that helps remember this guideline. In my mind it connects well with "copy and swap". You take your argument by value to get a copy of it, you decorate it, you return it. Simple.

Let's look at example often shown to show why C++11 is normally a better language than pure C.
 string compose(const string& name, const string& domain)
 {
  return name+’@’+domain;
 }
This example is a pure win compared to C solution that allocates memory and copies bytes in funny ways. It was used by Prof. Stroustrup himself in a recent interview. It can also be used to illustrate Copy and Decorate idiom:
 string compose(string name, const string& domain)
 {
  name+=’@’;
  name+=domain;
  return name;
 }

Name is copied anyway so the copy with passing it by value. Or by move. But I doubt that symbolic performance gain is worth the damage to readability. But there is more to it. This example can also be used to show the kind of tricks one may use in attempts to improve performance:
 string compose(string name, const string& domain)
 {
  name.reserve(name.size()+domain.size()+1);
  name+=’@’;
  name+=domain;
  return name;
 }

Here reserve is used to hint how much memory should be allocated. Also the function is already almost as long and as confusing as C alternative. The only difference is that in C++ error in estimation of the required size leads to poor performance, in C such miscalculation leads to memory corruption and crashes.
P.S: blogger WYSIWYG editor is horrible at formatting code. WYG is actually not WYS.

Wednesday, July 18, 2012

Comments

Consider the following code:

//Increment data count here
dataCount++;

The developer responsible for this legacy code heard that comments are good. What he was not aware of is DRY (Don't Repeat Yourself) principle. It is obvious what the code does. What is not obvious is why it is incremented here and not at the end of the function. I'd like to know why it is incremented unconditionally. As maintenance developer I'd like to see variable name that clarifies what kind of data it counts. Unfortunately there is no comment to help me.

Sunday, July 15, 2012

Phunction names.

Somewhere in code there was a function named "abtain_list_lock", That's right, with "a". A few years ago a rather large group of developers branched out from main version to work on a another set of special awesome features. The developers working on side branch quickly renamed the function to "obtain_list_lock" and continued working. Over the next three years much work done; lots of new functions that obtain list locks were written. It was time for a merge between branches and resulting version included awesome new features from both branches. It also included both "abtain_list_lock" and "obtain_list_lock" having the same function body. One day this will be fixed, but not today.

Why?
1. Developers do not read function and variable names carefully, IDE allows code navigation without reading function names.
2. Developers on side branch expected that the typo on main branch will be fixed by the next person who uses that function, extrapolating own quality standards on others. It also highlights some bureaucracy in branch ownership and commit policies in larger software corporations.
3. C language allows implicit function declarations. Many users of "abtain" did not include the relevant header file where function is declared. Thus removal of function with typo led to errors while linking, not during compilation. Due to complicated custom build system such errors did not show verbose error messages by default. A simple preprocessor macro in header file wouldn't be enough to make it link.
4. The merge was done in haste. People were under pressure to release product that contains all features from both branches as soon as possible.

Wednesday, June 13, 2012

Software maintenance and selection bias

As maintenance developer you often look at old code. And old code is ugly. If you think that all old code is ugly you are wrong. Good old code simply does not need your attention. Don't let selection bias influence your overall opinion on company code..

Wednesday, March 14, 2012

Rotational matrix as an example of Data Driven Design.

Every self respecting linear algebra teach causes students to hate matrices. If you can't you are not qualified. Only now, after three years without formal study of matrices, I learned to appreciate them.

I made a small computer graphics project at school. I had a separate function for X axis rotation, separate function for Y axis rotation, Z rotation and XYZ scaling were separate function, so was XYZ translation. My teacher told me to use matrices but I simply could not understand why it is better. I mean, matrices are creepy and functions are simple. Rotation around arbitrary axis was a bit cheaty (and buggy); this is fine for a small project though.

Now I understand the beauty of matrices. Now I see what is wrong with transformations stored as list of function objects. 8 years ago I was too young to understand the value of simple matrix that replaces arbitrarily long list of functions with parameters.

My friend recently showed me a piece of code from his hobby project. The code was full of conditional and switch cases. After little thought we realized that confusing branching in code can be replaced with a single matrix. It was the most satisfying change I ever made in code.

Saturday, February 25, 2012

The value of well placed goto.

Some legacy programming languages do not support nor destructors nor exceptions. You must carefully check the returned error codes in case of error you must carefully free all resources. Often this leads to code like this:
error_code =some_func(some_params);
if (error_code!=NOERROR) {
  goto cleanup;
}
error_code =other_func(some_params);
if (error_code!=NOERROR) {
  goto cleanup;
}
error_code =yetanother_func(some_params);
if (error_code!=NOERROR) {
  goto cleanup;
}
//... More code here


There is much code like this. At some point a well-intentioned developer who heard that goto is evil changed the code to look like this:
do {
  error_code =some_func(some_params);
  if (error_code!=NOERROR) {
    break;
  }
  error_code =other_func(some_params);
  if (error_code!=NOERROR) {
    break;
  }
  error_code =yetanother_func(some_params);
  if (error_code!=NOERROR) {
    break;
  }
  //... More code here
} while (0);


In my opinion the do-break-while(0) is much uglier and harder to read than goto.
1. Here break is just a goto in disguise.
2. If you see the beginning but not the end you think there is a loop here.
3. In original version goto is used to control the flow in exceptional cases. Goto does not change the structure of the code under non-exceptional scenario.

What do you think?

Saturday, February 18, 2012

More about globals.

In addition to possibility of an API that silently relies on global variables there are more potential pitfalls.

Suppose that you have a global variable which is modified within ten different if-else blocks. You wrote two tests - in one all ifs are true, in another all ifs are false. Now you believe that you have 100% code coverage.

The real coverage, however is not so good. 10 ifs that manipulate the same globals are semantically similar to a huge switch-case statement with 2^10=1024 possible cases. It will take 1024 tests to give true full coverage.

Tuesday, February 14, 2012

assert(g_everything->isEvil())

I'm in development for 3 months now. I must learn I understood something important. Something that I learned long ago but understood only now. Global vars are evil.

I saw many books about it, but I did not understand. A well placed global variable is very convenient and there is no harm in it. This is what I though when the largest project I experienced was around 2000 lines of code.

If you think what I thought 3 months ago you will hopefully learn from this blog post.

Look at this function signature:
error_code_t loadItemFromDefaultDir(const std::string filename);

As you can see function loads item and returns error code. Where is the item?

if(loadItemFromDefaultDir("it42.item")!=NO_ERR) {
throw load_exception;
}
sub_item_t* mySubItem = g_everything->repository->collections->getSub("it42", MY_SUB_CODE);


You may think there is no problem here, and you are partially right. The problem begins when the item is loaded in one place but the function that uses it is in different module. And the loader does not check the error code. And the item name is not constant, but dynamically created string... You got the idea.

Sometimes you write code like this and it does not work for your argument, but works for any other argument.
convertAndNormilize(&myConvertibleString);

You spend much time looking at the code and finally veteran developer feels pity on you and explains that the string you pass is defined in g_everything->exceptions->conversion_exempt_strings and you must first call
g_everything->exceptions->override_convert_exc_when_norm(myConvertibleString)

Monday, February 6, 2012

C++11 factory

Today I was playing with awesome new features of C++11. In the examples below B is base class and D is derived.
The following code works fine:
std::function<std::shared_ptr<B>()> factory;
factory = [](){return std::make_shared<D>();};

The following code doesn't work:
auto factory = std::make_shared<B>;
factory = std::make_shared<D>;

The first code works because polymorphic wrappers for function objects are smart; the second does not work because function pointers are not smart at all. You can not cast from function that returns derived shared pointer to function that returns base shared pointer, you need function wrappers for it.
I tried to implement parametrized factory method. VC10 does not support initializer so the following did not work yet:
std::map<std::string, std::function<std::shared_ptr<B>()>> factory { 
    std::make_pair("D", [](){return std::make_shared<D>();}),
    std::make_pair("D1", [](){return std::make_shared<D1>();})
};

I'll test this trick with gcc4.6 tomorrow. In VC10 you can initialize the map by hand:
std::map<std::string, std::function<std::shared_ptr<B>()>> factory;
factory.emplace(std::make_pair("D", [](){return std::make_shared<D>();}));
factory.emplace(std::make_pair("D1", [](){return std::make_shared<D1>();}));

Please note the use of emplace method.
You can use factory like this:
auto Bptr = factory["D"]();
//Do something with D object...
Bptr = factory["D1"]();

Unfortunately std::make_unique is not a part of new standard, you can copy the code for make_unique from Sutter's blog. Factories returning unique_ptr are considered better.

Monday, December 29, 2008

Potential girlfriend match test.

After seeing so many GF tests I decided to make my own.

As she rides home from university alone in the bus observe her behavior. If she endlessly chats on cell phone it means she is ready to throw money away, she does not care about it, she is unable to count her own money. She is not serious and she is a bad match.

Limitations: Impossible if she does not use mass transit.

Disclaimer: it is a joke, do not take it seriously.

Friday, December 26, 2008

Is there any wealth created by clicking ads?

Recently many economics bloggers, some of them have Ph.D, blamed new financial instruments for the crisis. Key claim of many of them is that fancy financial instruments do not create zero real economic value are not connected to labor productivity. Something that gives one money without useful work done is potentially evil. I'm not going to argue with them, I will explain how intangible banner clicks generate wealth.

Billy and Jimmy create state-of-the-art decorative fences from beer cans. Give them soldering iron and a ton of recyclable aluminum and they will create real masterpieces. They are very proud dudes, so proud that they refuse to work with Coca-Cola cans, only beer cans, please, Czech bear preferred. Also they are not very successful at selling their product, so they only work 3 hours a day.

Alice browsed a web site and saw a Google ad (under Firefox with AdBlock extension she wouldn't see it). She clicked the banner to read about decorative fences. After reading the relevant information she decided to order some for her garden. For the next three week Billy and Jimmy work full time and overtime thus reducing unemployment and creating real product to be consumed.

This way a single click that increased employment, GDP, consumption and everything else.

But wait! Alice could spend the money differently, she could spend it on aesthetic wallpapers from synthetic waffles that would require even more work-hours without wasting precious aluminum. She could donate it to kids starving from cancer. She could save the money for later use.

One more thing to note. Bob and Rob also clicked the link, but they did not need anything that Jimmy and Billy could offer. Their clicks that cost real money to Jimmy without creating actual wealth, they are potentially evil.

Back to our topic. Was there any wealth created by clicks? We will never know. Economics is about making theories based on wrong assumptions. Assumptions that are known to be wrong from the beginning. You can define wealth in one way and discover five alternative ways and no definition is better, in general case, than the other.

Wednesday, June 4, 2008

Why IDC is wrong and what should BSA do about it.

In IDC/BSA piracy report you see the size of pirated software market measured in billions of dollars and percentage of pirated units. It is wrong. Not only the number is wrong, their so-called researchers measure it in wrong units.

Pirates measure themselves in gigabytes. Bob will never say "I downloaded 7 movies that would cost 269$ if bought legally". In fact his friends will hear "I downloaded 15GB of videos and it all was crap". Pirates who sell disks in Moscow or Beijing do not charge per title or as percent of the price of legal copy, their customers pay per disk. Show me a BitTorrent tracker or DirectConnect server that measures users' activity in stolen dollars and number of products uploaded.

IDC/BSA should measure piracy in terabytes. They will surely learn something important if they do. I do not know what exactly and how, but using appropriate measures and terminology is always a good thing to do.

Surely you can say that Americans constitute 10% of all human mass on Earth, but such statistic fails to shows the population of USA and the obesity rate.

Saturday, March 22, 2008

Update your web site!

My friend invited me and 15 more friends to a restaurant in Tel Aviv. As we arrived we failed to locate the restaurant at the expected location. Our "leader" called them earlier at the same day to ask about their ability to accept 20 visitors at evening and the answer was positive. A quick investigation showed, that they moved long without updating their address on the web-site. This way small businesses lose customers by not updating their site.


P.S: (December 26, 2008) After several months of inactivity this post reminds me that I should do something with this blog.