HELP 🙌 - Corrigir exercícios de programação de forma automática (Obs. é pro meu TCC)

Contexto

Estou desenvolvendo um projeto de TCC para meu curso de Sistemas de Informação. De forma bem resumida estou criando uma plataforma online para aprender a programar de forma gratuita porém utilizando uma metologia e abordagem bem diferente. Não existe nem video aulas e nem tutores, apenas uma trilha a ser seguida junto com os exercícios. Todo o meu projeto é gamificado e da a sensação de estar jogando um jogo. Me baseei na 42 School uma escola de programação que segue exatamente essa metologia, porém está é presencial e apenas para aqueles que conseguirem a bolsa.

Problema 1

Já desenvolvi grande parte do front-end e do back-end e estou preso no core da minha aplicação, como saber se o código que o usuário enviou está correto com o que foi proposto pelo exercício ???? Resumindo, eu passo um exercício (que sera inicialmente apenas na linguagem C) e exijo uma resposta. Então o usuário envia seu código no qual acredita estar resolvendo o problema e eu preciso pegar isso e verificar se está correto e tem a resposta esperada, assim podendo aprovar sua requisição ou rejeitar.

Problema 2

(Essa parte foi adicionada depois) Segundamente, eu não sei como vou pegar essa resposta dos usuários. Lá na 42 é tudo muito automatizado e eles utilizam git. Porém como é presencial, eles possuem um servidor prórpio na intranet, cada escola processa a resposta daqueles alunos pelo que entendi. Como meu caso é online não sei como poderia ser resolvido. Queria muito tentar a ideia do git, mas não sei nem por onde começar. Caso contrário o usuário ia ter que escrever em algum editor embutido na plataforma e submeter o código que eu vou jogar dentro de algum arquivo pra ler posteriormente.

Ajuda 🙌🙌🙌

Sei que parece tolo, mas nunca fiz nada semelhante, não sei nem como pesquisar por isso, já tentei varías abordagens mas nada específico. Então alguem que tiver mais experiência ou já ouviu falar de algo seria de grande ajuda compartilhar esse conhecimento, qualquer link, referência, livro, vídeo, aplicação sera de muito valor!!!

A forma mais facil seria por meio de testes automatizados.

Uma plataforma que faz algo parecido é o exercism.org, que basicamente tem varios exercícios e cada um deles com o teste, assim que manda o codigo ele ja testa.

O problema seria que voce teria que dazer o teste para uma linguagem em específico, mas parece que o que voce quer é que a pessoa possa mandar em qualquer linguagem, né?

Nesse caso é um pouco mais complicado, voce teria que ter o teste para cada linguagem.

Minhas duas sugestões: tentar usar alguma IA para gerar o código de test e (vai que cola) e a outra seria fazer com que os outros usuários do sistema valide o código enviado, como disse que se inspirou na 42, acho que seria divertido. Seria um tipo de code review

Opa mano, obrigado por responder! Então eu cheguei a encontrar o Exercism nas minhas procuras, me parece que ele corrige o teste baseado em uma solução já pronta que eles tem. Tem a resolução do usuário, o teste e a resposta correta. A partir disso ele consegue dizer se está correto ou não. Só não sei se seria viável fazer algo do tipo e que resolvesse meu problema que é mais simples do que o exercism. Sobre as linguagens, vou apenas usar C, então ele não tem opção de escolher isso no momento. Em relação a outro usuário revisar não seria interessante pelo tempo, e também falha de correção, o próprio usuário pode estar errado. Realmente quero integrar a interação social dos usuários e até propor exercícios em dupla mas não é o foco no momento. Realmente tenho que focar em testes automatizados mas estou em branco, sem direção por onde começar até fazer isso realmente funcionar.

Dá pra fazer algo mais ou menos inspirado nesses sites de exercícios/desafios/etc.

Basicamente, para um problema, tem-se o formato da entrada e saída. Geralmente algo como "O programa deve ler X linhas, em cada linha tem N números. O programa deve fazer A, B e C e deve imprimir o resultado em tal formato".

Aí vc define as entradas, e as respectivas saídas. Depois, roda o programa para cada entrada e compara o resultado com a respectiva saída.

