Store 3-dimensional char array in program memory with PROGMEM
Here you have:

const char * const rules [16][5] PROGMEM ={ 


One const refers to the char* part. It means the pointer value doesn't change.

The other const refers to what it is pointed by. It means the thing this pointer points doesn't change (but the pointer itself can change).

In your sketch the thing that get put into PROGMEM is the pointer array; the texts themselves are still in RAM. Have a look at PROGMEM page and  study this example:

const char string_0[] PROGMEM = "String 0";   // "String 0" etc are strings to store - change to suit.
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";


// Then set up a table to refer to your strings.

const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};


You need to declare each string by itself, and later put them in an array.

As you can see, pointer are inescapable.

Edit: The F() Macro

Here is a proof of concept of how to manage an array of string (lowercase "s") and strings constants in PROGMEM:

const char uno[] PROGMEM = "1Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char dos[] PROGMEM = "2Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char tre[] PROGMEM = "3Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char cua[] PROGMEM = "4Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char cin[] PROGMEM = "5Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";

const char * const rules [3][2] = { 
 {uno, dos},
 {tre, cua},
 {cua, cin}
};

void setup() {
  Serial.begin(115200);
  Serial.println(F("PROGMEM string test"));
  Serial.println(F("-------------------"));
  for(int i = 0; i &lt; 3; i++){
    for(int j = 0; j &lt; 2; j++){
      Serial.print(i); Serial.print(" "); Serial.print(j); Serial.print(" ");
     Serial.println((const __FlashStringHelper *) rules[i][j]); 
    }
  }
}

void loop() {
}


Use the F() macro to put constant strings in PROGMEM. Otherwise, the strings will live in RAM. Are running short of RAM? Put the F() macro around those pesky strings and send them out of your RAM.