[Tous langages] Atelier de Noël : dessinons un sapin !

♪ Mon beau sapin, roi des forêts ♫

a marqué ce sujet comme résolu.

Un sapin et la fonction x -> |x|, c’est presque la même chose non ?

1
2
3
4
5
N = 7
lentab = N*2+1
mat = [[' ' if j < abs(i-(lentab)//2) or j == N else '*' for i in range(lentab)]for j in range(N+1)]
mat[-1][lentab//2] = '#'
for line in mat: print(''.join(line))
+1 -0

Des adeptes de zsh (5.4.2) ?

1
2
3
4
5
6
7
8
❯ N=6;N=$[N-1];for i in {0..$N};do printf ' %.0s' {0..$[N-i]};printf '*%.0s' {0..$[2*i]};echo;done;printf ' %.0s' {0..$N};printf '#'
      *
     ***
    *****
   *******
  *********
 ***********
      #

Je pense qu’il y a pas mal plus court mais je vois mal comment mieux combiner ces printf.

+2 -0

Un sapin et la fonction x -> |x|, c’est presque la même chose non ?

Jeannot62600

Ah, je voulais faire un truc comme ça justement.

1
2
3
4
5
6
7
8
N = 5

for y in range(N):
    for x in range(-N+1, N):
        print('*' if abs(x) <= y else ' ', end='')
    print()

print(' ' * (N-1) + '#')

Un peu de proba, en pensant au triangle de Pascal.

1
2
3
4
5
6
from scipy.special import comb
N = 11
midtab = (N*2+1)//2
mat = [[' ' if (comb(j,i-midtab)+comb(j,-i+midtab)) == 0 or j == N else '*' for i in range(N*2+1)]for j in range(N+1)]
mat[-1][midtab] = '#'
for line in mat: print(''.join(line))
+0 -0

En C, à compiler sans options strictes car le programme ne respecte pas la norme (pas d’include des headers de fonctions utilisées)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
             int
            main(
           void) {
          int n, b,
         s;if(scanf(
        "%d",&n) != 1
       ||n<1)return 1;
      for(b=n-1, s=1; b
     >= 0; b--, s += 2){
    int i; for (i=0; i<b;
   i++) putchar(' ');for(i
  = 1;i<s;i++)putchar('*');
 puts("*"); } for(b = 1; b <
n;b++)putchar(' ');puts("#");
          return 0;
              }

gcc -o sapin sapin.c passe avec un warning sur le scanf. Mais le programme fonctionne bien chez moi:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ ./sapin.exe
10
         *
        ***
       *****
      *******
     *********
    ***********
   *************
  ***************
 *****************
*******************
         #
+22 -0

Pour continuer en C, un code particulièrement dégueulasse :

  1. N’employant que les variables argc et argv ;
  2. Avec deux petits goto, c’est plus joli ;
  3. Des pointeurs et calculs dans tous les sens ;
  4. Une virgule, c’est plus clair ;
  5. Cerise sur le gâteau : conforme à la norme. :p
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int
main(int argc, char **argv)
{
    sscanf(argv[1], "%d", &argc);
    argv[0] = calloc(1, argc * 2 + 1);
    argv[1] = argv[0] + argc;
    goto init;
loop:
    memset(argv[1] -= 1, '*', (argc - (argv[1] - argv[0])) * 2 + 1);
    printf("%s\n", argv[0]);
init:
    memset(argv[0], ' ', argc * 2);
    if (argv[1] > argv[0]) goto loop;
    printf("%s\n", (argv[0][argc - 1] = '#', argv[0]));
    return 0;
}
1
2
3
4
5
6
7
8
9
$ ./x 7
      *                
     ***
    ***** 
   *******     
  *********    
 ***********   
*************  
      #        
+0 -0

Un petit code C++ à base de templates.

 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
#include <cstdio>
#include <string>

template <int size, int pos>
struct Feuilles
{
    std::string operator()()
    {
        return Feuilles<size, pos - 2>()() + std::string(((size - pos) / 2), ' ') + std::string(pos, '*') + "\n";
    }
};

template <int size>
struct Feuilles<size, 1>
{
    std::string operator()()
    {
        return std::string((size / 2), ' ') + "*\n";
    }
};

template <int size>
struct Sapin
{
    std::string operator()()
    {
        return Feuilles<size, size>()() + std::string((size / 2), ' ') + "#\n";
    }
};

int main()
{
    // ne pas mettre de chiffre pair ou négatif
    puts(Sapin<5>()().c_str());
}

je tenterais bien une version full compile-time plus tard.

+0 -0

Voici une version Ruby en une ligne resserrée.

1
(n=gets.to_i).times{|i|puts" "*(n-i-1)+"*"*(1+2*i)+(i+1<n ?"":"\n"+" "*i+"#")}
+2 -0

Pour changer un peu, j’ai essayé de raviver mes souvenirs d’OCaml.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let sapin n =
  let rec line c = function
    | (0, 0) -> print_newline ()
    | (0, chars) -> print_char c; line c (0, chars-1)                                                                                   
    | (spaces, chars) -> print_char ' '; line c (spaces-1, chars)
  in let rec sapin_aux = function
    | 0 -> ()
    | i -> sapin_aux (i-1); line '*' (n-i, i*2-1)
  in sapin_aux n; line '#' (n-1, 1)
in sapin (int_of_string (read_line ()))

Ma contribution en Go :D

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import (
    "fmt"
    "strings"
)

func main() {
    var n int
    if _, err := fmt.Scan(&n); err == nil {
        for i := 1; i <= n; i++ {
            fmt.Println(strings.Repeat(" ", n-i) + strings.Repeat("*", i*2-1))
        }
        fmt.Println(strings.Repeat(" ", n-1) + "♯")
    }
}
+1 -0

Hmm je viens de passer deux heures sur ma version inutilement compliquée en python, mais je pense qu’il va m’en falloir trois de plus. Je vous promets un résultat absolument passionnant pour qui est friand de connaissances sur un sujet aussi inutile au commun des développeurs que volatile et complexe. :)

Patience, donc, je ne vous oublie pas, et je devrais avoir ça avant demain dans le fuseau horaire de la France sur la planète Terre.

+2 -0

Pas mal Karnaj. Je connais pas trop Ruby mais en abusant complètem un peu, on peut faire encore plus court :

1
puts" "*((n=gets.to_i).times{|i|puts" "*(n-i-1)+"*"*(1+2*i)}-1)+"#"

Édité : En fait on peut peut-être raccourcir encore plus avec des variables, comme avec ton fantastique (n=gets.to_i).

+2 -0

Chose promise chose dûe. Voici une version Python…

Ou plutôt devrais-je dire une version CPython…

Ou plutôt devrais-je dire une version bytecode de CPython…

Ou plutot devrais-je dire une version qui s’assemble elle-même en du bytecode compatible avec CPython ! :D

A priori, vu que j’utilise des opcodes assez simples, ça devrait marcher avec n’importe quelle version de Python 3, et peut-être même aussi avec Python 2.7, ou bien à vraiment très peu de choses près. Dans tous les cas je l’ai testée avec la distribution standard de python 3.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
import struct
import types
from dis import opmap as OPMAP


CODE = """
# Define sys.stdout.write as 'put' local function

    LOAD_CONST      0
    LOAD_CONST      None
    IMPORT_NAME     sys
    LOAD_ATTR       stdout
    LOAD_ATTR       write
    STORE_FAST      put

# Now print the N first lines of the Christmas Tree

    LOAD_CONST      0
    STORE_FAST      i

@begin_lines_loop

    LOAD_FAST       i
    LOAD_FAST       n
    COMPARE_OP      0
    POP_JUMP_IF_FALSE   >end_lines_loop

# Print the n-i spaces

    LOAD_FAST       n
    LOAD_FAST       i
    BINARY_SUBTRACT
    STORE_FAST      n_spaces

    LOAD_CONST      0
    STORE_FAST      j

@begin_spaces_loop

    LOAD_FAST       j
    LOAD_FAST       n_spaces
    COMPARE_OP      0
    POP_JUMP_IF_FALSE   >end_spaces_loop

    LOAD_FAST       put
    LOAD_CONST      ' '
    CALL_FUNCTION   1
    POP_TOP

    LOAD_FAST       j
    LOAD_CONST      1
    INPLACE_ADD
    STORE_FAST      j

    JUMP_ABSOLUTE   >begin_spaces_loop

@end_spaces_loop

# Print the i*2+1 stars

    LOAD_FAST       i
    LOAD_CONST      2
    BINARY_MULTIPLY
    LOAD_CONST      1
    BINARY_ADD
    STORE_FAST      n_stars

    LOAD_CONST      0
    STORE_FAST      j

@begin_stars_loop

    LOAD_FAST       j
    LOAD_FAST       n_stars
    COMPARE_OP      0
    POP_JUMP_IF_FALSE   >end_stars_loop

    LOAD_FAST       put
    LOAD_CONST      '*'
    CALL_FUNCTION   1
    POP_TOP

    LOAD_FAST       j
    LOAD_CONST      1
    INPLACE_ADD
    STORE_FAST      j

    JUMP_ABSOLUTE   >begin_stars_loop

@end_stars_loop

    LOAD_FAST       put
    LOAD_CONST      '\\n'
    CALL_FUNCTION   1
    POP_TOP

    LOAD_FAST       i
    LOAD_CONST      1
    INPLACE_ADD
    STORE_FAST      i

    JUMP_ABSOLUTE   >begin_lines_loop

@end_lines_loop

# Now print the foot of the Christmas Tree. (n spaces + '#' and a newline)

    LOAD_CONST      0
    STORE_FAST      i

@begin_foot_loop

    LOAD_FAST       i
    LOAD_FAST       n
    COMPARE_OP      0

    POP_JUMP_IF_FALSE   >end_foot_loop

    LOAD_FAST       put
    LOAD_CONST      ' '
    CALL_FUNCTION   1
    POP_TOP

    LOAD_FAST       i
    LOAD_CONST      1
    INPLACE_ADD
    STORE_FAST      i

    JUMP_ABSOLUTE   >begin_foot_loop

@end_foot_loop

    LOAD_FAST       put
    LOAD_CONST      '#\\n'
    CALL_FUNCTION   1
    RETURN_VALUE
"""

STACK_SIZE = 10


def get_index(lst, val):
    """Helper function: get the index of val in the lst.

    If val is not in lst, it is appended to it and its index is returned.

    """
    try:
        return lst.index(val)
    except ValueError:
        lst.append(val)
        return len(lst) - 1


def parse(code_lines, args):
    """Parse python pseudo-assembly code of a function.

    Params:
        code_lines: code as splitted lines.
        args: tuple giving the positional parameter names of the function.

    Returns:
        statements: a list of (offset, opcode_name, arg) tuples.
        var_names: the indexed list of local variable names used in the code.
        names: the indexed list of 'global' names used in the code.
        consts: the indexed list of constants used in the code.
        labels: a label -> byte offset mapping.

    """
    names = []
    consts = [None]
    var_names = list(args)
    labels = {}
    offset = 0
    statements = []

    for line in code_lines:
        line = line.strip()
        if not line or line.startswith('#'):
            continue
        if line.startswith('@'):
            labels[line[1:]] = offset
            continue
        opcode, *arg = line.split(maxsplit=1)
        if arg:
            arg = arg[0]
            if opcode in {'LOAD_FAST', 'STORE_FAST'}:
                arg = get_index(var_names, arg)
            elif opcode in {'IMPORT_NAME', 'LOAD_ATTR'}:
                arg = get_index(names, arg)
            elif opcode in {'LOAD_CONST'}:
                arg = get_index(consts, eval(arg))
            elif opcode in {'CALL_FUNCTION', 'COMPARE_OP'}:
                arg = eval(arg)

            statements.append((offset, opcode, arg))
            offset += 3
        else:
            statements.append((offset, opcode, None))
            offset += 1

    return statements, var_names, names, consts, labels


def resolve_addresses(statements, labels):
    """Replace :relative and >absolute args with their values."""
    for idx, elt in enumerate(statements):
        offset, opcode, arg = elt
        if not isinstance(arg, str):
            continue
        if arg.startswith(':'):
            rel_address = labels[arg[1:]] - offset - 3
            statements[idx] = (offset, opcode, rel_address)
        elif arg.startswith('>'):
            abs_address = labels[arg[1:]]
            statements[idx] = (offset, opcode, abs_address)


def dump_bytecode(statements):
    """Dump an list of (offset, opcode_name, arg) tuples to python bytecode."""
    op_arg = struct.Struct('<BH')
    op_no_arg = struct.Struct('B')
    return b''.join(
        (op_arg.pack(OPMAP[opcode], arg)
         if arg is not None else
         op_no_arg.pack(OPMAP[opcode])
         for _, opcode, arg in statements)
    )


def mk_func(code, name, args=(), stack_size=STACK_SIZE):
    """Assemble code to a Python function object."""
    statements, lvars, names, consts, labels = parse(code.splitlines(), args)
    resolve_addresses(statements, labels)
    bytecode = dump_bytecode(statements)

    code_obj = types.CodeType(
        len(args),      # argcount
        0,              # kwonlyargcount
        len(lvars),     # nlocals
        STACK_SIZE,     # stacksize
        0,              # flags
        bytecode,       # codestring
        tuple(consts),  # constants
        tuple(names),   # names
        tuple(lvars),   # varnames
        '<bytecode>',   # filename
        name,           # name (of the function)
        1,              # firstlineno
        b''             # lnotab

        # Too lazy to generate a valid lnotab, anyway the compiled function
        # WOULD NEVER throw an exception, right? As hardcore elite python
        # bytecode writers, we don't need this, as this is used only by the
        # poor souls who happen to write code with bugs in it.
    )
    return types.FunctionType(code_obj, globals())

if __name__ == '__main__':
    sapin = mk_func(CODE, 'sapin', ('n'))
    sapin(5)

Concrètement, le code est écrit dans un pseudo-langage d’assemblage similaire à ce que nous sort le module standard dis, sauf qu’il est un peu plus user-friendly dans le sens où les arguments des opcodes sont passés tels quels (noms de variables ou constantes littérales Python), et que j’ai rajouté un système de labels + adresses relatives (:label) et absolues (>label) pour les passer en argument des opcodes qui prennent des deltas ou des offsets comme paramètre.

Chose rigolote, le bytecode qui est écrit ici n’a pas d’équivalent Python. Il prend quelques raccourcis par rapport au standard (typiquement il ne génère/ne poppe pas de blocs quand il crée des boucles, d’ailleurs il n’utilise même pas les opcodes de Python qui servent à boucler, mais juste des jumps).

Je précise également que je me suis contenté du strict nécessaire pour faire marcher ce bytecode. Il y a des choses qui ne sont pas gérées automatiquement comme le calcul de la taille max de la stack (ça alourdirait beaucoup le code pour pas grand chose), ou encore le mapping interne qui associe à un opcode un numéro de ligne de code, pace que celui-ci a l’air relou à générer.

Voilà voilà. :)

+8 -0

Bon, j’ai essayé de faire le plus court possible avec Elixir mais pour l’instant j’ai pas trouvé “mieux” que ça :

1
2
3
import Enum
s=fn n->map flat_map(1..n,&([[32,n-&1],[?*,&1*2-1],[10,1]]))++[[32,n-1],[?#,1]],&apply(List,:duplicate,&1)end
IO.puts s.(String.to_integer hd System.argv)

Donc ne vous inquiétez pas trop les pythonneux, vous avez de la marge.

(Dans iex, remplacez la dernière ligne par quelque chose comme IO.puts s.(34))

lol. Tu dessines un sapin avec la commande G, tu te places au milieu avec M (Spécial ?) pour allez au milieu, tu descends d’une case, tu écris # et c’est fini ?

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte