Canvas Apps

Can ChatGPT really help with building Power Apps?

A computer screen with ChatGPT open
ChatGPT

ChatGPT

ChatGPT is buzz word these days! Has been for some time and will probably remain for more. In my limited usage of ChatGPT, I enjoy the conversations, the answers, the back and forth. I have asked questions across several different/weird/random topics like:

  1. How deep do you have to dig for a high rise foundation?
  2. Which is the best horror movie on Netflix right now?
  3. What healthy snack can I eat in the middle of the day?

So it was just a matter of time before I attempted to mix ChatGPT and Power Apps. Which I finally did during the Battleship game build.

Power Apps and ChatGPT - What worked and what did not

Making computer moves intelligent

When I first asked ChatGPT to come up with code for automated computer moves, it gave me the code where each move is random.


Set(Board, 
    ForAll(
        Sequence(10), 
        ForAll(
            Sequence(10), 
            { Row: Value1, Column: Value2, IsHit: false, HasShip: false }
        )
    )
));

// Filter available cells
Set(AvailableMoves, Filter(Board, !IsHit));

// Determine computer's move
Set(RandomMove, If(CountRows(TargetMoves) > 0, First(TargetMoves), First(Shuffle(AvailableMoves))));

// Update the board
UpdateIf(Board, Row = RandomMove.Row && Column = RandomMove.Column, { IsHit: true });

As you can see, these moves are random. Then I asked if it could make it smarter. And it gave me the following code:

// Filter available cells
Set(AvailableMoves, Filter(Board, !IsHit));

// Decide move based on state
If(
    ComputerState.HitState = "Hunt",
    Set(ComputerMove, First(Shuffle(AvailableMoves))),
    Set(TargetCells, 
        Filter(
            Board,
            (
                (Row = ComputerState.LastHit.Row - 1 && Column = ComputerState.LastHit.Column) ||
                (Row = ComputerState.LastHit.Row + 1 && Column = ComputerState.LastHit.Column) ||
                (Row = ComputerState.LastHit.Row && Column = ComputerState.LastHit.Column - 1) ||
                (Row = ComputerState.LastHit.Row && Column = ComputerState.LastHit.Column + 1)
            ) && !IsHit
        )
    ),
    Set(ComputerMove, If(CountRows(TargetCells) > 0, First(TargetCells), First(Shuffle(AvailableMoves))))
);

// Update the board
UpdateIf(Board, Row = ComputerMove.Row && Column = ComputerMove.Column, { IsHit: true });

// Update computer state
If(
    ComputerMove.HasShip,
    Set(ComputerState, { HitState: "Target", LastHit: ComputerMove }),
    Set(ComputerState, { HitState: "Hunt", LastHit: Blank() })
);

// Check for sunk ships
If(
    IsBlank(Filter(Board, HasShip && !IsHit)),
    Set(ComputerState, { HitState: "Hunt", LastHit: Blank() })
);

Then I asked the following question: "In Battleship code using Power Fx, how do I make sure that the computer keeps moving along a line after a hit until the ship gets destroyed?"

And this was the output:

// Variables to track the game state
Set(LastHit, {x: 4, y: 3}); // Example starting point after a hit
Set(HitDirection, Blank()); // Initially, no direction is confirmed
Set(RemainingHits, 3); // Assume the ship has 3 parts left

// Function to continue attacking in a direction
If(
    !IsBlank(HitDirection),
    // Continue in the current direction
    Switch(
        HitDirection,
        "Horizontal", If(Board[LastHit.x + 1, LastHit.y] = "Ship", FireShot(LastHit.x + 1, LastHit.y)),
        "Vertical", If(Board[LastHit.x, LastHit.y + 1] = "Ship", FireShot(LastHit.x, LastHit.y + 1))
    ),
    // Determine direction after the first hit
    If(
        Board[LastHit.x + 1, LastHit.y] = "Ship",
        Set(HitDirection, "Horizontal"); FireShot(LastHit.x + 1, LastHit.y),
        Board[LastHit.x, LastHit.y + 1] = "Ship",
        Set(HitDirection, "Vertical"); FireShot(LastHit.x, LastHit.y + 1)
    )
);

