File: libpuzzle.xml

package info (click to toggle)
enigma 1.30%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 76,132 kB
  • sloc: xml: 162,251; cpp: 67,393; ansic: 28,606; makefile: 1,986; sh: 1,298; yacc: 288; perl: 84; sed: 16
file content (774 lines) | stat: -rw-r--r-- 19,312 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<el:level xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://enigma-game.org/schema/level/1 level.xsd" xmlns:el="http://enigma-game.org/schema/level/1">
  <el:protected>
    <el:info el:type="library">
      <el:identity el:title="" el:id="lib/libpuzzle"/>
      <el:version el:score="1" el:release="1" el:revision="0" el:status="released"/>
      <el:author  el:name="Raoul Bourquin" el:email="" el:homepage=""/>
      <el:copyright>Copyright © 2005, 2006 Raoul Bourquin</el:copyright>
      <el:license el:type="GPL v2.0 or above" el:open="true"/>
      <el:compatibility el:enigma="0.92">
      </el:compatibility>
      <el:modes el:easy="false" el:single="false" el:network="false"/>
      <el:comments>
        <el:code>Lua 5.1 and XML converted by Leveladministrators</el:code>
      </el:comments>
      <el:score el:easy="-" el:difficult="-"/>
    </el:info>
    <el:luamain><![CDATA[
-- libpuzzle, a library for enigma
-- Version 0.97

-- This is a lua-library to make it really easy to set up random puzzles.

-- Use this lib this as showed:
-- Save the .lua in /PFAD/TO/ENIGMA/data/levels/lib/libpuzzle.lua
-- On the beginning of your level, just include:
-- Require("levels/lib/libpuzzle.lua")
-- Now you can use every function here, but usually you would just call "puzzle(YOUR OPTIONS)"
-- But you can influence the puzzle() by setting the values in the WORLD section manually to different values.

-----------------------------------
-- User's Reference with Example --
-----------------------------------
--Example to generate an shuffled Ring with 8 Stones (the red ones) at the Position 4/5:
--puzzle({{1,1,1},{1,0,1},{1,1,1}},2,4,"red","yes")

-- in [], this are the values from the example above.

--original_atrix: this is the abstract definition of the puzzle:
--###
--# # this ring has the matrix: {{1,1,1},{1,0,1},{1,1,1}}
--###
--The format is: {row1, row2, row3, ...}
--where row1 is:{stone1, stone2, ston3, ...}
--Pseudo Pieces:
--If you want to set a pseudo piece, you set a "2" in the matrix. This piece will not appear in the level. But it will
--influence the others in this way, that where a pseusdo piece is, the "normal" pieces around it will have connections.
--This is the way to generate open clusters.
--[{{1,1,1},{1,0,1},{1,1,1}}]

--xtopleftcorner/ytopleftcorner or xcorner/ycorner: the absolute coordinates of the top left corner of your puzzle-matrix.
--It's not required that this is really a stone. 
--[2 and 4]

--puzzle_kind: this string describes if we use the blue or the red puzzle stones
--the values are: "blue" for blue and "red" for red ! 
--["red"]

--shuffle: this string says, if the puzzle must be shuffled or not.
--the values are "yes" and "no".
--["yes"]

--now, the syntax for a puzzle is:
--puzzle(original_matrix, xtopleftcorner, ytopleftcorner, puzzle_kind, shuffle)

--if you want to configure the lib for a level, may be change the shuffle algorithm, use the variables in the WORLD section... 

-- it's easy, isn't it ?

-------------------------------------
-- Programmer's Variable Reference --
-------------------------------------
--List of the main globals:
--DON'T change this values directly!
--matrix:  original_matrix
--matrix:  matrix
--matrix:  teile_matrix
--array:   teile={}
--array:   shuffled_pieces={}
--matrix:  stone_coordinates={{},{}}
--array:   xpermutations={}
--array:   ypermutations={}

--original_matrix: This matrix contains the original values, given in puzzle()
--in acts as a backup, it is never changed or used.
--just after calling puzzle() the values will be written in matrix.

--shuffled_pieces: this array keeps the mixed descriptions. 
--[no values, it's random!]

--stone_coordinates: this 2D Array keeps the coordinates of the stones.
--Format: {{X-Values},{Y-Values}}
--{{x-first-stone, xsecond-stone, ...},{y-first-stone, y-second-stone, ...}}
--[{{2,3,4,2,4,2,3,4},{4,4,4,5,5,6,6,6}}]

--teile: this array keeps all strings that describes the different stones used. 
--[{"es","ew","sw","ns","ns","ne","ew","nw"}]

--teile_matrix: this matrix stores the teile at their places.
--neede to shuffle with permutation.

--anz:stones: the number of stones needed.

--TODO:
-->clear the variables, locals, globals...use same names for same local vars, eg. temp,tmp...(half done)
-->release libpuzzle 1.0


----------------------------
--BEGINN OF LIBPUZZLE CODE--
---------------------------------------------------------------
--WORLD SECTION:
--This are global variables. They determine the exact behavior of the puzzle function.
--This values, you can use to configure the lib in your level.
--Just set the variable to the desired value, before you call puzzle().
--Then, your values will be kept until you change them again!

--must we shuffle the pieces or not ?
--1 means true, 0 means false.
--overwrite this in your level to get already solved puzzles.
must_shuffle=1

--which method to shuffle:
--"random" or "permutation"
--not yet used
shuffle_method="permutation"

--with how many permutations we shuffle:
--for bigger puzzles, take bigger values!
--this value is just the base, the real value (num_perm_todo) is calculated this way:
--num_perm_todo=num_perm+random(1,num_perm)
num_perm=10

--this value varies the real num_perm_todo
--without that, you would get everytime the same result, when you shuffle a one-line puzzle...
--do not calculate this value here, calc it in the wrapper function puzzle()
--num_perm_to_add=random(1,num_perm)

--is it allowed to generate "open" clusters ?
--yes=1 no=0
--not yet used, probably this will never be used...
open_cluster=1 

--Default value for the kind of puzzles:
--only used, if nothing given as parameter of puzzle()
art="2"

---------------------------------------------------------------
--HELPERFUNCTIONS:
---------------------------------------------------------------
--Determine the length of an array:
function arraydim(array)
 local i=1
 local array_length=0

 while array[i]~=nil do
  i=i+1
 end

 array_length=i-1
 return array_length
end

--copy a matrix a to a matrix b:
function copy_matrix(matrix1,matrix2)

 local matrix2={}
 local matrdim1=arraydim(matrix1)
 local matcdim1=arraydim(matrix1[1])
 local i=1
 local j=1

 for i=1,matrdim1 do
  matrix2[i]={}
  for j=1,matcdim1 do
   matrix2[i][j]=matrix1[i][j]
  end
 end

 return matrix2
end

--rewrite a matrix:
--DANGER, this changes the matrix in irrevocably!
--there will be a loss of information!
function rewrite_matrix(matrix)

 local rdim1=arraydim(matrix)
 local cdim1=arraydim(matrix[1])
 local i=1
 local j=1

 for i=1,rdim1 do
  for j=1,cdim1 do
   if matrix[i][j]==2 then
    matrix[i][j]=0
   end
  end
 end
 return matrix
end

---------------------------------------------------------------
--WRAPPER:
--The "normal" User of libpuzzle would call this function only.
function puzzle(original_matrix, xtopleftcorner, ytopleftcorner, puzzle_kind, shuffle)

 --make a copy of the original Matrix to work on.
 --this way there are no problem when changing the matrix and recalling puzzle() without regenerating the original matrix.
 matrix=copy_matrix(original_matrix,matrix)

 --argument parser:
 if puzzle_kind ~= nil then
  if puzzle_kind=="blue" then
   art=""
  elseif puzzle_kind=="red" then
   art="2"
  end
 end

 if shuffle ~= nil then
  if shuffle=="yes" then
   must_shuffle=1
  elseif shuffle=="no" then
   must_shuffle=0
  end
 end

 --call the matrix2places to get the real locations of the puzzlestones:
 matrix2places(matrix, xtopleftcorner, ytopleftcorner)

 --call the which_piece to determine the pieces we will need:
 which_piece(matrix)

 --shuffle the pieces?
 if must_shuffle==1 then
   --which method to shuffle?
   if shuffle_method=="random" then
     puzzle_shuffle(teile)
   elseif shuffle_method=="permutation" then
     --determine the number of permutations to use:
     num_perm_to_add=random(1,num_perm)
     num_perm_todo=num_perm+num_perm_to_add
     --cal the shuffle main method:
     shuffle_pieces_with_permutations(matrix,teile,num_perm_todo)
   end
 elseif must_shuffle==0 then
  --to get a shuffled_pieces array (Only necessary because of the arrayname). But the pieces are NOT shuffled.
  shuffled_pieces=teile
 end

 --draw the puzzle
 draw_pieces(stone_coordinates, shuffled_pieces, art)

 return 0
end

---------------------------------------------------------------
--INPUT_PARSER:

--Determine the real coordinates of the stones
function matrix2places(matrix,xcorner,ycorner)

 --new global:
 stone_coordinates={{},{}}

 local i,j
 local counter=1

 local rdim=arraydim(matrix)
 local cdim=arraydim(matrix[1])

  for i=1,rdim do
   for j=1,cdim do
    if matrix[i][j]==1 then
     stone_coordinates[1][counter]=xcorner+j-1
     stone_coordinates[2][counter]=ycorner+i-1
     counter=counter+1
    end
   end
  end

 --number of stones:
 anz_stones=arraydim(stone_coordinates[1])

 return stone_coordinates,anz_stones
end

---------------------------------------------------------------
--Determine the kind of the stones
function which_piece(matrix)

 --new global:
 teile={}

 local rdim=arraydim(matrix)
 local cdim=arraydim(matrix[1])
 local i,j
 local oben=""
 local links=""
 local unten=""
 local rechts=""
 local counter=1

 for i=1,rdim do
  for j=1,cdim do
   if matrix[i][j]==1 then

      if i==1 then
       oben=""
       if rdim>1 then
        unten=tests(matrix,j,i)
       end 
      elseif i==rdim then
       unten=""
       if rdim>1 then
        oben=testn(matrix,j,i) 
       end
      else
       oben=testn(matrix,j,i) 
       unten=tests(matrix,j,i) 
      end 
 
      if j==1 then
       links=""
       if cdim>1 then
        rechts=teste(matrix,j,i) 
       end
      elseif j==cdim then
       rechts=""
       if cdim>1 then
        links=testw(matrix,j,i) 
       end
      else
       rechts=teste(matrix,j,i) 
       links=testw(matrix,j,i) 
      end  

      -- To get a valid Stone if no neighbours were present, we define it as the (untunneled) cross ("nesw")
      if oben=="" and rechts=="" and unten=="" and links=="" then
       oben="n"
       rechts="e"
       unten="s"
       links="w"
      end

      teile[counter]=oben..rechts..unten..links
      counter=counter+1
   end
  end
 end

 --call the rewrite_matrix, because from now on, we wont have the disturbing "pseudo_pieces":
 --we ONLY use them to get a "special" teile Array, which will produce an "open" puzzle cluster.
 rewrite_matrix(matrix)

 return teile
end

---------------------------------------------------------------
--Helperfunction for testing the required connection of a puzzlestone:
function testn(matrix,posx,posy)
 if matrix[posy-1][posx]==1 or matrix[posy-1][posx]==2 then
  return "n"
 else
  return ""
 end
end

function teste(matrix,posx,posy)
 if matrix[posy][posx+1]==1 or matrix[posy][posx+1]==2 then
  return "e"
 else
  return ""
 end
end

function tests(matrix,posx,posy)
 if matrix[posy+1][posx]==1 or matrix[posy+1][posx]==2 then
  return "s"
 else 
  return ""
 end
end

function testw(matrix,posx,posy)
 if matrix[posy][posx-1]==1 or matrix[posy][posx-1]==2 then
  return "w"
 else
  return ""
 end
end

---------------------------------------------------------------
--RANDOM SHUFFLE
--shuffle the array teile, this is the classical method.
--it's not guaranted to get a solvable puzzle every time!
function puzzle_shuffle(teile)
 
 shuffled_pieces={}

 local restteile={}
 local anz=anz_stones
 local zyklen=anz
 local i,j,k
 local counter=1
 local aktteil

 for i=1,zyklen do

  --shuffle pieces:
  t=random(1,anz)
  
  aktteil=teile[t]
  shuffled_pieces[counter]=aktteil
  counter=counter+1

  --prepare the teile array for next cycle
  local restteile={}
  local schogse=0

  --copy the teile array, mark the piece we just have used with "0"
  for k=1,anz do
   if teile[k]==aktteil and schogse==0 then
    restteile[k]="0"
    schogse=1
   else
    restteile[k]=teile[k]
   end
  end

  --clear teile array:
  teile={}
  local t=1

  for j=1,anz do
   if restteile[j]~="0" then
    teile[t]=restteile[j]
    t=t+1
   end 
  end
  
  --we have used one piece:
  anz=anz-1
 end

end
---------------------------------------------------------------
--PERMUTATION-SHUFFLE:
--here you will get a solvable Puzzle EVERY time!
function analyzerow(matrix,row)

 --new global:
 xpermutations={}

 --Zählcountereter zum durchlaufen der Reihe:
 local counter = 1

 --Counter to count the number of Permutations:
 local perm_counter = 0
 local local_counter
 local local_length
 local array=matrix[row]
 local length=arraydim(array)

 while counter<=length do

  --If MatrixPlatz is zero, do nothing
  if array[counter]==0 then

   counter=counter+1

  --Get the length of the Ones-Sequenz
  else
   
   local_counter = 0
   local_length = 0

   --Schaue wie lang eine Reihe ist:
   while array[counter+local_counter]==1 do
    local_counter=local_counter+1
    local_length = local_length +1
   end
   
   --big IF, is it a Permutation or not?
   if local_length >= 2 then

    --Yes, increase the number of Permutations by 1:
    perm_counter = perm_counter + 1

    --add an array to the permutation-array:
    xpermutations[perm_counter]={}

    --start
    xpermutations[perm_counter][1]={}
    --end
    xpermutations[perm_counter][2]={}

    --set the beginning:
    --ROW value 
    xpermutations[perm_counter][1][1]=row
    --COL value
    xpermutations[perm_counter][1][2]=counter

    --set end:
    --ROW value
    xpermutations[perm_counter][2][1]=row
    --COL value
    xpermutations[perm_counter][2][2]=counter+local_length-1

   end
   
   --jump just after the sequence of "1":
   counter=counter+local_length

  end
 end

end

--------------------------------------------------------------------
function analyzecol(matrix,col)

 --new global:
 ypermutations={}

 --Zählcountereter zum durchlaufen der Reihe:
 local counter = 1

 --countereter der die anz Permutationen zählt:
 local perm_counter = 0
 local local_counter
 local local_length
 local length=arraydim(matrix)
 local array={}

 --make a valid array
 for i=1,length do
  array[i]=matrix[i][col]
 end

 while counter<=length do
  
  --Wenn der MatrixPlatz null ist, tue nichts
  if array[counter]==0 then

   counter=counter+1

  --Finde heraus, wie lang die Einsen-Sequenz ist
  else
   
   local_counter = 0
   local_length = 0

   --Schaue wie lang eine Reihe ist:
   while array[counter+local_counter]==1 do
    local_counter=local_counter+1
    local_length = local_length +1
   end
   
   --grosse IF entscheidung:
   if local_length >= 2 then

    --erhöhe die anzahl der gefundenen Permutationen um 1:
    perm_counter = perm_counter + 1

    --verlängere den Permutationsarray um einen nuenen array:
    ypermutations[perm_counter]={}

    --anfang
    ypermutations[perm_counter][1]={}
    --ende
    ypermutations[perm_counter][2]={}

    --setze anfang:
    --Reihenwert des Anfangs
    ypermutations[perm_counter][1][1]=counter
    --Spaltenwert des Anfangs
    ypermutations[perm_counter][1][2]=col

    --setze ende:
    --Reihenwert des endes
    ypermutations[perm_counter][2][1]=counter+local_length-1
    --Spaltenwert des endes
    ypermutations[perm_counter][2][2]=col

   end
   
   --springe gerade nach die einersequenz:
   counter=counter+local_length

  end
 end
 
end

--------------------------------------------------------------------------
function find_all_permutations(matrix)

 permutations={}
 number_of_permutations=1

 local rdim=arraydim(matrix)
 local cdim=arraydim(matrix[1])
 local i,j,k

 --search all permutations:
  --the x ones:
  for i=1,rdim do

   analyzerow(matrix,i)

   local temp=arraydim(xpermutations)

   --write the permutations to the global store:
   for k=1,temp do
    permutations[number_of_permutations]=xpermutations[k]
    number_of_permutations=number_of_permutations+1
   end
  end

  --the y ones:
  for i=1,cdim do

   analyzecol(matrix,i)
   local temp=arraydim(ypermutations)

   --write the permutations to the global store:
   for k=1,temp do
    permutations[number_of_permutations]=ypermutations[k]
    number_of_permutations=number_of_permutations+1
   end
  end

  --Because counter has initial value 1, we have count one permutation to much:
  number_of_permutations=number_of_permutations-1

  return permutations
end

--------------------------------------------------------------------------
function use_permutation(teile_matrix,permutations,n)

 local p=permutations[n]
 local rbeg=p[1][1]
 local cbeg=p[1][2]
 local rend=p[2][1]
 local cend=p[2][2]

 local temp = teile_matrix[rend][cend]

 if rbeg==rend then
  --horizontal
  local t=1

  while cend-t>=cbeg do
   teile_matrix[rbeg][cend-t+1]=teile_matrix[rbeg][cend-t]
   t=t+1
  end
  teile_matrix[rbeg][cbeg]=temp

 elseif cbeg==cend then
  --vertical
  local t=1

  while rend-t>=rbeg do
   teile_matrix[rend-t+1][cbeg]=teile_matrix[rend-t][cbeg]
   t=t+1
  end
  teile_matrix[rbeg][cbeg]=temp

 end

 return teile_matrix
end
--------------------------------------------------------------------------
function teile2teile_matrix(matrix,teile)

 --teile_matrix=matrix, but this seems not to work. So we call a function:
 teile_matrix=copy_matrix(matrix,teile_matrix)
 
 local rdim=arraydim(matrix)
 local cdim=arraydim(matrix[1])
 local i,j,t=1,1,1

 for i=1,rdim do
  for j=1,cdim do
   if matrix[i][j]==0 then
    teile_matrix[i][j]="--"
   else
    teile_matrix[i][j]=teile[t]
    t=t+1
   end
  end
 end

 return teile_matrix
end

--------------------------------------------------------------------------
function teile_matrix2teile(teile_matrix)

 --new global:
 shuffled_pieces={}

 local rdim=arraydim(teile_matrix)
 local cdim=arraydim(teile_matrix[1])
 local i,j,t=1,1,1
 
 for i=1,rdim do
  for j=1,cdim do
   if teile_matrix[i][j]=="--" then
    --do nothing
   else
    --write the found piece to the shuffled_pieces array:
    shuffled_pieces[t]=teile_matrix[i][j]
    t=t+1
   end
  end
 end

 return shuffled_pieces
end

--------------------------------------------------------------------------
--Main Function of the Permutation-Shuffle branch:
function shuffle_pieces_with_permutations(matrix,teile,num_perm_todo)

 --Search all Permutations:
 find_all_permutations(matrix)

 --Convert the teile to teile_matrix:
 teile2teile_matrix(matrix,teile)

 local i,t
 local num_perms=arraydim(permutations)

 --to catch the error, if num_perms<1:
 if num_perms>0 then
  for i=0,num_perm_todo do
   --if there were no permutations, this produces an error:
   t=random(1,num_perms)
   use_permutation(teile_matrix,permutations,t)
  end
 end
 
 --Convert the teile_matrix back to teile:
 teile_matrix2teile(teile_matrix,teile)

 return shuffled_pieces
end

---------------------------------------------------------------
--OUTPUT:

--Draw the Puzzlestones:
function draw_pieces(stone_coordinates,shuffled_pieces,art)

 local i
 local anz=anz_stones

  for i=1,anz do
  set_stone("st-puzzle"..art.."-"..shuffled_pieces[i], stone_coordinates[1][i], stone_coordinates[2][i])
 end
end

---------------------------------------------------------------
--END OF LIBPUZZLE CODE --
--------------------------
    ]]></el:luamain>
    <el:i18n>
    </el:i18n>
  </el:protected>
</el:level>