Aí entram outros problemas, já que muitos desses sites com "correção automática" podem dizer que o resultado está errado se tiver uma linha em branco a mais, um espaço a menos, etc. Tem que ver o quão rígido vc quer ser com esses detalhes.

Certifique-se também que os casos de teste são complicados o suficiente para que a pessoa não acerte por coincidência. Por exemplo, um programa que foi implementando errado, mas que por coincidência/sorte dá o resultado correto.

Essa parte é relativamente simples, o mais trabalhoso vai ser bolar tantos exercícios e casos de teste.

Siimm, muito bom seu questionamento @kht. Eu tinha pensado em uma solução que a própria 42 utiliza e muita galera faz algo la no github baseado nisso, ou seja, algumas dessas soluções estão disponíveis. Seria basicamente o seguinte, existem certas normas e padrões que todos exercícios devem seguir, e eles tem um programa pra cada padrão desse que é rodado antes mesmo de corrijir a resolução em si, se não passar o usuário nem chega e obter um resultado de seu código. Isso já elimina alguns problemas de código mal feito ou até mesmo uma coincidência como você apontou. Tudo ainda é um desafio muito grande de como vou chegar no final de tudo saindo do absoluto zero, mas com fé vai dar certo kkk, abraço amigo, valeu pelo comentário.

Já vi uma solução que era mais ou menos o seguinte:

O corretor era uma série de testes unitários (a maioria era com React Testing Library ou Cypress, pq eram projetos de front-end). O código da pessoa tinha que seguir alguns padrões, por exemplo ter um data-testid específico.

Aí o corretor era um script que rodava os testes unitários e gerava um JSON falando se a pessoa passou ou não no requisito. Tudo isso rodava em uma Github Action na hora que a pessoa abria um Pull Request no GitHub.

Era legal, mas o principal defeito é que você tem que limitar muitas coisas. O código da pessoa tem que ter um certo padrão pra corrigir certo.

@icaroharry, você comentou algo muito importante mano, até me levantou um questionamento, eu realmente não sei como vou pegar a resolução do usuário. Queria muito fazer com git, mas não faço ideia de como prosseguir, se tiver alguma referência de estudo eu agradeço demais. Além disso, gostei bastante desse exemplo que deu, no meu caso não seria em front-end mas sim em C, porém ja me serve de exemplo e me ajudou bastante!!
Não tenho exemplo porque era código proprietário :/ Mas a ideia do github é a seguinte: Vc cria um repositório base e nele vc configura uma action que vai rodar quando alguém abrir um PR. Aí quando o usuário abrir um PR vc vai ter acesso ao código dessa pessoa na action.
Interessante, vou dar uma estudada nesse action, será que pra escalar seria eficiente, ter um repositório recebendo varios PR ?? E como ficaria a questão do merge do código de muitas pessoas ?

Acho que tem dois caminhos para este problema:

  1. Usar uma biblioteca de testes para uma linguagem escolhida;
  2. Realizar testes de forma independente da linguagem.

Se seu curso for sobre frontend, acho que a alternativa 1 é a mais indicada. Caso contrário eu indico a segunda saída, que vou explicar como fazer agora.

obs: se seu curso for especificamente para uma linguagem só, pode considerar a alternativa 1 também, mas se decidir adicionar outra linguagem no futuro... boa sorte! Não será fácil converter a suíte de testes para uma maneira agnóstica de linguagem.

Vamos lá! Como eu implementaria a opção 2?

Eu iria utilizar STDIN e STOUT como parâmetros e saída para o programa do usuário. Esta é a maneira mais simples de comunicar entre processos e você pode enviar e receber qualquer tipo de dado: plaintext, json, binário, etc. Exemplos:

  • Números para fazer soma:
result="$(echo "10\n5" | somador_do_usuario)"
if [ "$result" -eq "15" ]; then
    echo "correto"
elif
   echo "errado"
fi

O | (pipe) do shell é o que permite a saída de um programa (stdout) ser a entrada (stdin) para outro.

  • Enviar e receber JSON
