SAP Builders Blog Posts
Learn from peers about their low-code journey and write your own blog posts to share your thoughts and experiences as you become an SAP Builder.
cancel
Showing results for 
Search instead for 
Did you mean: 
Dan_Wroblewski
Developer Advocate
Developer Advocate
0 Kudos

As you may know, I love games. I subscribe to Games magazine and do their 2 cryptic crosswords religiously. And I also like to enter the contests of various kinds (with nominal prizes of $100 or free subscriptions).

In the latest issue of Games magazine, there was this contest (note that the deadline is Oct. 31, so there is still plenty of time for you to enter):

contest-numbered.png

What you have to do is quite simple:

  1. Find the code, which is a basic substitution cypher where each original letter in a word is changed to a different letter.
  2. Decode the crossword clues.
  3. Find pairs of Across clues (or pairs of Down clues) that have the same answer, add the numbers of those two clues, and enter the answer in the grid with that sum.
  4. Finally, take the answer in the indicated answer (answer Across #33), run the cypher on that answer to get the answer to the contest.

dan_wroblewski_0-1695907217677.png

If you are not interested in solving the puzzle but want to learn helpful SAP Build Apps tips, you can skip to the SAP Build Apps Tips section for 3 helpful tips for SAP Build Apps developers.

dan_wroblewski_0-1695907217677.png

I have made the 2 projects available:

If you need help importing, just add a note to this blog. Here is the app in action.

 

How I Built My App

I wanted the app to keep track of the cypher as I was determining which letters were represented by what other letters. For example, you see that some words have at the end 'W, meaning that probably W = S, and the app needed to store this substitution. 

When I got all the clues and the answers, I need to store which answers to pair up, so I needed a place to store this, too. So I created a backend project with 2 entities in my Visual Cloud Functions project.

dan_wroblewski_0-1695804428620.png

  • Code: Source is the current letter, Target is th eletter is represents.
  • Pair: FirstClue and SecondClue are the clues that contain the same answer, and Answer is the answer to enter into the grid.

 

Front End

I set up my app page into 3 parts section.

dan_wroblewski_0-1695806801132.png

Left Side (Cypher)

 Here's where I have a simple text box which represents the letter that appears in the encrypted words, and next to it is a dropdown box where I can select the real letter this letter represents. Everytime I change the letter I do the following:

  1. I update the local variable that holds the cypher with the formula:
    • SET_ITEM(appVars.Code, item.source == repeated.current.source, {id: repeated.current.id, source: repeated.current.source, target: self.value} ) 
  2. I update the entity in the backend with the formula to find the ID for this letter in the backend:
    • FIND(appVars.Code, item.source == repeated.current.source).id:

Middle (Encrypted Clues)

Here, I simply display the encrypted clues, then displayed the clues with dashes instead and any letters I've found. I used the following formula to display the unencrypted words:

JOIN(MAP<mapitem>(SPLIT(repeated.current.Word,""),IF(NOT(CONTAINS(appVars.Letters,mapitem)),mapitem,IF(IS_EMPTY(FIND(appVars.Code, mapitem == item.source).target),"-",FIND(appVars.Code, mapitem == item.source).target))),"") 

For example, if I change A to represent T, then the list of words automatically update like so:

dan_wroblewski_1-1695807402149.png

Right (Paired clues)

Essentially this displays the data from the other entity, Pairs.

I click on the 2 clues I would like to pair up – say 2 and 3 – and type in "Step" for the answer.

dan_wroblewski_0-1695905655446.png

Then I click Add. The pair is added to backend entity, I display a list of pairs (included a delete button to remove them), and I shade the clues that have already been selected.

dan_wroblewski_1-1695905704294.png

You can see in the backend the record was added:

dan_wroblewski_2-1695905809176.png

And finally, the app automatically checks if the pair add up to 33 (and is across) and if it is, it tries to decrypt the word based on the existed cypher.

dan_wroblewski_3-1695906401684.png

Improvements

I could make the following improvements, which would be pretty easy:

  • Make the whole thing case-insensitive.
  • Check if I have already selected a given letter, and even reduce the dropdown to only those letters remaining.
  • Clean up some of the layouting.
  • Add a puzzle grid (any ideas here, let me know).
  • And, of course, add AI (but I think that would take the fun out 😄).

SAP Build App Tips

OK, it was a fun project, but I still learned some really cool things about SAP Build Apps that I wanted to pass on to you.

Nested Functions in Formula

The function I used to take the cypher and decrypt the words involved nested functions, that both used the built-in "item" object, so I at first had trouble getting the function to work.

 

 

JOIN(MAP<mapitem>(SPLIT(repeated.current.Word,""),
IF(NOT(CONTAINS(appVars.Letters,mapitem)),mapitem,
IF(IS_EMPTY(FIND(appVars.Code, mapitem == item.source).target),"-",FIND(appVars.Code, mapitem == item.source).target))),"") 

 

 

The function does the following:

  1. It takes the word to decrypt, and SPLIT function makes it an array of characters.
  2. The MAP function iterates over the list of characters.
  3. For each character it checks with the CONTAINS function if it's alphabetic (contained in the constant "ABCD..." stored in an app variable). If it's not alphabetic, then it returns whatever it found (generally punctuation).
  4. IF it is alphabetic, we use the FIND function to see what the decrypted letter is, if it was yet determined. If not, a dash is returned.

But we had a problem in the FIND function, because it uses a built-in item object to determine the field to check, but we want to check that against the item object we got from the MAP function. So we used the prefix <mapitem> to set the name of the object in the MAP function to something different, and now we could compare the 2 values.

Style By Formula

I wanted to highlight the clues when they were used in one of the pairs. So I edited the style, and created a new "Local Palette" color for the Background color property.

dan_wroblewski_0-1695908150865.png

For the color, I made it the following formula:

 

 

IF(IS_IN_ARRAY(CONCAT(PLUCK(appVars.Pairs,"firstClue"),
PLUCK(appVars.Pairs,"secondClue")), repeated.current.Number),"#777777","#FFFFFF")

 

 

The formula does the following:

  • I retrieved the list of paris from the backend and put in appVars.Pairs.
  • With PLUCK, I created an array of numbers from all the firstClue fields and a list from the secondClue fields. 
  • I used CONCAT to merge the to lists.
  • I used IS_IN_ARRAY to check whether the current clue number was in the combined list. If yes, shaded, and if not, white. 

Layout Spacing

I worked quite a bit on spacing of the app, especially padding. IN a container, there are 2 places for managing spacing, especially with Container components:

  • LAYOUT TAB: The space between components manages the space around the current component, next to other components.

 dan_wroblewski_1-1695908623272.png

  • STYLE TAB: The padding managed the space inside the container, the padding like in an HTML table. dan_wroblewski_2-1695908797561.png
  • In addition, each component within a container can define its own extra space for itself.

This may be obvious, but I still get confused how to manage spacing, since there are many places to manage it, and there are additional properties (e.g., alignment, rows, cells, width, position) that will affect how the whole application lines up.

Also, in the project, note the use of containers to enable greater flexible in controlling spacing.

Happy building 🏗😺