Friday, September 15, 2023

CEO group think

 It's like these CEOs all know each other and share the same culture of stupidity and shamelessness.

The Unity video game engine has decreed that come January 1st, all games made with their engine will have a new contract enforced retroactively, and that the developers will owe Unity a fee for every download (ever) of any game made with their engine.

The current CEO of Unity is the former CEO of Electronic Arts, who was fired for running EA into the dirt at the time.  He's a graduate of Berzerkeley school of business, and apparently has a long history of being promoted from failure to failure.

As George Carlin said, "It's a big club, and you ain't in it."

Friday, September 1, 2023

Simple D&D melee stunts

House rule for simple melee combat stunts in a D20 system. After making the attack roll:

Succeed: Disadvantaged damage and stunt, or normal damage.
Succeed by 5: Normal damage and stunt, or advantaged damage. Succeed by 10 (crit): Advantaged damage and stunt, or doubled damage.
Fail: No effect. Fail by 5: Take disadvantaged damage from opponent. Fail by 10 (fumble): Take normal damage from opponent. Bosses (and PCs) could deal disadvantaged damage and a stunt instead.

Advantaged roll: Roll two dice, take the better. Disadvantaged roll: Roll two dice, take the worse.


Bonus idea: armor damage reduction

If you want armor to reduce damage instead of (or in addition to) making the target harder to hit, do not use a static number for the armor. Roll for it, just like for the weapon damage. Shields add an extra armor roll. (Shields should be more useful in games than they usually are.)

Option 1 (math, less deadly): Subtract the armor roll from the weapon roll. If the armor equals or exceeds the weapon, no damage is taken.

Option 2 (no math, more deadly): Compare the rolls. If the weapon roll is higher, the target takes the damage.

If you really want to get crunchy with this, you could add skill dice to either or both sides of the combat. Add terrain and range dice for defending against a ranged attack. Use your imagination. You could even use this system in place of making separate attack and damage rolls. (h/t: Into The Odd)

Sunday, August 27, 2023

Why Listack? A personal journey.

Why Listack?  By that, I mean, why did I go to the trouble of creating the language at all?

Easy answer - I'm a language geek.  I like languages, human and programming.  I collect programming language books, and read them for fun.

Harder answer - I'm bored.  This is something that is challenging me, and making me learn new things.  Mostly by trial and error, partly through reading books (Crafting Interpreters by Nystrom is great!) and watching videos.

I started learning Python a couple years ago to get back into programming.  I started programming when I was 12 and received a Commodore Vic-20 for Christmas.  I eventually saved up my pennies and bought a Commodore 64 on layaway.  a decade later (early 1990's), I went to college for "computer science", and was... disappointed with the education provided.  I worked for the university to make money (a bit above minimum wage), and ended up being one of the two student managers for the computer labs.  We supervised around 40 lab consultants, and reported to a full time staffer.  I dropped out of college (the second time, mind you) to join the Army (again).  I just lost interest at the beginning of my senior year.  Especially after I noticed that I was helping the grad students with their thesis projects.  Especially especially after finding out that the local programming jobs (mostly at Compuserve) paid a dollar more an hour than working at McDonalds.  So I quit, went to work for McDonalds for a year an a half, and finally got back into the Army.  (They don't like taking in retreads.  A retread is worth 1 point, where a new recruit is worth 8 to the recruiter.)

History - I bought a book on the Forth programming language from a library sale when I was a teenager for a quarter.  I've been interested in its simplicity ever since.  I read the book about the same time I got my first real calculator - an HP 11C, which I still have and use.  The old HP calculators work in Reverse Polish Notation:  instead of 2 + 3, you enter 2 3 +.  Just like Forth.  RPN = postfix notation = stack based language.

Despite its power and simplicity, Forth is what is called a "Write only" language.  It's easy to write programs in it.  It's terrible to go back to and debug later.

