Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Please post your after action reports on your battles and campaigns here.

Moderator: Campaign Series Matrix Edition Development Group

User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)


The main purposes of this AAR is to dive deep into the CSVN CSEE (Campaign Series Event Engine); Lua scripting; the SAI (Scripted AI) and the EAI (Engine AI), and how they work in tandem (with an assist by the AAI (Adaptive AI)).

(Also to showcase the game's many features; to explain some of the finer points of game play; to post some pretty screenshots; and to have some fun. It all goes without saying?)

(Additional commentary (and discussion) of a more general or technical nature will be posted here: https://www.matrixgames.com/forums/view ... 4#p4996754.)

For this AAR, I have decided to play, and to Lua script, the scenario VN_550921_Rung_Sat. This is the follow-up to the VN_550429_Saigon scenario. Together, these two scenarios cover the early years of South Vietnam's Civil War period, when the political situation in the south was sorting out after the French had lost the First Indochina War, and before the Viet Cong (assisted by the North Vietnamese) began vying with the Government of South Vietnam for military and political supremacy.



VN_550429_Saigon Scenario Information

29 April, 1955
[Saigon, South Vietnam]: [SIDE A / H2H] [HIS] [CSL]:

Prime Minister Ng Dinh Diem issued an ultimatum for the private army of the Binh Xuen organized crime syndicate to surrender their arms and come under state control in early 1955. The Binh Xuyen was a private army within the Vietnamese National Army, as decreed by Emperor Bao Dai and was funded with revenues from legally running brothels and casinos.

The group consisted of five battalions and armed with similar weapons as the VNA. In reaction to Diem's call, elements of the Binh Xuyen attacked the VNA Headquarters in Saigon, beginning a series of engagements throughout the city.

As the VNA funneled more forces into the city, house to house fighting became prevalent. The Binh Xuyen was pushed into the Cholon District, where they had their fortified headquarters established.

Eventually the VNA was forced to use heavier artillery, destroying much of the Cholon District, causing hundreds of deaths and displacing thousands.

Seeing their position as being untenable, the Binh Xuyen escaped south into the Rung Sat Swamp.

[NOTE: The Binh Xuyen Forces are portrayed with US armed Viet Cong platoons]



VN_550921_Rung_Sat Scenario Information

After the fighting in Saigon, the Binh Xuen had fled to the Rung Sat Swamp, southeast of the city. While the Government forces were occupied in the Mekong Delta, dealing with Hoa Hao, the Binh Xuen continues to harass shipping and attack government outposts in the area.

As this impeded shipping movements along the Saigon River the government was forced to take military action, under the name Operation HOANG DIEU.

The objectives of the operation were to destroy the remaining forces of the Binh Xuen, including their bases and supplies, and to clear the Saigon River and restore normal commercial waterway traffic.

Composing of an assortment of Infantry, Airborne and Marines in Naval Assault Groups, the Rung Sat area was surrounded and systematically combed through to hunt down the remaining Binh Xuen forces. Airborne forces landed on the northern end of swamp to secure the area for the supporting artillery assets that would assist the operation.

Notorious for pirate activities, the mangrove forest became known as the "Forest of Assassins"

[NOTE: The Binh Xuyen Forces are portrayed with US armed Viet Cong platoons]



Why this scenario? (The Rung Sat one.)

  • It is big, rich with possibilities. The map is huge, the forces considerable.
  • It is varied. Ground combat, of course, but also paratroop drops, riverine assaults, airstrikes, etc.
  • The Side A VNA (Vietnamese National Army) and Side B BX Army -- both sides are scripted.
  • Although well developed, the VN_550921_Rung_Sat.lua file could stand some improvement, and is not quite finished. I Lua scripted Rung Sat a couple of years ago, before many CSEE functions and coding/debugging techniques were available or fully developed. At the time, the new airstrike system was not implemented at all. Even though aircraft were later included in the scenario, their use was not scripted. (A human player can make good use of them, of course.) As of now, the scenario plays pretty well (especially as human played Side A), but game play could be better.

In this AAR, I [the human] will not be playing either side. The AI will play itself!

Scripting this scenario, watching it play out, with plenty of backtracking and replays and revisions and second thoughts and third and fourth ... tries -- this should be fun!

Or it might end in disaster. Let's find out!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

A 2D screenshot of most of the battlefield (with a narrow bottom slice not displayed), showing the VNA force disposition (more units will arrive as reinforcements later):

CSVN_AAR_AI_AI_RungSat1.jpg
CSVN_AAR_AI_AI_RungSat1.jpg (2.03 MiB) Viewed 2751 times

Somewhere in that vastness (everywhere?), the BX Army is hiding away, licking its wounds.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Spoiler Alert! The disposition of the BX Army:

CSVN_AAR_AI_AI_RungSat7.jpg
CSVN_AAR_AI_AI_RungSat7.jpg (1.93 MiB) Viewed 2750 times
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

A 3D screenshot of the map center:

CSVN_AAR_AI_AI_RungSat2.jpg
CSVN_AAR_AI_AI_RungSat2.jpg (2.51 MiB) Viewed 2748 times

Such complex terrain!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

How complex? Toggling ON Map Hints will give you a better idea:

CSVN_AAR_AI_AI_RungSat3.jpg
CSVN_AAR_AI_AI_RungSat3.jpg (2.38 MiB) Viewed 2747 times

The Jump Map, showing the extent of that last screenshot:

CSVN_AAR_AI_AI_RungSat4.jpg
CSVN_AAR_AI_AI_RungSat4.jpg (635.13 KiB) Viewed 2747 times

All of those rivers and swamps make scripting this scenario hugely challenging!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

The Side A VNA Scenario Briefing

CSVN_AAR_AI_AI_RungSat5.jpg
CSVN_AAR_AI_AI_RungSat5.jpg (199.9 KiB) Viewed 2745 times

The Side B BX Army Scenario Briefing

CSVN_AAR_AI_AI_RungSat6.jpg
CSVN_AAR_AI_AI_RungSat6.jpg (173.02 KiB) Viewed 2745 times

CSEE/SAI nitty gritty to follow...
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

First, be sure to read this intro to the CSEE:

https://www.matrixgames.com/forums/view ... 7#p4996927

Down to particulars.

The VN_550921_Rung_Sat.lua file begins with:


Code: Select all

-- VN_550921_Rung_Sat.lua

-- Author: Jason Petho
-- Scripter: Robert ("Berto") Osterlund


The name of the scenario Lua file, the scenario Author, and the CSEE scripter of that scenario (who may or may not be same as the Author).

Those are examples of Lua comments.

See a more detailed discussion of Lua comments here:

https://www.matrixgames.com/forums/view ... 4#p4996934

Next, our first function definition:


Code: Select all

function on_startup () -- DO NOT REMOVE

    -- set/unset optional rules (effective in on_startup() only!)
    -- for example:
    -- set_option(OPTHISTORICALLZS)
    -- unset_option(OPTFACING)
    -- to specify every optional rule:
    -- set_options(OPTFIREMAP|OPTEXTFOW|OPTCOMMANDCTRL|OPTVISIBILITY)

    -- set/unset move tracking globally (maybe unset if performance lags)
    -- set_move_tracking()
    -- unset_move_tracking()

    -- for deep debugging:
    --traceon()

    init_constants ()
    init_variables ()

    set_org_lists()

end


In Lua, every function definition must begin with keyword 'function' and end with the keyword 'end'. Duh? But you would be surprised how, in the frenzy of coding, you sometimes mistakenly omit the 'end' or otherwise mangle your code with too many or too few 'end' statements. If you do that, you have created a bug, and the program won't run!

on_startup() is a so-called "trigger function". In the CSEE, all trigger functions begin with the name 'on_'. Something has happened in the game engine: a new scenario has started up. This event triggers a call to the event engine. The CSEE springs to action, in this case doing the things described in the on_startup() function definition.

After some useful comments, we have


Code: Select all

    init_constants ()
    init_variables ()


which set some program constants, then initialize some program variables.

init_constants() is our first "function call" (invocation). At this point, the code will take a detour to the init_constants() function, return, then detour to the init_variables() function, then return again.

Omitting some of the detail (look in VN_550921_Rung_Sat.lua, see it all for yourself), the init_constants() function definition:


Code: Select all

function init_constants ()

    -- initialize names and labels unvarying through the course of the scenario
    -- also define here lists with "holes" (index gaps) (such lists are not saved)
    -- called in on_startup(), and potentially again (and again) in any subsequent on_resume()

    -- Side "a" and "b" values with descriptive names
    SIDE_A = "a" -- _NATION ## (in user.lua)
    ARVN_SIDE = SIDE_A
    SIDE_B = "b" -- _NATION ## (in user.lua)
    BX_SIDE = SIDE_B

    OBJECTIVES = {}
    OBJECTIVES[1] = "27,38" -- 1-40[2/2] 11
    OBJECTIVES[2] = "34,33" -- 1-40[2/2] 11
    OBJECTIVES[3] = "42,55" -- 1-40[2/2] 21
    OBJECTIVES[4] = "43,35" -- 1-40[2/2] 21

    ...

    LANDING_ZONES = {}
    LANDING_ZONES[1] = "62,13"
    LANDING_ZONES[2] = "63,14"
    LANDING_ZONES[3] = "64,14"
    LANDING_ZONES[4] = "65,15"

    ...

    -- others ...

end


The ARVN_SIDE and BX_SIDE are arbitrary; csmklua.pl (use it!) does not auto-generate those. It is a common mistake forgetting to set these SIDE variables properly, or at all.

csmklua.pl auto-generates creation of the OBJECTIVES AND LANDING_ZONES lists.

In the Lua CSEE, "lists" are denoted by an opening '{' (left curly brace) and ending '}' (right curly brace). In between is a list of values, with each list member separated one from another by a comma. List members can be numbers, character strings, variable or constant names -- all manner of things.

OBJECTIVES[1] signifies the first member of the list, OBJECTIVES[2] the second list member, OBJECTIVES[3] the third, and so on.

After the auto-generated lists, you are free to define your own.

The init_variables() function definition:


Code: Select all

function init_variables ()

    -- initialize values possibly varying through the course of the scenario
    -- called once only, in on_startup()

    -- embark/disembark points
    -- for example:
    -- _D303_VC_MAIN_FORCE_INF_BTN_2ND_RIFLE_COY_EMBARK_PT = random_index(3,5)

    -- target points
    -- for example:
    -- _MACV_GUNSHIPS_TARGET_PT = random_pick({1,2,3,4,5,6,7,8,12})

    -- others ...
    -- for example:
    -- NUMBER_VC_ATTACKS = 0
    -- _2ND_ARVN_MARINE_BTN_LEADERLESS = false
    -- NUMBER_US_AIRSTRIKES = air_support("a")
    -- BATTLE_PLAN_HMONG = dieroll(2)

    -- logistic defense assignments

    _C1_1ST_1ST_RIFLE_COY_48_US_6_POST = random_pick({146, 490}) -- _SUPPLY_CACHE_146, _JUNGLE_FACTORY_490
    _C3_1ST_1ST_RIFLE_COY_48_US_16_POST = random_pick({148, 148, 148, UNKNOWN}) -- _SUPPLY_CACHE_148
    if _C3_1ST_1ST_RIFLE_COY_48_US_16_POST == 148 then
        _C2_1ST_1ST_RIFLE_COY_48_US_11_POST = UNKNOWN
    else
        _C2_1ST_1ST_RIFLE_COY_48_US_11_POST = 148 -- _SUPPLY_CACHE_148
    end

    _C1_1ST_2ND_RIFLE_COY_48_US_37_POST = random_pick({"71,37", HEXUNKNOWN, HEXUNKNOWN, HEXUNKNOWN}) -- nothing at "71,37"
    _C2_1ST_2ND_RIFLE_COY_48_US_42_POST = random_pick({147, 488}) -- _SUPPLY_CACHE_147, _JUNGLE_FACTORY_488
    _C3_1ST_2ND_RIFLE_COY_48_US_47_POST = random_pick({"91,50", OBJECTIVES[28], OBJECTIVES[32]}) -- _SUPPLY_DEPOT_486 at "91,50"

    _C2_1ST_5TH_RIFLE_COY_48_US_73_POST = random_pick({149, UNKNOWN}) -- _JUNGLE_FACTORY_149

end


In init_variables(), typically csmklua.pl will not auto-generate anything for you. It is for you to define your Lua script's variables.

Note the use of the random_pick() function. We will return to that later.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

In on_startup() (see above), after the init_constants() and init_variables() calls, we return to call the function set_org_lists().


Code: Select all

function set_org_lists (turn, side)

    -- called in on_startup(), in every on_next_turn(), and potentially again (and again) in any subsequent on_resume()

    --traceon()

    turn = turn or 1
    side = side or "a"

    ...


(In the above and all of our examples, understand the three-dots '...' elipsis to mean something omitted in the interest of brevity, else a signal that something is unfinished and will be returned to later. Do not confuse it with the Lua two-dots '..' string concatenation (join) operator.)

The set_org_lists() function is defined with the inputs 'turn' and 'side'. It is not strictly necessary to call set_org_lists() with explicit 'turn' and 'side' inputs. If not supplied, they default to


Code: Select all

    turn = turn or 1
    side = side or "a"


That is to say: if turn has a value (is not nil), set turn equal to its value; else if not defined, give it the default value 1. Similarly for 'side'. If supplied as an explicit input, retain its value; else if not defined, assign 'side' the default value "a".

That is an example of Lua's so-called variadic function arguments. For more on this topic, do a Web search: lua variadic function

In on_startup()'s function definition (see above), set_org_lists() is called without explicit inputs. So by means of the initalization mechanism just described, at scenario startup, turn is set to 1, and side is set to "a".

Continuing the set_org_lists() function definition:


Code: Select all

    ...

    ALLA = counters_all(SIDE_A)
    ALLB = counters_all(SIDE_B)

    ...


Those statements set the ALLA and ALLB variables to lists of all in-scenario counters for Side A and Side B respectively.

Continuing:


Code: Select all

   ...

    -- auto-generated org lists to follow; rename, resequence, regroup, and reorganize as necessary

    -- KEY:

    -- [P] Platoon
    -- [C] Company
    -- [B] Battalion
    -- [R] Regiment
    -- [G] Brigade
    -- [D] Division
    -- [K] Corps
    -- [A] Army
    _BINH_XUYEN_PRIVATE_ARMY_1 = {2,3,5,7,8,9,10,12,13,14,15,17,18,19,20,22,23,24,25,36,38,39,40,41,43,44,45,48,49,50,53,54,55,67,69,70,71,74,75,76,79,80,81,82,84,85,86,95,96,98,100,101,102,103,105,106,107,110,111,112,115,116,117,127,130,131,134,135,482,483,484,485,486,487,488,489,490,146,147,148,149,150,153,154,157,158,159,160,161,162,163,164,165,166,491,492,493,494,495,496,497,498,499,500,501} -- [R] [2122218] Binh Xuyen Private Army

    _TRUNG_DOAN_HQ_2 = {2} -- [P] [74,59] [213014] VM Regimental HQ (foot)

    _BAY_VIEN_3 = {3} -- [P] [75,56] [214204] VM Commander 4

    _1ST_1ST_BINH_XUYEN_INF_BTN_48_US_4 = {5,7,8,9,10,12,13,14,15,17,18,19,20,22,23,24,25} -- [B] [2112207] 1st/1st Binh Xuyen Infantry Battalion 48 - US

    _TIEU_DOAN_HQ_5 = {5} -- [P] [76,28] [213015] VM Battalion HQ (foot)

    _C1_1ST_1ST_RIFLE_COY_48_US_6 = {7,8,9,10} -- [C] [2102218] C1/1st/1st Rifle Company 48 - US
    _1ST_VM_RIFLE_48_7 = {7} -- [P] [90,33] [212040] VM Rifle Platoon 48 (U.S. Arms)
    _2ND_VM_RIFLE_48_8 = {8} -- [P] [90,32] [212040] VM Rifle Platoon 48 (U.S. Arms)
    _3RD_VM_RIFLE_48_9 = {9} -- [P] [89,33] [212040] VM Rifle Platoon 48 (U.S. Arms)
    _MORTAR_10 = {10} -- [P] [89,33] [211008] VM 50mm Mortars

   ...


For each of those variables, they are set to "lists" of trackid #s. Trackid #s are ID numbers that the game engine, also the CSEE, use to identify individual counters (units).

The '[R]' indicates that _BINH_XUYEN_PRIVATE_ARMY_1 is a Regiment, while '[2122218]' indicates its unit ID number (defined in the game's platoon OOB files).

In the CSEE, we use the conventions

  • prepend all organization variable names with a '_' (underscore)
  • append those names with the organization's trackid #
  • render the org name ALL CAPS

So for



_BAY_VIEN_3 = {3} -- [P] [75,56] [214204] VM Commander 4



'BAY_VIEN' is the organization (unit) name, 3 is its trackid #. _BAY_VIEN_3 is assigned the value '{3}', a list with a single member, the trackid # 3. _BAY_VIEN_3 is a platoon, initially located at hex 75,56, has an organization platoon id 214204, with the unit identifier 'VM Commander 4'.

Note that by excluding the comment, everything following the '--', the Lua interpreter sees that line as, effectively:



_BAY_VIEN_3 = {3}



After the '{3}', the rest (the comment) is ignored.

And so on for all the other unit organizations present in the current scenario (whether initially on map, else arriving later as reinforcements).

In this VN_550921_Rung_Sat.lua file, the set_org_lists() function ends with


Code: Select all

    ...

    _DOUGLAS_B26_INVADER_BOMBS_515 = {515} -- [P] [9999,9999] [115012] Douglas B-26 Invader {Bombs}

    _DOUGLAS_B26_INVADER_BOMBS_516 = {516} -- [P] [9999,9999] [115012] Douglas B-26 Invader {Bombs}

    -- hand-crafted org lists, if any, to follow

    --traceoff()

end


The '[9999,9999]' is a special hex coordinate indicating off-map, at some remote airbase.

If you employ the CSlint utility csmklua.pl to make your scenario Lua file -- you most certainly should! -- that program will auto-generate the org lists -- their org variable names, the trackid #s, the unit type, the map location, etc. Good luck trying to do all of that by hand! You will soon grow tired of it, and abandon your scripting project. No, use csmklua.pl! (see: https://www.matrixgames.com/forums/view ... 9#p4995709)

After the auto-generated org lists


Code: Select all

    -- hand-crafted org lists, if any, to follow


invites you to create any auxiliary org variable by hand (DIY, Do-It-Yourself). Thus far, in VN_550921_Rung_Sat.lua, we have no need to do that.

With that, set_org_lists() is done. So too is on_startup() (see above). The CSEE has processed its first game engine event. For now, nothing more for the CSEE to do!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

But soon thereafter, another game engine event -- the first game turn!

Which calls the CSEE trigger function on_next_turn():


Code: Select all

function on_next_turn (turn) -- DO NOT REMOVE

    -- after first (in)direct fire, Side A loses (arty) supply each turn
    if SIDE_A_FIRED then
        inc_ammo_level(SIDE_A, -2)
    end
    if SIDE_A_ARTY_FIRED then
        inc_arty_ammo_level(SIDE_A, -2)
    end

    -- after first (in)direct fire, Side B loses (arty) supply each turn
    if SIDE_B_FIRED then
        inc_ammo_level(SIDE_B, -2)
    end
    if SIDE_B_ARTY_FIRED then
        inc_arty_ammo_level(SIDE_B, -2)
    end

    ...


For either side, if artillery firing has commenced (if any of the SIDE_*_FIRED boolean TRUE/FALSE variables is set to 'true'), then the side's Ammo or Arty Ammo is adjusted downward by 2. Rather than "bean count", this is a rough and ready way to model the depletion of artillery stocks over the course of a battle.

Initially SIDE_*_FIRED are undefined, have the default Lua value 'nil'. In some contexts, 'nil' has the same effect as the Lua value 'false'. So


Code: Select all

    if SIDE_A_FIRED then
        inc_ammo_level(SIDE_A, -2)
    end


is effectively


Code: Select all

    if false then
        inc_ammo_level(SIDE_A, -2)
    end


since at scenario startup (where we are now), there is no firing of any kind yet. Thus there is not yet any downward adjustment to Ammo or Arty Ammo.

How and when are SIDE_*_FIRED set to true? If fire happens in the game -- another special event -- the game engine calls one or the other of these two CSEE trigger functions:


Code: Select all

function on_unit_fire (hc, trackid, pid, name, side, nation, oid, orgname, strength) -- DO NOT REMOVE

    if side == SIDE_A then
        SIDE_A_FIRED = true
    elseif side == SIDE_B then
        SIDE_B_FIRED = true
    end

end

function on_unit_arty_fire (hc, trackid, pid, name, side, nation, oid, orgname, strength) -- DO NOT REMOVE

    if side == SIDE_A then
        SIDE_A_ARTY_FIRED = true
    elseif side == SIDE_B then
        SIDE_B_ARTY_FIRED = true
    end

end


Would you look at all those function inputs! The game engine supplies those inputs; the CSEE accepts them as given.

The effect of those two functions: In game, every time a unit fires, that event triggers a call to either of the on_unit_fire() or on_unit_arty_fire() CSEE trigger function as appropriate. Then the given side's SIDE_*_FIRED variable is set to 'true', likely again and again and again ... as more and more firing happens throughout the scenario. With any of the SIDE_*_FIRED set to 'true', this means that ever thereafter the sides' Ammo and/or Arty Ammo levels are decremented. In the Rung Sat scenario case, by 2 each turn. (Other CSEE code might up the Ammo and Arty Ammo from time to time, from event to event.)

Note: The per turn -2 downward adjustment of Ammo and Arty Ammo is an arbitrary amount. In other scenarios, the decrement is -3. In a few scenarios, the decrement is -1 at night, -2 at day. It's all up to the scripter! And whatever is deemed appropriate to the situation.

Continuing with on_next_turn():


Code: Select all

    ...

    if turn == 1 then
        allot_airstrikes(UNKNOWN, ARVN_SIDE, 0, 0, 0)
    end
    if turn == 18 then
        allot_airstrikes(18, ARVN_SIDE, dieroll(3)-1, 3, 14, "", "Some air support is now on station and ready to assist in your operation!")
    elseif turn >= 19 then
        allot_airstrikes(UNKNOWN, ARVN_SIDE, random_pick({0,0,1,1,1,1,2,2,2}), 3)
    end

    ...


The above is some code invoking the rather convoluted airstrike allotment system as defined in user.lua (which see). We will save discussion of those details for later.

Continuing:


Code: Select all

    ...

    -- BX gain 100 EP for holding OBJECTIVES[13] until Turn 8
    if turn == 8 then
        if not OBJECTIVES13_CAPTURED then -- see on_objective_capture()
            inc_event_points(BX_SIDE, 100)
        end
    end

    -- BX gain 100 EP for holding OBJECTIVES[21] until Turn 30
    if turn == 30 then
        if not OBJECTIVES21_CAPTURED then -- see on_objective_capture()
            inc_event_points(BX_SIDE, 100)
        end
    end

end


On the indicated turns, if either of those objectives are captured (where the OBJECTIVES*_CAPTURED are set to 'true' in the on_objective_capture() trigger function; details to be discussed at some later date), the BX Army side's EPs (Event Points) are incremented by 100.

That's it! on_next_turn() is finished. Another triggering event dealt with.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Followed immediately by another: the scenario's first turn Side A first phase. Which naturally enough calls its own special CSEE trigger function, on_next_phase():


Code: Select all

function on_next_phase (turn, side) -- DO NOT REMOVE

    -- for testing purposes; note that these might override player selected options!
    -- by default, in normal game play, the player selected options apply;
    -- by default, in auto-test mode, FOW is OFF, all the time, for both sides
    if is_test_trial_play() and not is_qa_testing() then
        set_fow(side, 0) -- for current side, set FOW OFF
        set_fow(other_side(side), 1) -- for opposing side, set FOW ON
    end

    set_org_lists(turn, side)

    if turn == 1 then
        show_briefing(side)
    end

    battle_plan(turn, side)

--[[ commonly used, uncomment and adapt as necessary:
    -- every phase, adjust downward the NVA assault_attack_aggressiveness_effect bonus per their current loss rate
    adjust_adaptive_ai (sideof(NORTH_VIETNAM_NATION), NORTH_VIETNAM_NATION, "assault_attack_aggressiveness_effect", - math.floor(total_loss_rate(sideof(NORTH_VIETNAM_NATION))/2))
--]]

    csee_check(turn, side) -- DO NOT MOVE, OR REMOVE

end


The purpose of


Code: Select all

    if is_test_trial_play() and not is_qa_testing() then
        set_fow(side, 0) -- for current side, set FOW OFF
        set_fow(other_side(side), 1) -- for opposing side, set FOW ON
    end


is to set the game's FOW appropriate for testing. We can ignore that for now.

In


Code: Select all

    set_org_lists(turn, side)


Note that here, unlike in on_startup(), set_org_lists() is called with the explicit 'turn' and 'side' inputs.

What are 'turn' & 'side'? Those are function inputs, passed as "arguments" to the function. What are there values? See the preceding


Code: Select all

function on_next_phase (turn, side) -- DO NOT REMOVE


Those 'turn' and 'side' too are inputs provided by the game engine when it calls the on_next_phase() function.

So set_org_lists() is called each and every phase, but after game startup with 'turn' and 'side' specified explicitly. (In your set_org_lists(), you might have customized dynamic org lists dependent on the current turn and side.)

The


Code: Select all

    if turn == 1 then
        show_briefing(side)
    end


says that on Turn 1 (only), show (in the form of an in-game dialog) the scenario briefing for the current side. (In multi-day scenarios, it is possible to show different briefings at later turns.)

Then


Code: Select all

    battle_plan(turn, side)


invokes the battle_plan() function for the current side and turn.

Note where


Code: Select all

--[[ commonly used, uncomment and adapt as necessary:
    -- every phase, adjust downward the NVA assault_attack_aggressiveness_effect bonus per their current loss rate
    adjust_adaptive_ai (sideof(NORTH_VIETNAM_NATION), NORTH_VIETNAM_NATION, "assault_attack_aggressiveness_effect", - math.floor(total_loss_rate(sideof(NORTH_VIETNAM_NATION))/2))
--]]


is not uncommented, remains commented out. In this Rung Sat scenario, there is no NORTH_VIETNAM_NATION. In fact (due to the VN_550921_Rung_Sat.ai settings), the Side B BX Army data parameters are exactly the same as the Side A VNA data parameters. Since the BX Army assault_attack_aggressiveness_effect is not initially set high, there is no good reason to reduce it during the course of the scenario. Like the VNA assault_attack_aggressiveness_effect, there is no special downward adjustment; it will remain unchanged.

For now, you can ignore the


Code: Select all

    csee_check(turn, side) -- DO NOT MOVE, OR REMOVE


We will discuss the purposes of that some other time.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

on_next_phase() has called battle_plan(). This is where it really starts to get interesting!


Code: Select all

function battle_plan (turn, side)

    -- UNLEASH is true if game launched with -U switch, or if Ctrl+Alt+U in game
    -- Ctrl+Alt+U is a toggle, so will set UNLEASH to false after an earlier Ctrl+Alt+U (setting UNLEASH to true)
    if UNLEASH then
        unleash(ALLA)
        unleash(ALLB)
        return
    end

    if side == SIDE_A and is_ai(side) then

        --traceon()
        battle_plan_a(turn, side)
        --traceoff()

    elseif side == SIDE_B and is_ai(side) then

        --traceon()
        battle_plan_b(turn, side)
        --traceoff()

    end

end


Ignore the UNLEASH business for now. We will return to that at a later date.

battle_plan() calls one side or the other's battle_plan_a() or battle_plan_b() function. But only if the side is AI-played. If the side is under human player control, its battle plan function will not be called. You, the human player, don't want the CSEE/SAI giving unit orders for you!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Taking things out of order, let's look first at the Side B BX Army battle plan function. The BX Army is on the defensive in this scenario. Its SAI will be less dynamic, easier to plot and to understand, than the VNA's. So with the Side B BX Army we begin.


Code: Select all

function battle_plan_b (turn, side)

    -- standard orders, uncomment as necessary:
    if turn == 1 then
        -- all Side B units hold initially (or maybe use halt())
--        defend_strong(ALLB) -- no, because we don't any of the transports to mistakenly move
        hold(ALLB)
    end

    -- don't act until turn 2, awaiting placement of logistic units (as first-turn reinforcements)
    if turn == 1 then
        return
    end

    ...


Initially, by default and absent any subsequent order overrides, we 'hold(ALLB)' -- set all BX Army units to "hold". The hold() function (see Manual/LUA_FUNCTIONS_REFERENCE.txt) orders the indicated units to neither move nor fire. (halt() prevents movement, but does permit firing.) This is a typical battle_plan function opening.

Next we say that on Turn 1 (only), just return; we are done with this function. What?

The mystery is solved by the Schedule Dialog:

CSVN_AAR_AI_AI_RungSat8.jpg
CSVN_AAR_AI_AI_RungSat8.jpg (91.56 KiB) Viewed 2611 times

On Turn 1, under control of the game engine, not the CSEE, the BX Army supply units are randomly scattered around their respective center points.

For example



[29] 1 (??) at 60,59 (3) Side B Supply Cache (VP)



says that for reinforcement group #29, on Turn 1, there is a ?? chance (in this case, 100%) that a Side B Supply Cache (VP) unit will locate (arrive as "reinforcement") anywhere within 3 hexes around hex 60,59.

(The supply unit random placements are randomly scattered to enhance scenario replayability. Note: The scatter # is the maximum random displacement from the center hex; the actual scatter might be less than that.)

After all of this discussion, this is a good time to step back, run an auto-test, observe the game engine and event engine take action.

See: Auto-Testing

In my current auto-test, the Supply Cache unit for that Reinforcement Group 29 has in fact been randomly placed at hex 63,59 -- three hexes away from 60,59:

CSVN_AAR_AI_AI_RungSat9.jpg
CSVN_AAR_AI_AI_RungSat9.jpg (283.73 KiB) Viewed 2639 times

Here, with supply units highlighted in magenta (kinda hard to see, admittedly), are the actual random placements for the reinforcements listed in that Schedule Dialog:

CSVN_AAR_AI_AI_RungSat10.jpg
CSVN_AAR_AI_AI_RungSat10.jpg (1.44 MiB) Viewed 2639 times

We must let the game engine take action, bring on those reinforcements, before we have the Side B SAI determine anything. This is because a turn's battle_plan orders are assigned before any game action for that turn. We want the supply units at first to arrive on Turn 1. Then at the beginning of Turn 2, by now knowing where the supply units are, the SAI can figure out which orders to give the other non-supply units of Side B. Hence the need for the 'if turn == 1 then return end' abort in battle_plan_b(). On Turn 1 only, Side B units on map at turn's beginning are ordered to "hold", and do nothing else. Verstehen?
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

In what follows, indeed throughout the course of this AAR, if you try to replicate what you see here, you will fail. Your Mileage Will Vary.

This is because

  • I will be coding game engine changes along the way, changes not yet available in public release. (I discovered and fixed another issue just this morning, an issue affecting cross river movement in the Rung Sat.)
  • I will be revising the VN_550921_Rung_Sat.lua file along the way, and possibly also the scenario file VN_550921_Rung_Sat.scn, perhaps even other game data files.

So we will not be on the same page game version and data wise.

But just as important

  • So much of this randomized. By chance, your results are sure to differ from mine.

Remember this if you try to replicate my results. Your experience will not be the same, and will maybe be even far different from mine (the latter when my private, modified version of the scenario Lua file differs greatly from the public version).

Still, the principles and techniques etc. will vary little. What you learn here in this AAR (also here) should be generally applicable and mostly unchanging.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

For now, we will focus on just one BX Army Company, its assigned defense, and its first several turn moves.

In VN_550921_Rung_Sat.lua's set_org_lists() function, we have


Code: Select all

    _C2_1ST_5TH_RIFLE_COY_48_US_73 = {74,75,76} -- [C] [2102218] C2/1st/5th Rifle Company 48 - US
    _1ST_VM_RIFLE_48_74 = {74} -- [P] [71,65] [212040] VM Rifle Platoon 48 (U.S. Arms)
    _2ND_VM_RIFLE_48_75 = {75} -- [P] [70,65] [212040] VM Rifle Platoon 48 (U.S. Arms)
    _3RD_VM_RIFLE_48_76 = {76} -- [P] [71,65] [212040] VM Rifle Platoon 48 (U.S. Arms)


The C2/1st/5th Rifle Company (trackid 73; see that number appended to its org name) has three platoons (trackids 74, 75 & 76). For the platoons, note their initial map placements (and compare with the initial screenshots following). They all share the same platoon ID#, 212040.

In the battle_plan_b() function, we have


Code: Select all

    -- _C2_1ST_5TH_RIFLE_COY_48_US_73 -- C2/1st/5th Rifle Company 48 - US
    do local units = _C2_1ST_5TH_RIFLE_COY_48_US_73
        if counter_exists(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST) then
            local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)
            if not within(units, objective, DOWNLEFTDIR, 2) then
                move_norush(units, objective, NODIR, 0, 100)
            else
                defend_scatter(units, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
            end
        else
            defend_way_point(units, {"71,64", "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)
        end
    end


In that typical CSEE code "block", we begin with an identifying comment (see the first line, beginning with the Lua comment marker '--').

Followed by a 'do-end' block


Code: Select all

    do ...

        ...

    end


What is the purpose of that 'do-end' block? Contrast this


Code: Select all

    -- _C2_1ST_5TH_RIFLE_COY_48_US_73 -- C2/1st/5th Rifle Company 48 - US
    if counter_exists(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST) then
        local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)
        if not within(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, DOWNLEFTDIR, 2) then
            move_norush(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, NODIR, 0, 100)
        else
            defend_scatter(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
        end
    else
        defend_way_point(_C2_1ST_5TH_RIFLE_COY_48_US_73, {"71,64", "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)
    end


with the 'do-end' version above.

In the second version, note the repeated referencing of '_C2_1ST_5TH_RIFLE_COY_48_US_73'. In the CSEE/SAI, unit org names tend to be rather long and "busy". Hard to read, easy to make typos.

In the first version, by opening the 'do ... end' with setting a local variable (local to the block) 'units' equal to '_C2_1ST_5TH_RIFLE_COY_48_US_73', we make the code easier to read. And we are far less likely to misspell 'units'.

The first version is better, and is preferred, standard practice for CSEE/SAI. (At least when auto-generated via csmklua.pl.)

IMPORTANT: As a 'local' variable, 'units' only has meaning in the 'do ... end' block where it was declared. After the 'end', 'units' ceases to have meaning. (Unless/until you create an entirely separate 'units' local variable elsewhere in the script.) If you were to do something like this


Code: Select all

    do local units = _C2_1ST_5TH_RIFLE_COY_48_US_73

        ...

    end

    halt(units)


this would break your script. The Lua interpreter would complain of a "nil value" when referencing that last 'units' mention outside the preceding 'do ... end' scope.

When CSEE scripting the AI (SAI'ing), you will make plenty of "nil value" mistakes. Repeating: Run csluachk.pl early and often!

For more about Lua local variables, see:

https://www.lua.org/pil/4.2.html

Or do a Web search: lua local variable
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

In the above "orders" assigned to _C2_1ST_5TH_RIFLE_COY_48_US_73, we have assigned another local variable



local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)



counter_hex() returns the hex coordinates -- an "x,y" character string -- locating the _C2_1ST_5TH_RIFLE_COY_48_US_73_POST. Where did that come from?

In the VN_550921_Rung_Sat.lua init_variables(), we have



_C2_1ST_5TH_RIFLE_COY_48_US_73_POST = random_pick({149, UNKNOWN}) -- _JUNGLE_FACTORY_149



There we assign _C2_1ST_5TH_RIFLE_COY_48_US_73_POST to either of

  • 149, the trackid of the unit _JUNGLE_FACTORY_149
  • UNKNOWN, which is defined as -1 (in init.lua)


random_pick() is a function to randomly select something from a list of values. For more explanation, see LUA_FUNCTIONS_REFERENCE.txt

If 149 is selected,



local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)



sets objective to the location of the _JUNGLE_FACTORY_149



_JUNGLE_FACTORY_149 = {149} -- [P] [T1: 71,67] [218809] Jungle Factory



Recall the discussion of reinforcement scatter above. The _JUNGLE_FACTORY_149 might appear at 71,67 or it might scatter some distance away from that.

if random_pick() selects UNKNOWN, then its counter_hex() is set to



HEXUNKNOWN = "-1,-1"



where that is defined in init.lua. (Which see, if you care too.)

NOTE: Already this might appear to be dauntingly complex and difficult to understand. Bear with me. Although we tried to KISS (Keep it Simple, Stupid) the CSEE/SAI, you still need to climb a learning curve, hopefully not too steep. After you understand and internalize some of these basics, if you persist, sooner or later these nitpicky details will become second nature to you. If you are patient, you will get the hang of it.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Review the the 'do ... end' block in the earlier post.

If _C2_1ST_5TH_RIFLE_COY_48_US_73 has been ordered to defend the _JUNGLE_FACTORY_149, and with 'objective' defined -- as a so-called "hc", a hex coordinates string -- we have the code


Code: Select all

            if not within(units, objective, DOWNLEFTDIR, 2) then
                move_norush(units, objective, NODIR, 0, 100)
            else
                defend_scatter(units, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
            end


this means to say

  • If all of _C2_1ST_5TH_RIFLE_COY_48_US_73 is not within 2 hexes, and situated generally down and to the left, of _JUNGLE_FACTORY_149, then those units are to move (no rush) to that objective.
  • Else they are in place, so scatter outward as much as 2 hexes from that objective, and defend that objective strongly.

Otherwise if _C2_1ST_5TH_RIFLE_COY_48_US_73 has been assigned to defend UNKNOWN at HEXUNKNOWN, we have the code


Code: Select all

            defend_way_point(units, {"71,64", "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)

meaning to say

  • Move to OBJECTIVES[19] following the way points "71,64", "71,61", "73,61", and finally OBJECTIVES[19].
  • Strongly defend that objective arrayed generally in an upward/rightward direction, but no more than 1 hex away.

Ignore the 100 (and 50 above) for now. Their significance will be explained some other time.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Is this getting to be complicated? You bet!

Couldn't the scenario have been designed to locate the _JUNGLE_FACTORY_149 at a specified, fixed location? Sure.

Could we not just order _C2_1ST_5TH_RIFLE_COY_48_US_73 to defend _JUNGLE_FACTORY_149, and only that? Right.

But where's the replayability? Randomizing placements and unit orders -- not just for _C2_1ST_5TH_RIFLE_COY_48_US_73 but the other BX Army units also -- means the scenario will play out differently each time. Good.

Could we not have started with a simpler scenario and built the Lua file from scratch? (Well, basing off the csmklua.pl empty template.) Yup.

But possibly the best way to ease yourself into the world of CSEE/SAI scripting is to take an existing scenario Lua file and tweak and modify it. As we are doing here.

Besides, VN_550921_Rung_Sat.lua might be in some ways outdated (since it was first written a year or two ago); it is unfinished, we can make it better.

So let's make it better!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Words, words, words. Enough with the code examples! Let's see some screenshots! Let's try an auto-test.

At the very beginning of the scenario, we see this:

CSVN_AAR_AI_AI_RungSat12.jpg
CSVN_AAR_AI_AI_RungSat12.jpg (574.11 KiB) Viewed 2584 times

_C2_1ST_5TH_RIFLE_COY_48_US_73 consists of the green circled units at hexes 70,65 and 71,65.

The Schedule Dialog

CSVN_AAR_AI_AI_RungSat11.jpg
CSVN_AAR_AI_AI_RungSat11.jpg (322.51 KiB) Viewed 2584 times

says that on Turn 1, there is a ?? (in fact, 100%) chance that Reinforcement Group #28 -- double click on the item to show the Unit List Dialog -- will appear within 2 hexes of 71,67 (light blue box).

We have set up a pass file "b" meaning to stop at the beginning of every Side B phase. (For more about pass files, see Auto-Testing.)

As described earlier, all BX Army Side B "reinforcements" appear on Turn 1. At the beginning of Turn 2, we see this:

CSVN_AAR_AI_AI_RungSat13.jpg
CSVN_AAR_AI_AI_RungSat13.jpg (579.12 KiB) Viewed 2584 times

Ah! The _JUNGLE_FACTORY_149 (light blue circle) "arrived" at hex 72,67, scattered 1 hex away from its default 71,67 location.

Was _C2_1ST_5TH_RIFLE_COY_48_US_73 assigned to defend _JUNGLE_FACTORY_149 or OBJECTIVES[19]?

After the Side B moves in Turn 2, at the beginning of Turn 3, we see this:

CSVN_AAR_AI_AI_RungSat14.jpg
CSVN_AAR_AI_AI_RungSat14.jpg (576.05 KiB) Viewed 2584 times

_C2_1ST_5TH_RIFLE_COY_48_US_73 appear to be moving away from _JUNGLE_FACTORY_149. Evidently, the company is not ordered to defend the supply factory. Instead, it is ordered to move off to defend OBJECTIVES[19]. Their assigned orders appear to be


Code: Select all

           defend_way_point(units, {[b]"71,64"[/b], "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)


Note where the lead platoons have stopped at the first way point, "71,64". This is no accident. Upon reaching a way point, a unit will end its turn there, only to resume movement on the following turn.

On the following turn, Turn 4, the units are at

CSVN_AAR_AI_AI_RungSat15.jpg
CSVN_AAR_AI_AI_RungSat15.jpg (579.02 KiB) Viewed 2584 times

The lead units have reached the second way point, 71,61. The tail unit has only moved one hex to the first way point, 71,64, where it stops.

Next turn, the beginning of Turn 5 shows:

CSVN_AAR_AI_AI_RungSat16.jpg
CSVN_AAR_AI_AI_RungSat16.jpg (577.67 KiB) Viewed 2584 times

The lead units have reached the third way point, 73,61, with the third company pulling up the rear at way point #2, 71,61.

At the beginning of Turn 6, we see:

CSVN_AAR_AI_AI_RungSat17.jpg
CSVN_AAR_AI_AI_RungSat17.jpg (576.36 KiB) Viewed 2584 times

The lead units have arrived at OBJECTIVES[19]. The laggard unit is now at way point #3.

Turn 7's start shows:

CSVN_AAR_AI_AI_RungSat18.jpg
CSVN_AAR_AI_AI_RungSat18.jpg (582.96 KiB) Viewed 2584 times

Okay! _C2_1ST_5TH_RIFLE_COY_48_US_73 is now in place, within 1 hex of OBJECTIVES[19] (yellow circle) defending generally in an upward, rightward direction (any of the green circles).

Letting the auto-test go another turn, I see where there is no further movement. _C2_1ST_5TH_RIFLE_COY_48_US_73 is at its final defensive positions. There is no movement to hex 74,58 since it is a waterway hex, and is therefore impassable.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

What if instead of using move_way_point() we had specified more simply



defend_strong(units, OBJECTIVES[19], UPRIGHTDIR, 1, 100)



The above says to (implied) move to defend OBJECTIVES[19] without specifying any way points; let the game engine's internal path finding routines figure out the route.

With that code, and after an auto-test restart, several turns in, at the beginning of Turn 5, we see

CSVN_AAR_AI_AI_RungSat19.jpg
CSVN_AAR_AI_AI_RungSat19.jpg (581.42 KiB) Viewed 2547 times

Hmm. It seems that, under control of the game engine path finding, _C2_1ST_5TH_RIFLE_COY_48_US_73 has taken a somewhat different path. Compare with the beginning of Turn 6 screenshot above. More importantly, _C2_1ST_5TH_RIFLE_COY_48_US_73 is moving generally faster. By not stopping at way points and instead proceeding immediately onward, sufficient APs remaining, the platoons are able to go farther along turn by turn.

Next turn, at the beginning of Turn 6, _C2_1ST_5TH_RIFLE_COY_48_US_73 has arrived:

CSVN_AAR_AI_AI_RungSat20.jpg
CSVN_AAR_AI_AI_RungSat20.jpg (577.3 KiB) Viewed 2547 times

But have they really? Let's run the auto-test an additional turn to find out.

At the beginning of Turn 7, we see:

CSVN_AAR_AI_AI_RungSat21.jpg
CSVN_AAR_AI_AI_RungSat21.jpg (581.61 KiB) Viewed 2547 times

Oh, _C2_1ST_5TH_RIFLE_COY_48_US_73 has assumed the same defensive positions as Turn 7 of the way point case.

It appears that way point move is somewhat slower. Why then would we ever use it?

  • In complex terrain, the game engine path finding will sometimes screw up, send units in sub-optimal directions, maybe direct units down a dead-end street, into a cul de sac, where they will remain stuck.
  • The game engine path finding will sometimes send units off road, when staying on road will be faster.
  • There may be minefields, and with way point movement you know the precise route to avoid them.
  • You are trying to avoid other known dangers.
  • You are deliberately taking a roundabout route for purposes of reconnoitering or patrolling.
  • Other reasons.

From my earlier experience scripting this scenario and other scenarios, in the complex Vietnamese terrain, way point movement is often the best way to go.

In VN_550921_Rung_Sat, we know that the VNA is quite a ways distant, won't arrive in these parts for a good number of turns yet. _C2_1ST_5TH_RIFLE_COY_48_US_73 is in no rush to reach OBJECTIVES[19]. Way point movement it shall remain!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

I try another auto-test, this time to see if, by chance, _C2_1ST_5TH_RIFLE_COY_48_US_73 might be ordered to defend _JUNGLE_FACTORY_149.

At the beginning of Turn 2, after "arrival" (as "reinforcements") of the supply units on Turn 1, but before the assignment and execution of orders on Turn 2:

CSVN_AAR_AI_AI_RungSat22.jpg
CSVN_AAR_AI_AI_RungSat22.jpg (324 KiB) Viewed 2541 times

Note that this time _JUNGLE_FACTORY_149 appears at a different location (see the preceding auto-test).

We resume the auto-test (AI > Activate AI, else Turn > Next). After the Turn 2 movement, at the beginning of Turn 3:

CSVN_AAR_AI_AI_RungSat23.jpg
CSVN_AAR_AI_AI_RungSat23.jpg (327.22 KiB) Viewed 2541 times

Okay, _C2_1ST_5TH_RIFLE_COY_48_US_73 is moving towards _JUNGLE_FACTORY_149. The code that's having them do that:


Code: Select all

            if not within(units, objective, DOWNLEFTDIR, 2) then
                move_norush(units, objective, NODIR, 0, 100)
            else
                defend_scatter(units, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
            end


Let's see where they settle. We resume the auto-test.

After the Turn 3 movement, at the beginning of Turn 4, there is no change.

Is that it? Is that the settled defensive posture for _C2_1ST_5TH_RIFLE_COY_48_US_73?

It would seem to be so. We restart the auto-test, and at the beginning of Turn 5, again there is no change.

Why is that? Look at the code above. Note that at all of the _C2_1ST_5TH_RIFLE_COY_48_US_73 platoons are presently within 2 hexes of the objective (yellow circle) in a downward/leftward direction (green circles).

It wouldn't seem to be so. But you need to understand that, in the context of within(), DOWNLEFTDIR is broadly defined to include that direction but also the direction flank left, DOWNDIR, and flank right, UPLEFTDIR. All of _C2_1ST_5TH_RIFLE_COY_48_US_73 are in the UPLEFTDIR, so are within the directionality broadly defined.

So in effect, "DOWNLEFTDIR" really means a semi-circle around the center point, hex 71,68, pointing in the DOWNLEFTDIR direction. (Is there a way to more narrowly specify the direction so that it doesn't also extend to flank left and flank right? Why, yes there is. You could do that by specifying DOWNLEFTDIR as a list, {DOWNLEFTDIR}, in that within() function call.)

All of _C2_1ST_5TH_RIFLE_COY_48_US_73 are within that broadly defined semi-circle (two platoons of them right at the edge) no more than 2 hexes from the center. Given that is so, here is the operative command (the 'else' part):


Code: Select all

                defend_scatter(units, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)


The _C2_1ST_5TH_RIFLE_COY_48_US_73 is in a random scatter defensive position within 2 hexes of _JUNGLE_FACTORY_149 in a generally (broadly defined) "downward, leftward" direction. No more orders in battle_plan_b() function follow for that org. Done.

For _C2_1ST_5TH_RIFLE_COY_48_US_73 and _JUNGLE_FACTORY_149, is that an optimal defense? At this point, who knows? See how complicated the terrain is in that area:

CSVN_AAR_AI_AI_RungSat24.jpg
CSVN_AAR_AI_AI_RungSat24.jpg (1.55 MiB) Viewed 2541 times

Morever, from which direction might the VNA attack? Who knows?

We accept these _C2_1ST_5TH_RIFLE_COY_48_US_73 and _JUNGLE_FACTORY_149 orders without change. When this scenario was first scripted a year or two ago, it seemed good enough at the time. Good enough then, good enough for now. But subject to possible change later. We shall see.

A digression...

Returning to this code over a year later after first written, I admit to being puzzled by the DOWNLEFTDIR business, and why the within() true?/false? was determined to be true. I tried some advanced debugging techniques, especially Lua tracing, together with careful review of the code. Ultimately I was able to confirm the correctness of everything.

Everybody should know that, among so many ways we test, test, test nearly every aspect of the Campaign Series code and data, one of the tests is a suite of automated validation tests of the CSEE. Literally thousands and thousands of test cases covering all 600+ of the CSEE functions, many of those test cases designed to stress the system, to test odd and quirky situations in addition to the commonplace ones. Is the CSEE perfect? No, nothing is perfect (or finished). Is the CSEE WAD (Working As Designed)? To the best of our ability to assess that, we believe so.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
Post Reply

Return to “After Action Report”