Perl, 293 bytes
-9 bytes thanks to @Dom Hastings
{$==7+rand 30;@r=$"=();@a=((C)x4,(E)x3,("#")x1369,(" ")x1369);for$i(0..7+rand 30){$r[$i][$_]=splice@a,rand@a,1for 0..$=}$r[0][$=]=F;$r[-1][0]=P;$_=$r=join$/,$v="#"x($=+=3),(map"#@$_#",@r),$v;1while$r=~s/F(.{$=})?[^#F]/F$1F/s||$r=~s/[^#F](.{$=})?F/F$1F/s;$r!~/[CEP]/&&/C.*C/s&&/E/?last:redo}say
Add -E
flag to run it:
perl -E '{$==7+rand 30;@r=$"=();@a=((C)x4,(E)x3,("#")x1369,(" ")x1369);for$i(0..7+rand 30){$r[$i][$_]=splice@a,rand@a,1for 0..$=}$r[0][$=]=F;$r[-1][0]=P;$_=$r=join$/,$v="#"x($=+=3),(map"#@$_#",@r),$v;1while$r=~s/F(.{$=})?[^#F]/F$1F/s||$r=~s/[^#F](.{$=})?F/F$1F/s;$r!~/[CEP]/&&/C.*C/s&&/E/?last:redo}say'
However, it takes a long time to run, so I recommend using this version instead:
perl -E '{${$_}=8+rand 30for"=","%";@r=$"=();@a=((C)x4,(E)x3,("#")x($v=rand $=*$%),(" ")x($=*$%-$v));for$i(0..$%-1){$r[$i][$_]=splice@a,rand@a,1for 0..$=-1}$r[0][$=-1]=F;$r[$%-1][0]=P;$_=$r=join$/,$v="#"x($=+=2),(map"#@$_#",@r),$v;1while$r=~s/F(.{$=})?[^#F]/F$1F/s||$r=~s/[^#F](.{$=})?F/F$1F/s;$r!~/[CEP]/&&/C.*C/s&&/E/?last:redo}say'
Try it online!
Explanation
{ # enter a block (which is used as a loop)
{$==7+rand 30; # randomly select the width of the map -2
# (-2 because we don't include the borders yet)
@r=$"=(); # reset @r, and set $" to undef
@a=( # create a list of the character that can be on the board
(C)x4, # 4 coins 'C'
(E)x3, # 3 enemies 'E'
("#")x1369, # 37*37 '#'
(" ")x1369); # 37*37 spaces
for$i(0..7+rand 30) # create the 2D map (7+rand 30 is the height, which is generated just now)
for$_(0..$=-1){
$r[$i][$_]= # index [$i][$_] receives ...
splice@a,rand@a,1 # .. a random character from the list previously generated
# (the character is then removed from the list thanks to 'splice')
}
}
$r[0][$=]=F; # add the finish cell
$r[-1][0]=P; # add the start cell
$_=$r= # here we generate a string representation of the map
join$/, # join the following elements with newlines
$v="#"x($=+=3), # a first line of # only
(map"#@$_#",@r), # add a # to the beginning and the end of each line
$v; # the last line of #
1while # the following regex will replace every accessible cell with a F
$r=~s/F(.{$=})?[^#F]/F$1F/s # a cell on the right or the bottom of a F cell is replaced
|| # or
$r=~s/[^#F](.{$=})?F/F$1F/s; # a cell on the left or the top of a F cell is replaced
$r!~/[CEP]/ # if there is no C, E or P on the map (meaning they were all accessible)
&& /C.*C/s # and there are at least 2 coins
&& /E/ ? # and 1 enemy
last: # the the map is valid, we exit the loop
redo # else, start over
}
say # and print the board
It takes a long time to run, because the list from which we randomly pick the characters to put on the board (@a
) contains 1369 whitespaces and #
, and only 4 coins and 3 enemies. So if the size of the width and height are small, there are a lot of spaces and #
compared to the coin and the enemies, so it's quite likely that a random map won't be valid. That's why the "optimized" version is faster: the list from which we pick the characters is just a little bigger than the map (the list is @a=((C)x4,(E)x3,("#")x($v=rand $=*$%),($")x($=*$%-$v))
: a random number $v
of #
(inferior to the size of the map), and size of the map - $v
whitespaces).