// Update variables after each shot
If(
    FireShot(x, y) = "Hit",
    Set(LastHit, {x: x, y: y}); Set(RemainingHits, RemainingHits - 1),
    // If a miss, switch direction if possible
    If(HitDirection = "Horizontal", Set(HitDirection, "Vertical"), Set(HitDirection, "Horizontal"))
);

// Reset when the ship is destroyed
If(RemainingHits = 0, ResetVariables());

And then I asked one final question:

If while going vertically up, the computer misses after a few hits, how do I make sure the computer now tries to go down vertically along the same line?

And the response was:


// Define the board size
Set(BoardWidth, 10);
Set(BoardHeight, 10);

// Function to fire at a cell and update state
Set(CurrentCell, {x: LastHit.x, y: LastHit.y});
If(
    // Check if the next cell in the current direction is within bounds and not already targeted
    (HitDirection = "Up" && CurrentCell.y > 1 && !LookUp(CheckedCells, x = CurrentCell.x && y = CurrentCell.y - 1)) ||
    (HitDirection = "Down" && CurrentCell.y < BoardHeight && !LookUp(CheckedCells, x = CurrentCell.x && y = CurrentCell.y + 1)),

    // Fire in the current direction
    If(
        HitDirection = "Up",
        Set(NextCell, {x: CurrentCell.x, y: CurrentCell.y - 1}),
        Set(NextCell, {x: CurrentCell.x, y: CurrentCell.y + 1})
    );

    // Check the result of the shot
    If(
        FireShot(NextCell.x, NextCell.y) = "Hit",
        Set(LastHit, NextCell); Collect(CheckedCells, NextCell),
        // If it's a miss, reverse direction
        If(
            HitDirection = "Up",
            Set(HitDirection, "Down"),
            Set(HitDirection, "Up")
        )
    ),

    // If out of bounds or already targeted, reverse direction
    If(
        HitDirection = "Up",
        Set(HitDirection, "Down"),
        Set(HitDirection, "Up")
    )
);

// If the ship is destroyed, reset the state
If(RemainingHits = 0, ResetVariables());

As you can see, some of the code is just not right and/or doesn't follow any of the coding standards that everyone should. BUT, the logic is pretty well laid out. It is good enough to give you some direction or to get you started on the right path.

Making computer layout ships randomly

Similar to the above point, ChatGPT laid out the logic well but didn't generate working code. In fact, for this one, I had the longest back and forth I have ever had with ChatGPT. It will not be practically possible to share all of that code here but I will share a snippet that shows where it was falling short. It kept generating code snippets that had functions that aren't there at all in Power Fx.

Clear(colComputerBoard);
ForAll(
    Sequence(10) As Value1,
    ForAll(
        Sequence(10) As Value2,
        Collect(
            colComputerBoard,
            {
                Row: Value1.Value,
                Column: Value2.Value,
                IsHit: false,
                HasShip: false
            }
        )
    )
);

// Ship sizes
ClearCollect(
    colShipSizes,
    {Size: 5},
    {Size: 4},
    {Size: 3},
    {Size: 3},
    {Size: 2}
);

// Maximum number of attempts to place each ship
ClearCollect(
    colMaxAttempts,
    {MaxAttempts: 10}
);

// Initialize placement attempts collection
ClearCollect(colPlacementAttempts, 
    {ShipIndex: 1, Attempts: 0, ShipSize: 5, ShipPlaced: false},
    {ShipIndex: 2, Attempts: 0, ShipSize: 4, ShipPlaced: false},
    {ShipIndex: 3, Attempts: 0, ShipSize: 3, ShipPlaced: false},
    {ShipIndex: 4, Attempts: 0, ShipSize: 3, ShipPlaced: false},
    {ShipIndex: 5, Attempts: 0, ShipSize: 2, ShipPlaced: false}
);

