hit counter

Timeline

My development logbook

Design Pattern

From p63 of the Design Pattern Slide by Peter Norvig.

It sounds like a very good advise.

Design Strategy: English Translation

-  To insure that your program says what you mean:

(1) Start with English description

(2) Write code from description

(3) Translate code back to English; compare to (1)

Example: (1), (2) from a Lisp textbook (1) “Given a list of monsters, determine the number that are swarms.”

(2) See next slide

(3) “Given a list of monsters, produce a 1 for a monster whose type is swarm, and a 0 for others. Then add up the numbers.”

9x9 Suduko Solver

Based on the previous version

It takes advantage of the parameter pattern matching of erlang, but not much else. There is a lot of room for improvement.

sudoku9x9 linenos:true (sudoku9x9.erl) download
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
-module(sudoku9x9).

 -include_lib("eunit/include/eunit.hrl").

-export([seed_matrix/0, matrix_ans/0]).
-export([run/0]).
-export([solve/1, take_row_and_column/3, pick_num/4, pos_to_xy/2]).
-export([is_well_formed/2]).

-define(SIDELEN, 9).


%% matrix representation
seed_matrix() ->
   [0, 0, 0, 0, 7, 0, 0, 1, 0,
    0, 0, 7, 1, 0, 5, 3, 0, 0,
    0, 0, 0, 9, 0, 0, 5, 2, 0,
    0, 0, 0, 6, 0, 0, 7, 3, 0,
    4, 0, 0, 0, 0, 0, 0, 0, 9,
    0, 5, 8, 0, 0, 7, 0, 0, 0,
    0, 2, 1, 0, 0, 9, 0, 0, 0,
    0, 0, 5, 4, 0, 3, 9, 0, 0,
    0, 3, 0, 0, 8, 0, 0, 0, 0].

matrix_ans() ->
   [5, 9, 2, 3, 7, 6, 8, 1, 4,
    8, 4, 7, 1, 2, 5, 3, 9, 6,
    1, 6, 3, 9, 4, 8, 5, 2, 7,
    2, 1, 9, 6, 5, 4, 7, 3, 8,
    4, 7, 6, 8, 3, 1, 2, 5, 9,
    3, 5, 8, 2, 9, 7, 6, 4, 1,
    7, 2, 1, 5, 6, 9, 4, 8, 3,
    6, 8, 5, 4, 1, 3, 9, 7, 2,
    9, 3, 4, 7, 8, 2, 1, 6, 5].



pos_to_xy(Pos, Len) when Pos rem Len =:= 0 ->
    {(Pos div Len), Len};
pos_to_xy(Pos, Len) ->
    {(Pos div Len) + 1, Pos rem Len}.


take_row_and_column(R, C, AnsMatrix) ->
    % io:format("take_row_and_column: ~p~n", [AnsMatrix]),
    RowVector = [ lists:nth((R-1)*?SIDELEN+X, AnsMatrix) || X <- lists:seq(1, ?SIDELEN)],
    ColVector = [ lists:nth((X-1)*?SIDELEN+C, AnsMatrix) || X <- lists:seq(1, ?SIDELEN)],
    {RowVector, ColVector}.

%% pick a number to test for coordinate x,y
pick_num(R, C, AnsMatrix, StartFromThisNum) ->
    {RV, CV} = take_row_and_column(R, C, AnsMatrix),
    pick_num2(R, C, RV, CV, StartFromThisNum, ?SIDELEN).
pick_num2(R, C, RV, CV, Num, Bound) when Num =< Bound ->
    F = fun(X) -> X == Num end,
    InRow = lists:any(F, RV),
    InCol = lists:any(F, CV),
    case { InRow, InCol } of
  {false, false} -> {ok, Num};
  _ -> pick_num2(R, C, RV, CV, Num+1, Bound)
    end;
pick_num2(_R, _C, _RV, _CV, _Num, _Bound) ->
    {err, 0}.



is_check_point(21, 9) ->
    {yes, [{1, 21}]};
is_check_point(24, 9) ->
     {yes, [{1, 21}, {4, 24}]};
is_check_point(27, 9) ->
     {yes, [{1, 21}, {4, 24}, {7, 27}]};
is_check_point(48, 9) ->
     {yes, [{1, 21}, {4, 24}, {7, 27}, {28, 48}]};
