Create itemCountInSelectedContainer function for a decision

I’m trying to figure out how to get a total item count from the orehold to make a decision.

Here is the starting function.

[...]
Just itemInInventory ->
                    describeBranch "I see at least one item in the ore hold. Move this to the item hangar."
                        (endDecisionPath
                            (actWithoutFurtherReadings
                                ( "Drag and drop."
                                , EffectOnWindow.effectsForDragAndDrop
                                    { startLocation = itemInInventory.totalDisplayRegion |> centerFromDisplayRegion
                                    , endLocation = itemHangar.totalDisplayRegion |> centerFromDisplayRegion
                                    , mouseButton = MouseButtonLeft
                                    }
                                )
                            )
                        )

Here is what I am trying to achieve.

[...]
Just itemInInventory ->
    if itemCountInSelectedContainer > 5 then
        describeBranch "At least 5 items in the ore holder. Stack them."
            (useContextMenuCascade
                ( "Ore hold", itemInInventory )
                    (useMenuEntryWithTextContaining "Select All" 
                        (useMenuEntryWithTextContaining "Stack all" menuCascadeCompleted)
                    )
                    context.readingFromGameClient
            )

    else
        describeBranch "I see at least one item in the ore hold. Move this to the item hangar."
            (endDecisionPath
                (actWithoutFurtherReadings
                    ( "Drag and drop."
                     , EffectOnWindow.effectsForDragAndDrop
                        { startLocation = itemInInventory.totalDisplayRegion |> centerFromDisplayRegion
                        , endLocation = itemHangar.totalDisplayRegion |> centerFromDisplayRegion
                        , mouseButton = MouseButtonLeft
                        }
                    )
                )
            )

I’m not sure how to extract the itemCount in Int from this function in ParseUserInterface.elm

maybeSelectedContainerInventoryNode =
        rightContainerNode
            |> Maybe.andThen
                (listDescendantsWithDisplayRegion
                    >> List.filter
                        (\uiNode ->
                            [ "ShipCargo", "ShipDroneBay", "ShipOreHold", "StationItems", "ShipFleetHangar", "StructureItemHangar" ]
                                |> List.member uiNode.uiNode.pythonObjectTypeName
                        )
                    >> List.head
                )

Here is a function to parse the list of items in the inventory (plus other interesting elements):

Here is a screenshot from the guide illustrating this area:

parsed items in the inventory window

The linked mining bot also contains the function selectedContainerFirstItemFromInventoryWindow that does almost the same what you need:

You can reuse that to build your counting function:

itemCountInSelectedContainerFromInventoryWindow : EveOnline.ParseUserInterface.InventoryWindow -> Maybe Int
itemCountInSelectedContainerFromInventoryWindow =
    selectedContainerItemsFromInventoryWindow
        >> Maybe.map List.length


selectedContainerItemsFromInventoryWindow : EveOnline.ParseUserInterface.InventoryWindow -> Maybe (List UIElement)
selectedContainerItemsFromInventoryWindow =
    .selectedContainerInventory
        >> Maybe.andThen .itemsView
        >> Maybe.map
            (\itemsView ->
                case itemsView of
                    EveOnline.ParseUserInterface.InventoryItemsListView { items } ->
                        items

                    EveOnline.ParseUserInterface.InventoryItemsNotListView { items } ->
                        items
            )

How did I find the functions linked above? When you test the mining bot example bot, you can see it uses a drag&drop action to move items from the selected container to the item hangar.
This means it somehow reads the location of an item in the selected container.
That bot also displays this text when it performs this action:

“I see at least one item in the ore hold. Move this to the item hangar.”

You can use that text to find the action in the program code. In that part of the function to select the next action, you see the usage of selectedContainerFirstItemFromInventoryWindow to get the item in the inventory.

The guide on the parsing library for EVE Online, on the other hand, is linked in the overview of guides. You can find it at bots/guide/eve-online/parsed-user-interface-of-the-eve-online-game-client.md at main · Viir/bots · GitHub

1 Like

Much easier than what I expected.

I cannot evaluate this line properly with the maybe Int.