const child = child_process.spawn('programa_do_usuario');
child.stdin.write(JSON.stringify({ foo: 'bar' }));
child.stdin.write('\n'); // flush
child.stdout.on('data', (data) => {
    const response = JSON. parse(String(Buffer.from(data)));
  // validar json
}); 

Como pode perceber, essa solução independe da linguagem de programação usada pelo usuário e pela linguagem de programação dos testes. A ideia envolve executar o programa e se comunicar pelos pipes unix.

A única coisa a se acertar com os usuários é o formato esperado de entrada e saída de dados, ou seja, JSON (e qual o schema), plaintext e qual a ordem dos dados, etc. Você pode também utilizar argumentos aos programas (programa agr1 arg2) para indicar mais informações ao programa - exemplo qual teste rodar.

Isole o ambiente!

Idealmente (obrigatoriamente) você deve isolar o ambiente que você está rodando o código do terceiro, por questões de segurança e validação dos testes. Para isso, recomendo a utilização de containers, como o Docker ou Podman. Isso depende muito de cada situação, mas basicamente basta um Dockerfile que faz um git pull do código do usuário, compile (se necessário), rode e realize os testes.

Se tiver alguma pergunta ou algum outro exemplo/implementação sinta-se livre para perguntar e tentarei te ajudar. Muito legal seu projeto de aprendizado de programação gratuito. Sucesso.

obs: nenhum dos códigos foram testados, todos mostrados como exemplos.

Cara simplesmente incrível a forma que você explicou isso, ja me deu uma luz muito grande. Teve algo importante que talvez faltou de mencionar, vou até editar a postagem. A linguagem que vou utilizar é fixa, (No caso vai ser em C). Os exercícios seguirão um certo padrão evoluindo apenas em dificuldade. Gostei muito desse seu ponto sobre isolar o ambiente, acredito que vai ser muito neccessário pra mim, se tiver mais informações, ou até mesmo alguma fonte de estudo para me aprofundar nessa solução ficarei grato. Desde já, agradeço muito pelo comentário, @GRFreire. Abraço mano!

Esse video do DevSoutinho pode te dar uma luz.

Obrigado por tirar um tempo para ajudar, @iorjunior. Com certeza vou assistir o vídeo, vai ajudar demais mano!!

Fui aluno da 42 São Paulo e lá pude passar pela experiência de correção várias vezes. Nós alunos criamos várias teorias acerca do tema, mas fizemos algumas descobertas legais e vou compartilhar com você!

A norminette

A norminette é a responsável pelas frustações dos alunos, ela é o sistema que faz a correção dos exercícios. A construção da norminette é feita em Python e tem um repositório oficial no github onde você pode tentar se aprofundar

Algumas ideias que podem ser mais simples:

  • Verificar somente a saída do programa e ver se tem o retorno esperado
  • Verificar a saída do programa e alguns fatores do código Se você olhar a documentação, verá que existem algumas regras, como uma struct deve ter o nome começando por s_, funções devem ter 25 linhas e não podem ter mais de 5 funções em um arquivo,

Envio de projetos

Os projetos são dispostos através do git, então aqui vão algumas ideias

  • Utilizar um sistema de upload que envia o código para o servidor onde o sistema de correção fica
  • Criar repositórios no git e trabalhar com o uso de SSH para enviar os arquivos para esse repositório
Cara valeu demais pela contribuição, que daora esse seu comentário! Vou dar uma olhada pra ver se consigo o que to buscando. Sobre o envio de projetos to querendo muito fazer algo com serviço de git igual fazem lá, entretanto é muita coisa pra estudar e to na luta aqui. Se você puder deixar alguma forma de contato, discord por exemplo, e não for um incomodo eu gostaria de tirar algumas dúvidas futuramente, abraços!
Claro, seria um prazer poder ajudar, fala comigo no discord ```iaze_```

O que você pode fazer é receber as respostas por uma api em formato de json, no formato rest, assim independente da linguagem pode conseguir validar com os seus testes, só comparando a resposta correta com a recebida.

Olá, Ramon. Exatamente isso que já estava previsto de ser feito. Obrigado pela dica. Mas complementando sua resposta, você acha que é melhor fazer um serviço rest separado apenas para essa verificação ou junto com o backend da aplicação ??