is_check_point(51, 9) ->
     {yes, [{1, 21}, {4, 24}, {7, 27}, {28, 48}, {31, 51}]};
is_check_point(54, 9) ->
    {yes, [{1, 21}, {4, 24}, {7, 27}, {28, 48}, {31, 51}, {34, 54}]};
is_check_point(75, 9) ->
    {yes, [{1, 21}, {4, 24}, {7, 27}, {28, 48}, {31, 51}, {34, 54}, {55, 75}]};
is_check_point(78, 9) ->
    {yes, [{1, 21}, {4, 24}, {7, 27}, {28, 48}, {31, 51}, {34, 54}, {55, 75}, {58, 78}]};
is_check_point(81, 9) ->
    {yes, [{1, 21}, {4, 24}, {7, 27}, {28, 48}, {31, 51}, {34, 54}, {55, 75}, {58, 78}, {61 ,81}]};
is_check_point(_,_) ->
    {no, []}.

is_well_formed(M, Pts) when is_list(Pts) ->
    F = fun({X, Y}) -> is_well_formed_checksum(M, X, Y) and is_well_formed_uniq(M, X, Y) end,
    lists:all(F, Pts).

is_well_formed_checksum(M, 1, 21) ->
    case lists:nth(1, M) + lists:nth(2, M) + lists:nth(3, M) + lists:nth(10, M) + lists:nth(11, M) + lists:nth(12, M) + lists:nth(19, M) + lists:nth(20, M) + lists:nth(21, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 28, 48) ->
    case lists:nth(28, M) + lists:nth(29, M) + lists:nth(30, M) + lists:nth(37, M) + lists:nth(38, M) + lists:nth(39, M) + lists:nth(46, M) + lists:nth(47, M) + lists:nth(48, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 55, 75) ->
    case lists:nth(55, M) + lists:nth(56, M) + lists:nth(57, M) + lists:nth(64, M) + lists:nth(65, M) + lists:nth(66, M) + lists:nth(73, M) + lists:nth(74, M) + lists:nth(75, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 4, 24) ->
    case lists:nth(4, M) + lists:nth(5, M) + lists:nth(6, M) + lists:nth(13, M) + lists:nth(14, M) + lists:nth(15, M) + lists:nth(22, M) + lists:nth(23, M) + lists:nth(24, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 31, 51) ->
    case lists:nth(31, M) + lists:nth(32, M) + lists:nth(33, M) + lists:nth(40, M) + lists:nth(41, M) + lists:nth(42, M) + lists:nth(49, M) + lists:nth(50, M) + lists:nth(51, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 58, 78) ->
    case lists:nth(58, M) + lists:nth(59, M) + lists:nth(60, M) + lists:nth(67, M) + lists:nth(68, M) + lists:nth(69, M) + lists:nth(76, M) + lists:nth(77, M) + lists:nth(78, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 7, 27) ->
    case lists:nth(7, M) + lists:nth(8, M) + lists:nth(9, M) + lists:nth(16, M) + lists:nth(17, M) + lists:nth(18, M) + lists:nth(25, M) + lists:nth(26, M) + lists:nth(27, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 34, 54) ->
    case lists:nth(34, M) + lists:nth(35, M) + lists:nth(36, M) + lists:nth(43, M) + lists:nth(44, M) + lists:nth(45, M) + lists:nth(52, M) + lists:nth(53, M) + lists:nth(54, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(M, 61, 81) ->
    case lists:nth(61, M) + lists:nth(62, M) + lists:nth(63, M) + lists:nth(70, M) + lists:nth(71, M) + lists:nth(72, M) + lists:nth(79, M) + lists:nth(80, M) + lists:nth(81, M) of
        45 -> true;
        _ -> false
    end;
is_well_formed_checksum(_, _, _) ->
    false.

is_well_formed_uniq(M, 1, 21) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(1, M) , lists:nth(2, M) , lists:nth(3, M) , lists:nth(10, M) , lists:nth(11, M) , lists:nth(12, M) , lists:nth(19, M) , lists:nth(20, M) , lists:nth(21, M)]);
is_well_formed_uniq(M, 28, 48) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(28, M) , lists:nth(29, M) , lists:nth(30, M) , lists:nth(37, M) , lists:nth(38, M) , lists:nth(39, M) , lists:nth(46, M) , lists:nth(47, M) , lists:nth(48, M)]);
is_well_formed_uniq(M, 55, 75) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(55, M) , lists:nth(56, M) , lists:nth(57, M) , lists:nth(64, M) , lists:nth(65, M) , lists:nth(66, M) , lists:nth(73, M) , lists:nth(74, M) , lists:nth(75, M)]);
is_well_formed_uniq(M, 4, 24) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(4, M) , lists:nth(5, M) , lists:nth(6, M) , lists:nth(13, M) , lists:nth(14, M) , lists:nth(15, M) , lists:nth(22, M) , lists:nth(23, M) , lists:nth(24, M)]);
is_well_formed_uniq(M, 31, 51) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(31, M) , lists:nth(32, M) , lists:nth(33, M) , lists:nth(40, M) , lists:nth(41, M) , lists:nth(42, M) , lists:nth(49, M) , lists:nth(50, M) , lists:nth(51, M)]);
is_well_formed_uniq(M, 58, 78) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(58, M) , lists:nth(59, M) , lists:nth(60, M) , lists:nth(67, M) , lists:nth(68, M) , lists:nth(69, M) , lists:nth(76, M) , lists:nth(77, M) , lists:nth(78, M)]);
is_well_formed_uniq(M, 7, 27) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(7, M) , lists:nth(8, M) , lists:nth(9, M) , lists:nth(16, M) , lists:nth(17, M) , lists:nth(18, M) , lists:nth(25, M) , lists:nth(26, M) , lists:nth(27, M)]);
is_well_formed_uniq(M, 34, 54) ->
    [1,2,3,4,5,6,7,8,9]==lists:sort([lists:nth(34, M) , lists:nth(35, M) , lists:nth(36, M) , lists:nth(43, M) , lists:nth(44, M) , lists:nth(45, M) , lists:nth(52, M) , lists:nth(53, M) , lists:nth(54, M)]);