Is this the right way?

if (itemCountInSelectedContainerFromInventoryWindow |> Maybe.withDefault 0) > 5 then

What error message do you get from the editor for the code you posted?

TYPE MISMATCH - This function cannot handle the argument sent through the (|>) pipe:
373|                     if (itemCountInSelectedContainerFromInventoryWindow |> Maybe.withDefault 0) > 5 then
                                                                                #^^^^^^^^^^^^^^^^^^^#
The argument is:
    #EveOnline.ParseUserInterface.InventoryWindow -> Maybe.Maybe Int#
But (|>) is piping it to a function that expects:
    #Maybe.Maybe number#Elm

Going by that error message, the types don’t match exactly, but almost.
What we need to know here is that Int is a number. This means that Maybe.Maybe Int satisfies the constraint of Maybe.Maybe number.

Knowing the relationship between Int and number, we can understand we only need to get rid of the EveOnline.ParseUserInterface.InventoryWindow -> part in the front.
The arrow tells us it is a function argument. To get rid of a function argument, we apply the function to an argument.

The types should match as soon as you add a value of type EveOnline.ParseUserInterface.InventoryWindow.

So I should add a value of this type to the right?

Is this where we have to use >> instead of |>? I’m still not sure how to logically use >> to manipulate functions

 Just itemInInventory ->
                       if (itemCountInSelectedContainerFromInventoryWindow |> Maybe.withDefault 3) > 5 then
                           describeBranch "At least 5 items in the ore holder. Stack them."
                               (useContextMenuCascade
                                   ( "Ore hold", itemInInventory )
                                   (useMenuEntryWithTextContaining "Select All"  menuCascadeCompleted)
                                   )
                                   context.readingFromGameClient
                               )

That should work, yes.

No need to use >> or |>. You never need any of these operators when writing code.

Finally figured it out.

Thanks for not spoon feeding my the answer. I now know what my mistake was. I was not supplying the container argument to the function.

It took many clues but I got it lol

 if (itemCountInSelectedContainerFromInventoryWindow inventoryWindowWithOreHoldSelected |> Maybe.withDefault 0) > 5 then
1 Like

Here is the full function that works.

dockedWithOreHoldSelected : BotDecisionContext -> EveOnline.ParseUserInterface.InventoryWindow -> DecisionPathNode
dockedWithOreHoldSelected context inventoryWindowWithOreHoldSelected =
    case inventoryWindowWithOreHoldSelected |> itemHangarFromInventoryWindow of
        Nothing ->
            describeBranch "I do not see the item hangar in the inventory." askForHelpToGetUnstuck

        Just itemHangar ->
            case inventoryWindowWithOreHoldSelected |> selectedContainerFirstItemFromInventoryWindow of
                Nothing ->
                    describeBranch "I see no item in the ore hold. Check if we should undock."
                        (continueIfShouldHide
                            { ifShouldHide =
                                describeBranch "Stay docked." waitForProgressInGame
                            }
                            context
                            |> Maybe.withDefault (undockUsingStationWindow context)
                        )

                Just itemInInventory ->
                    if (itemCountInSelectedContainerFromInventoryWindow inventoryWindowWithOreHoldSelected |> Maybe.withDefault 0) > 5 then
                        describeBranch "At least 5 items in the ore holder. Stack them."
                            (useContextMenuCascade
                                ( "Ore hold", itemInInventory )
                                (useMenuEntryWithTextContaining "Stack all" menuCascadeCompleted)
                                context.readingFromGameClient
                            )

                    else
                        describeBranch "I see at least one item in the ore hold. Move this to the item hangar."
                            (endDecisionPath
                                (actWithoutFurtherReadings
                                    ( "Drag and drop."
                                    , EffectOnWindow.effectsForDragAndDrop
                                        { startLocation = itemInInventory.totalDisplayRegion |> centerFromDisplayRegion
                                        , endLocation = itemHangar.totalDisplayRegion |> centerFromDisplayRegion
                                        , mouseButton = MouseButtonLeft
                                        }
                                    )
                                )
                            )
1 Like