Amateur Programmer Thread

google: how to save file vim

1 Like

how to undo vim

my vim wrote 100 copies of the same character on its own

:help vimtutor or something.

As an example: there are 34 distinct mahjong tiles; each tile[index] represents the number of that specific tile.

fn find_valid_hands(tiles: &[u8; 34]) -> Vec<[u8; 14]> {
        // Determine each triplet, sequence, and pair, then look at all potential
        // combinations to find the set of valid hands.

        let triplets: Vec<(usize, usize, usize)> = Yaku::find_triplets(&tiles)
            .iter()
            .map(|x| (*x, *x, *x))
            .collect();

        let sequences: Vec<(usize, usize, usize)> =
            Yaku::find_sequences(&tiles).iter().map(|x| *x).collect();

        let melds = [triplets, sequences].concat();
        let jantou = Yaku::find_pairs(&tiles);

        let mut hands = Vec::new();

        // A potential meld still has its respective tiles in the pool; either
        // reject a meld because of this lacking, or subtract them from the pool.
        let potential_meld = |pool: &mut [u8; 34],
                              cur_hand: &mut [u8; 34],
                              (a, b, c): (usize, usize, usize)|
         -> bool {
            if pool[a] == 0 {
                return false;
            } else {
                pool[a] -= 1;
                cur_hand[a] += 1;
            }

            if pool[b] == 0 {
                return false;
            } else {
                pool[b] -= 1;
                cur_hand[b] += 1;
            }

            if pool[c] == 0 {
                return false;
            } else {
                pool[c] -= 1;
                cur_hand[c] += 1;
            }

            true
        };

        for i in 0..melds.len() {
            let mut tmp_i = tiles.clone();
            let mut hnd_i = [0u8; 34];
            if !potential_meld(&mut tmp_i, &mut hnd_i, melds[i]) {
                continue;
            }

            for j in i..melds.len() {
                let mut tmp_j = tmp_i;
                let mut hnd_j = hnd_i;
                if !potential_meld(&mut tmp_j, &mut hnd_i, melds[j]) {
                    continue;
                }

                for k in j..melds.len() {
                    let mut tmp_k = tmp_j;
                    let mut hnd_k = hnd_j;
                    if !potential_meld(&mut tmp_k, &mut hnd_j, melds[k]) {
                        continue;
                    }

                    for l in k..melds.len() {
                        let mut tmp_l = tmp_k;
                        let mut hnd_l = hnd_k;
                        if !potential_meld(&mut tmp_l, &mut hnd_l, melds[l]) {
                            continue;
                        }

                        for m in 0..jantou.len() {
                            match tmp_l[jantou[m]] {
                                0 | 1 => continue,
                                _ => {
                                    hnd_l[jantou[m]] += 2;
                                    let mut specific_hand = [0u8; 14];
                                    let mut i = 0;
                                    for value in hnd_l {
                                        for _ in 0..value {
                                            specific_hand[i] = value;
                                            i += 1;
                                        }
                                    }

                                    hands.push(specific_hand);
                                }
                            }
                        }
                    }
                }
            }
        }

        hands
    }

Here's my thoughts on this absolute travesty of a function.

I likely shouldn't be computing triplets, sequences, and pairs within this function,

I am de-referencing these usize values: these are being copied I guess, but it feels like I'm just doing a bunch of worthless nonsense data-massaging that isn't really needed. No idea what this ends up as in ASM.

The potential meld closure doesn't even need to be a closure, it should just be a function (they likely are compiled down to the same thing). This triple if statement should likely just be a single function applied to each value. Futhermore, do I really want to be passing in mutable arrays? It's likely more performant.

That's an N^4 loop. The ranges of the loops can be cut down (the first meld can't be the last three melds because there has to be 4 melds in total), but that will underflow if melds are nothing, so that optimization just isn't included.

Each N^4 loop effectively does the exact same thing. It's the same piece of code copy pasted 4 times.

I think ideally, I would have an iterator that produces the set of combinations of the seq/triplets, and then produce that sets combinations with the pairs.

Aesthetically, this is painful. Look at how deep the scope goes.

Within regular riichi mahjong, you only have 14 tiles; I have feeling you can just precompute a LUT between those 14 and the respective yaku it earns. If I'm taking hands that are larger than 14 and are of variable size, I have to do this nonsense.

The cardinality of the set [u8; 34] where there are 14 tiles is finite. Say that we have some function M that takes a hand of N tiles, then reduces it to 14: there are multiple ways to do this, so we compute all possible reductions down to 14, then utilize the LUT.

This all needs to be cacheable too; Ideally, I can compute the difference between M(N) and M(N+1) and just lookup those new hands.

The code you produce should be so caked in this inane, worthless analysis that it becomes effectively impossible for you to do anything. It should feel like you're stuck in the mud. You should have no idea what the problem is. You should be entirely directionless and compensating with awful design decisions centered around "paradigms", "design patterns", to make you feel like you have some inkling of what you're doing.

Here's another high-level thing you could start doing: constructing a highly-tuned vim configuration. Can you post the cfg for you vim-airline statusline bro?

I hope you're using Neovim aswell...

Off the top of my head.

set ruler
set colorcolumn=80
set number
set expandtab

Very fascinating analysis - I do try to very critically analyze my code but more often than not it leads me to spending too much time paralyzed, not to mention the effort of sinking into the quicksand codebase I already have. I wish I could analyze my 20 line Flask server to this level, but unfortunately I don't understand a single line.

For the Game itself, I think my readability and performance average is higher than your sample function, but not necessarily my Design average.

My "Paradigm" is object oriented AIDs coupled with coroutine overuse and psychotic state machines, but most of the code isn't inherently mathematical in the same sense as the Mahjong Game.

i dont know what a vim airline is. I'm using the default centos vim settings

  1. you have routes that as far as I can tell end up at the same place.

route("/", methods=["GET"])

should collide with the other route("/").

  1. there's no versioning information as to your morgue file: it's just some generalized json blob. should likely have some sort formal, explicit serialization/deserialization type mechanism.

  2. storing just random files on the server prevents database like functionality, i.e. sorting for the largest score, whatever. If you're going to do it this way, I would just rather expose them through directory listing in the webserver configuration.

1 Like

It's storing and getting the json files from the same place (server directory)

I don't know what this means.

That is true and a good point. I do the sorting in game right now but that obviously isn't a very good idea.

jsonFile = open("morgue-"+name+'-'+date+".json","x")

1 Like

@app.route('/', methods=['GET', 'POST'])
def results():
    if request.method == 'POST':
        file = request.get_json()
        date = file['date'].replace('/','-').replace(' ','-').replace(':','-')
        name= file['name']
        jsonFile = open("morgue-"+name+'-'+date+".json","x")
        
        #remove header data
        jsonString = str(request.data)[2:-1]
        
        jsonFile.write(jsonString)
        
        return jsonString
  

You are matching both 'GET' and 'POST': you only are only doing something on the POST here. I am assuming the other request falls through to hello() because you have multiple functions bound to the same '/' GET handler.

If you have multiple formats of morgue files (say, you want to change something) you're going to need multiple sets of code to handle each of them. How do you differentiate between them? How do you validate you have the needed entities without crashing/failing out?

Right now I'm reading from json into a Morgue class, and I have the function FromJson<Dungeon.Morgue>(string json) inside a "Try Catch" block.

Let's say your utilizing a database on the server end to parse the morgue files: the code on both the client and server in regards to serialization/deserialization has to be effectively the same.

before switching to json i was using "binaryformatter" serialization but apparently that was insecure

Like an SQL database? My "serialization" right now is this:
image