is_well_formed_uniq(M, 61, 81) ->
   [1,2,3,4,5,6,7,8,9]==lists:sort([ lists:nth(61, M) , lists:nth(62, M) , lists:nth(63, M) , lists:nth(70, M) , lists:nth(71, M) , lists:nth(72, M) , lists:nth(79, M) , lists:nth(80, M) , lists:nth(81, M)]);
is_well_formed_uniq(_, _, _) ->
    false.

solve(Seed) ->
    %% round() is needed to convert it to integer
    solve(Seed, 1, [], Seed, 1, ?SIDELEN, []).

solve_backtrack(Seed, _PosInSeed, _Begin, _Rest, _LastNum, SideLen, []) ->
    solve(Seed, 1, [], Seed, 1, SideLen, []); % initial step
solve_backtrack(Seed, _PosInSeed, Ans, _Rest, _LastNum, SideLen, [LastStep|Tail]) ->
    {LasPosInSeed, LastPosInSeedNum} = LastStep,
    PrevAns = lists:sublist(Ans, 1, LasPosInSeed-1),
    Rest = lists:sublist(Seed, LasPosInSeed, length(Seed)-LasPosInSeed+1),
    solve(Seed, LasPosInSeed, PrevAns, Rest, LastPosInSeedNum, SideLen, Tail).

solve(_Seed, EndPos, Ans, [], _, SideLen, _Steps) when EndPos == SideLen * SideLen + 1  ->
    %% pass the end post
    Ans;