// Place ships with retry logic
ForAll(
    colShipSizes As ThisSize,
    With(
        {
            varMaxAttempts: First(colMaxAttempts).MaxAttempts,
            varShipPlaced: false,
            varShipSize: ThisSize.Size,
            varAttempts: 0
        },
        // Retry logic: Attempt placement up to `MaxAttempts` times
        While(
            varAttempts < varMaxAttempts && !varShipPlaced,
            With(
                {
                    varOrientation: First(
                        Shuffle(
                            [
                                "horizontal",
                                "vertical"
                            ]
                        )
                    ).Value,
                    varStart: First(
                        Shuffle(
                            Filter(
                                colComputerBoard,
                                !HasShip && 
                                ((varOrientation == "vertical" && Row <= (10 - ThisSize.Size)) || 
                                 (varOrientation == "horizontal" && Column <= (10 - ThisSize.Size))) ) ) ), varNumberofCells: ThisSize.Size }, If( varStart, If( varOrientation == "horizontal", If( CountRows( Filter( colComputerBoard, Row = varStart.Row && Column >= varStart.Column && Column < (varStart.Column + varNumberofCells) && !HasShip ) ) = varNumberofCells, // Place the ship ForAll( Sequence(varNumberofCells) As ThisValue, UpdateIf( colComputerBoard, Row = varStart.Row && Column = (varStart.Column + ThisValue.Value), {HasShip: true} ) ), // Update attempts and retry if placement fails Patch( colPlacementAttempts, LookUp(colPlacementAttempts, ShipSize = varShipSize), {Attempts: varAttempts + 1} ) ), If( CountRows( Filter( colComputerBoard, Column = varStart.Column && Row >= varStart.Row && Row < (varStart.Row + varNumberofCells) && !HasShip
                                )
                            ) = varNumberofCells,
                            // Place the ship
                            ForAll(
                                Sequence(varNumberofCells) As ThisValue,
                                UpdateIf(
                                    colComputerBoard,
                                    Row = (varStart.Row + ThisValue.Value) && Column = varStart.Column,
                                    {HasShip: true}
                                )
                            ),
                            // Update attempts and retry if placement fails
                            Patch(
                                colPlacementAttempts,
                                LookUp(colPlacementAttempts, ShipSize = varShipSize),
                                {Attempts: varAttempts + 1}
                            )
                        )
                    ),
                    // If placement is successful, mark the ship as placed and stop retrying
                    Patch(
                        colPlacementAttempts,
                        LookUp(colPlacementAttempts, ShipSize = varShipSize),
                        {ShipPlaced: true}
                    )
                )
            )
        )
    )
);

// Optional: Check placement attempts
ClearCollect(
    colPlacementAttemptsReport,
    Filter(colPlacementAttempts, ShipPlaced = false)
);

I kept highlting that While isn't a function in Power Fx and it kept on apologizing yet still generating new code with the same function, or another one that doesn't exist in Power Fx.

Other ways ChatGPT helped

I used ChatGPT to get all the images I have used in the app - app logo, home screen background, background for other screens, game over & ship destroyed graphics. It also gave me some inspiration for screen layouts. I am not creative when it comes to UI, so all of this was super helpful and I am thankful to ChatGPT to have made this easier for me.

Conclusion

As you can see, ChatGPT isn't the best in generating out correct Power Fx code but is pretty good in churning out logic and explaining the approach. I haven't tested ChatGPT for any other coding language but I would think it will do better than what it does with Power Fx. All of this, to me, means more good than bad. Good because it can help you get some ideas for a complex piece of logic that you might be struggling with. But, at the same time, isn't smart enough (yet!) to write it all by itself and replace you!

Recent articles

    1. Have you played Battleship? In Power Apps?
    2. How to fix a weird behavior with search function?
    3. How to choose between the different types of Power Apps

 

 

Leave a Reply