While I was playing around with the esoteric languages (thanks, Truttle1), I ran across False, a minimalist stack-based language.  I implemented it, and saw the power of the concept of quoted programs.  I then found Factor.  It advertised itself as  "... a concatenative, stack-based programming language with high-level features ..."  From there I found Joy, the concatenative (AKA stack based, AKA postfix) language that drew the most academic attention, because it was created (and advertised) by a full Professor who had the mathematical logic to justify it.  (Do check out his papers.  They're quite readable, which is unusual for functional languages and combinatory theory.)

The power of functional programming laid bare in its most essential, simplified, true form is astonishing.  But it's not easy to read, unless this like reading enjoy you.

And then it occurred to me:  words (functions) are just tokens in a list.  Lists can be rearranged!  All I needed was a coherent naming convention and a means to tell whether and how far to move the tokens.  Words can be rearranged, but get executed serially, first come, first served.  When Listack encounters a word, it checks to "fix", and moves the word around if necessary.  (Words are executed serially, one after the other, with no priorities other than 'first come, first served'.)  And thus, slowly, Listack is taking shape.

How is Listack different from the horde of other stack based languages? 
Universal Function Call Syntax.
The four functional word forms:

Postfix:                                          .word            after all arguments
Infix:                                               word            after first argument
Prefix:                                             word:           before all arguments
Callable:                                         word()          before parens around arguments
Immediate:                                     word             after arguments

You can use any of these with most function words.  They won't have much effect on Immediate words, of course, because Immediate and Postfix are how everything actually works.  But you can type in your program in the style you find most suitable.  They all work, with just a little effort on the programmer's part.

These are all the same program:

+: 1 2 if: {=: 3} {println: "True!"}{println: "False"}
1 + 2 = 3 if {"True" println} else {"False" println}        # else is semantic sugar
1 2 .+ 3 .= {"True".println} {"False".println} .if            # This is how it actually executes.
+(1, 2) if(=(3) then {println("True")} else {println("False")})    # then is also semantic sugar

But it could also be written as:

if: (1 + 2 = 3) then {println("True")} else {"False".println}

The point is, you, the programmer, get to decide how you want your program to look.  They all work the same, because words are just tokens in a list that are executed serially.


Monday, July 31, 2023

How should we train men to be military officers?

    The service academies are now a national joke.  They teach very little aside from arrogance and woke Leftism.  They should teach future military officers to be leaders and thinkers.  How could we better go about this task?  What value does a 4 year degree, in this age of reduced (or eliminated) standards, bring to military leadership?  Remember, the ultimate purpose of officers is to lead men into battle and win wars.

    I like Heinlein's idea.  No man shall be eligible for officer training until after he has honorably served no less than two years nor more than ten as an enlisted man.  This weeds out a lot of undesirable candidates.  If you are unwilling to serve amongst the lowest of the low, to put up with the dirty end of the stick, you aren't what we need or want to lead troops into battle.  We also only want patriots.  Anti-American types need not apply.

    After two years enlisted duty, a young man may apply to officer candidate school.  I envision this school as lasting at least six months, at most two years.  The topics taught shall be leadership, psychology, advanced first aid and field hygiene, military history, geography (both physical and human), effective communications (oral and written), basic accounting, technology (heavy on the practical, light on the theoretical), tactics, logistics, military operations, and basic staff work.  Practical courses shall include rigorous physical training (daily), weapons handling (daily), marksmanship (monthly), a martial arts discipline (weekly), close combat (weekly, using airsoft guns), drone use (weekly), wargaming (weekly, starting with miniatures, eventually moving on to board games, and finally computerized staff games).  One week per month shall be spent training in the field or onboard ship.

    Graduation is to be conditional upon successful completion of all courses and activities, honorable and moral conduct throughout, as well as acceptable conduct during a six week final exercise in the field or onboard ship.  Upon graduation, these young men will be commissioned as junior officers, with a service obligation of six years.

    What about doctors and nurses?  They do not lead troops into combat, so they don't get to be officers.  Doctors could be Warrant officers, but honestly, most of them should just be DoD civilians under long term contracts.

    Those are my thoughts on the topic.  What are yours?

Friday, July 28, 2023

FRPG alternate rules

 Abstract wealth:

Wealth is measured in abstract coinage. If you want to buy something, you compare its price with your coinage. If the price equals your coinage, you buy it and lose a coin. If the price is one less, you buy it and lose a coin one third of the time. If the price is two or more coins less, you just buy it. You can't afford anything that costs more coins than you have.

Adding treasure works similarly. Anything worth less than one coin than you have doesn't add to your wealth. One coin less will add to your coinage one chance in six. Treasure equal to your coinage increases it half the time.  Treasure one coin higher than your current wealth is increased one chance in six.

When using slot based encumbrance, your coinage occupies 1/2/3/5/10 slots. When using pounds, your coinage weighs 1/3/10/30/100 pounds. More than that and you need a wagon or magical container.


Alternate spellcasting: 
Slots and checks in a d20 system.
(Spells have tiers instead of levels to avoid confusion.)
Use the spells per day charts. Casting those spells doesn't require a check, but does use the slots. Daily renewal of spell slots requires a minimum of 6 hours of sleep plus one hour of spellbook study or prayer.
To cast additional spells, the caster makes a spellcasting check at
DC 10 + twice the spell tier. Their casting bonus is their spellcasting level plus stat bonus.
If a caster fails a spellcasting roll by 5 or more, they cannot attempt to cast that spell again until after a full turn of uninterrupted spellbook study or prayer. If they fail by 10 or more, they can't attempt spellcasting checks of spells of that tier or higher until after an uninterrupted hour of spellbook study or prayer.
If a caster makes a roll by 10 or more, something good happens. Examples: Spell is upcast, damage/healing is rolled with advantage/maximized/doubled, spells affects triple the usual targets, spell duration is tripled, area of effect is doubled, allies/enemies in AOE are ignored, caster gets to choose instead of rolling on a random chart, caster regains a used spell slot of that tier or higher, caster heals the spell's tier in hit points...
Cantrips are tier 0 spells. Cantrip checks are made with advantage once the caster gains a tier 2 spell slot. Read Magic is a cantrip.
Caster can attempt to cast a known spell of one tier higher than their chart says they can, but the check is made with disadvantage. Failure by 5 results in the loss of half the spell's tier in hit points.  Failure by 10 results in the loss of the spell's tier in hit points.  These are in addition to the usual penalties.
To upcast a spell (when allowed), either use a higher tier slot, two equal slots, a slot and two of the next lower slots, a slot and a check, or disadvantage on the check.
Turn Undead is a cleric spell-like ability, where the DC is 10 + twice the target's hit dice. For every point the check exceeds the DC, one additional creature (of that DC or lower) is turned. Succeeding by 10 destroys the target, with an additional creature destroyed for every point higher. Failing by 5 or more results in the cleric not being able to attempt to turn that creature again until after an uninterrupted turn of prayer. Failing by 10 results in the cleric not being able to turn any creatures of that power or higher until after an uninterrupted hour of prayer.

Wednesday, July 26, 2023

Referencing and Dereferencing in Nim



Here is an executable guide to how referencing and dereferencing work in Nim. I made this to understand it myself, because there is no explanation in the online manual or either book on Nim.

******************


type
refInt = ref int
var
a: int = 1
b: refInt
c: refInt
d: refInt


# UPDATE
# Use this template to make referencing much easier!

template `-->` [T](receiver: ref T, target: T)=
receiver = cast[ref type(target)](target.addr)

echo a
echo "a ", a.addr.repr
# Sadly, the Nim 2.0.0 update removed the physical addresses from repr
new(b)
b[] = a
echo b[]
echo "b ", b.addr.repr
a = 2
echo a
echo "a ", a.addr.repr
echo b[]
b = cast[ref int](alloc0(sizeOf(a))) # primitive, use new(b) instead
b[] = a
echo b[]
echo "b ", b.addr.repr
a = 3
echo a
echo b[]
b = cast[ref int](a.addr) # this works!
echo b[]
new(c)
c = b
echo "c ", c[]
a = 4
echo "a ", a
echo b[]
echo c[]
echo type(c)
echo "a ", a.addr.repr
echo "b ", b.addr.repr
echo "c ", c.addr.repr
echo ""

var x: array[5, int]
type ArrRef = ref array[5, int]
var y, z : ArrRef
x = [1, 2, 3, 4, 5]
y = cast[ArrRef](x.addr)
# addr provides a ptr type
# cast to convert ptr to ref
z = y
echo "x ", x, " is ", x.addr.repr
# repr is a lower-level version of the $ to-string operator
echo "y ", y[], " is ", y.addr.repr
# [] is dereference, so c[] --> b
echo "z ", z[], " is ", z.addr.repr
echo ""
z[4] = 10
echo "x ", x, " is ", x.addr.repr
echo "y ", y[], " is ", y.addr.repr
echo "z ", z[], " is ", z.addr.repr
new(z)
# use new() to allocate space for the value to be referenced
z[] = x
z[4] = 20
echo ""
echo "x ", x, " is ", x.addr.repr
echo "y ", y[], " is ", y.addr.repr
echo "z ", z[], " is ", z.addr.repr
# only z is now [1, 2, 3, 4, 20]

var q: ArrRef
q --> x