solve(Seed, PosInSeed, Ans, [H|Rest], LastNum, SideLen, Steps) when H =:= 0
    ->
    {R, C} = pos_to_xy(PosInSeed, SideLen),
    CurrentMatrix = lists:sublist(Ans, 1, PosInSeed-1) ++ lists:sublist(Seed, PosInSeed, length(Seed)-PosInSeed+1),
    % io:format("Working on matrix ~p~n", [CurrentMatrix]),
    case pick_num(R, C, CurrentMatrix, LastNum+1) of
      {ok, NewH} ->
            case is_check_point(PosInSeed, SideLen) of
      {yes, Pts} ->
          ProposedMatrix = lists:sublist(Ans, 1, PosInSeed-1) ++ [NewH] ++ lists:sublist(Seed, PosInSeed+1, length(Seed)-PosInSeed+1),
          case is_well_formed(ProposedMatrix, Pts) of
          true -> %% move on
              % io:format("Got it ~p. Next pos ~p~n", [NewH, PosInSeed+1]),
              solve(Seed, PosInSeed+1, Ans++[NewH], Rest, 0, SideLen, [{PosInSeed, NewH}]++Steps);
          false -> %% backtraked
              % io:format("backtracked (1) at ~p. Tried ~p at (~p,~p)~n", [ PosInSeed, NewH, R, C ]),
              solve_backtrack(Seed, -1, Ans, [H]++Rest, -1, SideLen, Steps)
          end;
      {no, _} -> % continue
          % io:format("backtracked (2) at ~p. Tried ~p at (~p,~p)~n", [ PosInSeed, NewH, R, C ]),
          solve(Seed, PosInSeed+1, Ans++[NewH], Rest, 0, SideLen, [{PosInSeed, NewH}]++Steps)
       end;
      _ -> %% backtracked
      % io:format("backtracked (3) at ~p. No soln at (~p,~p)~n", [ PosInSeed, R, C ]),
            solve_backtrack(Seed, -1, Ans, [H]++Rest, -1, SideLen, Steps)
    end;
solve(Seed, PosInSeed, Begin, [H|Rest], _TriedNumAtThisPos, SideLen, Steps) ->
    %% seeded value, keep moving on
    solve(Seed, PosInSeed+1, Begin ++ [H], Rest, 0, SideLen, Steps).

run() ->
    Ans = solve(seed_matrix()),
    io:format("Ans ~p~n", [Ans]).

unit_test() ->
    [?assert(pos_to_xy(1, 4) =:= {1, 1}),
     ?assert(pos_to_xy(4, 4) =:= {1, 4}),
     ?assert(pos_to_xy(7, 4) =:= {2, 3}),
     ?assert(pick_num2(1, 1, [1,0], [0,0], 1, 2) =:= {ok, 2}),
     ?assert(pick_num2(1, 1, [1,0], [1,2], 1, 2) =:= {err, 0}),
     ?assert(pick_num2(1, 1, [1,0], [1,2], 5, 2) =:= {err, 0})
    ].


Octopress Deploy Rejected

Got this error when running ‘rake deploy’

1
2
3
4
5
6
7
8
9
10
11
12
13
## Pushing generated _deploy website
Counting objects: 9767, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5297/5297), done.
Writing objects: 100% (7245/7245), 3.98 MiB | 121.00 KiB/s, done.
Total 7245 (delta 3406), reused 2619 (delta 423)
remote: error: GH001: Large files detected.
remote: error: Trace: 2cb44393c3f81f907b200d71379dc45c
remote: error: See http://git.io/iEPt8g for more information.
remote: error: File downloads/code/erlang/out3.log is 122.48 MB; this exceeds GitHub's file size limit of 100 MB
To git@github.com:kongakong/kongakong.github.com
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@github.com:kongakong/kongakong.github.com'

It is because I have left a log file in the downloads/code folder. Although I did not check in the file, it is copied into the _deploy folder and get committed for deployment.

Here is a solution:

1) Make sure the unwanted file is removed

2) Add *.log to .gitignore to permanently ignore this file type.

3) Download bfg to remove the redundant, big files in the _deploy folder

Here is the partial output from bfg

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
These are your protected commits, and so their contents will NOT be altered:

 * commit d8b90a41 (protected by 'HEAD')

Cleaning
--------

Found 910 commits
Cleaning commits:       100% (910/910)
Cleaning commits completed in 3,051 ms.

Updating 1 Ref
--------------

    Ref                 Before     After   
    ---------------------------------------
    refs/heads/master | d8b90a41 | 1c0ad187

Updating references:    100% (1/1)
...Ref update completed in 16 ms.

Commit Tree-Dirt History
------------------------

    Earliest                                              Latest
    |                                                          |
    ...........................................................D

    D = dirty commits (file tree fixed)
    m = modified commits (commit message or parents changed)
    . = clean commits (no changes to file tree)

                            Before     After   
    -------------------------------------------
    First modified commit | 6725709a | f2971f05
    Last dirty commit     | cbc09c38 | 774bb768


In total, 14 object ids were changed - a record of these will be written to:

    /Users/antkong/octopress/_deploy.bfg-report/2014-03-22T21-03/object-id-map.old-new.txt

BFG run is complete!

4) Now you can run the rake deploy again

4x4 Suduko Solver in Erlang

Very crappy code written in a couple of hours.

It implements backtracking in a procedural way.

sudoku2x2 linenos:true (sudoku4x4.erl) download
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
-module(sudoku4x4).

 -include_lib("eunit/include/eunit.hrl").

-export([seed_matrix2/0]).
-export([run/0]).
-export([element/3, solve/1, take_row_and_column/4, pick_num/4, pos_to_xy/2]).


%% matrix representation
seed_matrix2() ->
    [0, 0, 1, 0,
     4, 0, 0, 0,
     0, 0, 0, 2,
     0, 4, 0, 0].

matrix2_ans() ->
    [3, 2, 1, 4,
     4, 1, 2, 3,
     1, 3, 4, 2,
     2, 4, 3, 1].


element(X, Y, _) when X < 0; Y < 0 ->
    nil;
element(X, Y, Matrix) ->
    % make sure it is zero-indexed
    lists:nth(X*9 + Y - 1, Matrix).

pos_to_xy(Pos, Len) when Pos rem Len =:= 0 ->
    {(Pos div Len), Len};
pos_to_xy(Pos, Len) ->
    {(Pos div Len) + 1, Pos rem Len}.

take_row_and_column(R, C, AnsMatrix) ->
    SideLen =  round(math:sqrt(length(AnsMatrix))),
    take_row_and_column(R, C, AnsMatrix, SideLen).

take_row_and_column(R, C, AnsMatrix, SideLen) ->
    % io:format("take_row_and_column: ~p~n", [AnsMatrix]),
    RowVector = [ lists:nth((R-1)*SideLen+X, AnsMatrix) || X <- lists:seq(1, SideLen)],
    ColVector = [ lists:nth((X-1)*SideLen+C, AnsMatrix) || X <- lists:seq(1, SideLen)],
    {RowVector, ColVector}.

%% pick a number to test for coordinate x,y
pick_num(R, C, AnsMatrix, StartFromThisNum) ->
    SideLen =  round(math:sqrt(length(AnsMatrix))),
    {RV, CV} = take_row_and_column(R, C, AnsMatrix, SideLen),
    pick_num2(R, C, RV, CV, StartFromThisNum, SideLen).
pick_num2(R, C, RV, CV, Num, Bound) when Num =< Bound ->
    F = fun(X) -> X == Num end,
    InRow = lists:any(F, RV),
    InCol = lists:any(F, CV),
    case { InRow, InCol } of
  {false, false} -> {ok, Num};
  _ -> pick_num2(R, C, RV, CV, Num+1, Bound)
    end;
pick_num2(_R, _C, _RV, _CV, _Num, _Bound) ->
    {err, 0}.

solve(Seed) ->
    %% round() is needed to convert it to integer
    solve(Seed, 1, [], Seed, 1, round(math:sqrt(length(Seed))), []).

solve_backtrack(Seed, PosInSeed, _Begin, Rest, LastNum, SideLen, []) ->
    solve(Seed, 1, [], Seed, 1, SideLen, []); % initial step
solve_backtrack(Seed, _PosInSeed, Ans, _Rest, _LastNum, SideLen, [LastStep|Tail]) ->
    {LasPosInSeed, LastPosInSeedNum} = LastStep,
    PrevAns = lists:sublist(Ans, 1, LasPosInSeed-1),
    Rest = lists:sublist(Seed, LasPosInSeed, length(Seed)-LasPosInSeed+1),
    solve(Seed, LasPosInSeed, PrevAns, Rest, LastPosInSeedNum, SideLen, Tail).

is_check_point(6, 4) ->
    {yes, [{1, 6}]};
is_check_point(8, 4) ->
    {yes, [{1, 6}, {3, 8}]};
is_check_point(14, 4) ->
    {yes, [{1, 6}, {3, 8}, {9, 14}]};
is_check_point(16, 4) ->
    {yes, [{1, 6}, {3, 8}, {9, 14}, {11, 16}]};
is_check_point(_,_) ->
    {no, []}.

is_well_formed(M, Pts) when is_list(Pts) ->
    F = fun({X, Y}) -> is_well_formed(M, X, Y) end,
    lists:all(F, Pts).
is_well_formed(M, 1, 6) ->
    case lists:nth(1, M) + lists:nth(2, M) + lists:nth(5, M) + lists:nth(6, M) of
  10 -> true;
  _ -> false
    end;
is_well_formed(M, 3, 8) ->
    case lists:nth(3, M) + lists:nth(4, M) + lists:nth(7, M) + lists:nth(8, M) of
  10 -> true;
  _ -> false
    end;
is_well_formed(M, 9, 14) ->
    case lists:nth(9, M) + lists:nth(10, M) + lists:nth(13, M) + lists:nth(14, M) of
  10 -> true;
  _ -> false
    end;    
is_well_formed(M, 11, 16) ->
    case lists:nth(11, M) + lists:nth(12, M) + lists:nth(15, M) + lists:nth(16, M) of
  10 -> true;
  _ -> false
    end;
is_well_formed(_, _, _) ->
    false.


solve(Seed, PosInSeed, Ans, [H|Remain], LastNum, SideLen, Steps) when H =:= 0
    ->
    {R, C} = pos_to_xy(PosInSeed, SideLen),
    CurrentMatrix = lists:sublist(Ans, 1, PosInSeed-1) ++ lists:sublist(Seed, PosInSeed, length(Seed)-PosInSeed+1),
    case pick_num(R, C, CurrentMatrix, LastNum+1) of
      {ok, NewH} ->
            case is_check_point(PosInSeed, SideLen) of
      {yes, Pts} ->
          ProposedMatrix = lists:sublist(Ans, 1, PosInSeed-1) ++ [NewH] ++ lists:sublist(Seed, PosInSeed+1, length(Seed)-PosInSeed+1),
          case is_well_formed(ProposedMatrix, Pts) of
          true -> %% move on
              solve(Seed, PosInSeed+1, Ans++[NewH], Remain, 0, SideLen, [{PosInSeed, NewH}]++Steps);
          false -> %% backtraked
              solve_backtrack(Seed, -1, Ans, [H]++Remain, -1, SideLen, Steps)
          end;
      {no, _} -> % continue
          solve(Seed, PosInSeed+1, Ans++[NewH], Remain, 0, SideLen, [{PosInSeed, NewH}]++Steps)
       end;
      _ -> %% backtracked
            solve_backtrack(Seed, -1, Ans, [H]++Remain, -1, SideLen, Steps)
    end;
solve(Seed, PosInSeed, Begin, [H|Remain], _TriedNumAtThisPos, SideLen, Steps) ->
    %% seeded value, move on
    solve(Seed, PosInSeed+1, Begin ++ [H], Remain, 0, SideLen, Steps);
solve(_Seed, EndPos, Ans, _, _, SideLen, _Steps) when EndPos == SideLen*SideLen+1 ->
    Ans.


run() ->
    Ans = solve(seed_matrix2()),
    io:format("Ans ~p~n", [Ans]).

unit_test() ->
    [?assert(pos_to_xy(1, 4) =:= {1, 1}),
     ?assert(pos_to_xy(4, 4) =:= {1, 4}),
     ?assert(pos_to_xy(7, 4) =:= {2, 3}),
     ?assert(pick_num2(1, 1, [1,0], [0,0], 1, 2) =:= {ok, 2}),
     ?assert(pick_num2(1, 1, [1,0], [1,2], 1, 2) =:= {err, 0}),
     ?assert(pick_num2(1, 1, [1,0], [1,2], 5, 2) =:= {err, 0})
    ].




Sometimes It Is Not Obvious at the Beginning

I want to create a function for use as a predicate in the function lists:any. I try it first in erlang shell,

137> F = fun(X) -> X == 1 end.
#Fun<erl_eval.6.80484245>

It works as expected:

138> lists:any(F, [2, 3]).
false
139> lists:any(F, [2, 1, 3]).
true

Then I want to change the condition to something else, and I got an error message:

140> F = fun(X) -> X /= 1 end.
** exception error: no match of right hand side value #Fun<erl_eval.6.80484245>

My first thought: It is interesting… Did I mix up the ‘not equal’ operator in erlang? Maybe it is not ‘/=’?

It turns out it is because the code at 140 is effectively an attempt to rebound the F variable. The error message is actually about this rather than the operator. I do not usually test code in the erlang shell, so it took me a while to figure it out.