Skip to content

librity/ft_push_swap

Repository files navigation

42 São Paulo - push_swap

42 São Paulo License Code size in bytes Lines of code Top language Last commit

Build Norminette v3

An exercise on Sorting Algorithms and Efficiency in pure C.


📜 Table of Contents

🧐 About

Sorter

This program that takes a list of ints as arguments:

$ ./push_swap 7 1 8 9 4 2 6 5 3

Parses them into stack a:

|A: (top) 7 1 8 9 4 2 6 5 3 (bottom)|
|B: (top) (bottom)|

Then sorts them with stack b and the operations:

  • sa (swap a): Swap the first 2 elements at the top of stack a:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
sa
|A: (top) 6 2 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
  • sb (swap b): Swap the first 2 elements at the top of stack b:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
sb
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 1 7 8 9 4 (bottom)|
  • ss : sa and sb at the same time:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
sb
|A: (top) 6 2 5 3 (bottom)|
|B: (top) 1 7 8 9 4 (bottom)|
  • pa (push a): Take the first element at the top of b and put it at the top of a:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
pa
|A: (top) 7 2 6 5 3 (bottom)|
|B: (top) 1 8 9 4 (bottom)|
  • pb (push b): Take the first element at the top of a and put it at the top of b:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
pa
|A: (top) 6 5 3 (bottom)|
|B: (top) 2 7 1 8 9 4 (bottom)|
  • ra (rotate a): The first element becomes the last one:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
ra
|A: (top) 6 5 3 2 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
  • rb (rotate b): The first element becomes the last one:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
rb
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 1 8 9 4 7 (bottom)|
  • rr : ra and rb at the same time:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
rr
|A: (top) 6 5 3 2 (bottom)|
|B: (top) 1 8 9 4 7 (bottom)|
  • rra (reverse rotate a): The last element becomes the first one:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
rra
|A: (top) 3 2 6 5 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
  • rrb (reverse rotate b): The last element becomes the first one:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|

|A: (top) 2 6 5 3 (bottom)|
|B: (top) 4 7 1 8 9 (bottom)|
  • rrr : rra and rrb at the same time:
|A: (top) 2 6 5 3 (bottom)|
|B: (top) 7 1 8 9 4 (bottom)|
rrr
|A: (top) 3 2 6 5 (bottom)|
|B: (top) 4 7 1 8 9 (bottom)|

stack a must be sorted from smallest to largest:

|A: (top) 0 1 2 3 4 5 6 7 8 (bottom)|
|B: (top) (bottom)|
Is sorted: YES

The more operations it takes to sort, the lower your grade.

I enjoyed the project and learned a lot about doubly linked lists, sorting algorithms and optimizations. I especially liked the optimization aspect of it: it's not as easy as implementing some random sorting algorithm with a good average performance when the cost of swapping two arbitrary numbers is so high. The limitation of the stack and it's operations challenge you to think outside the box and understand the entire workflow of your solutions.

Checker

The checker program takes a list of ints as arguments and a list of operations from STDIN. It then executes all operations and checks if it properly sorts the stack:

$ ./checker 3 2 1 0
sa
rra
pb
KO
$ ./checker 3 2 1 0
rra
pb
sa
rra
pa
OK

✅ Checklist

Mandatory

  • DON'T TURN IN LIBS AS SUBMODULES
  • MAKEFILE EXPLICIT SOURCE FILES (echo M_SOURCES)
  • Make must compile without relinking
    • SHOOULDNT RECOMPILE/REARCHIVE OBJECTS WITH MAKE ALL
  • .linux file (42 Workspaces)
  • Test in workspaces
  • Follows norminette 3.3.51
  • Turn in Makefile, *.h, *.c , .linux , .gitignore
  • Makefile rules: $(NAME) all clean fclean re
  • Program name push_swap
  • Compiles with -Wall -Wextra -Werror
  • Should not quit unexpectedly (segmentation fault, bus error, double free, etc.)
  • All allocated heap memory properly freed, no memory leaks.
    • Check memory leaks with valgrind
  • Allowed functions:
    • read, write, malloc, free, exit
    • libft allowed
    • Your ft_printf (may be modified)
      • No printf from stdio.h
  • Handle arguments list of integers
    • If no arguments are specified (argc == 1) then exits with EXIT_SUCCESS
    • If arguments aren’t all int then exits with "Error\n" to STDERR_FILENO
    • If any argument is bigger than INT_MAX then exits with "Error\n" to STDERR_FILENO
    • If any argument is smaller than INT_MIN then exits with "Error\n" to STDERR_FILENO
    • If any argument is a duplicate then exits with "Error\n" to STDERR_FILENO
  • Implement stack a and b
  • Implement all operations
    • sa (swap a): Swap the first 2 elements at the top of stack a. Do nothing if there is only one or no elements.
    • sb (swap b): Swap the first 2 elements at the top of stack b. Do nothing if there is only one or no elements.
    • ss : sa and sb at the same time.
    • pa (push a): Take the first element at the top of b and put it at the top of a. Do nothing if b is empty.
    • pb (push b): Take the first element at the top of a and put it at the top of b. Do nothing if a is empty
    • ra (rotate a): Shift up all elements of stack a by 1. The first element becomes the last one.
    • rb (rotate b): Shift up all elements of stack b by 1. The first element becomes the last one.
    • rr : ra and rb at the same time.
    • rra (reverse rotate a): Shift down all elements of stack a by 1. The last element becomes the first one.
    • rrb (reverse rotate b): Shift down all elements of stack b by 1. The last element becomes the first one.
    • rrr : rra and rrb at the same time.
  • Print the list of instructions separated by '\n' to STDOUT_FILENO
  • Implement radix sort for 84% score.
  • Implement best rotation sort for 100% score.
  • Pass all testers

Bonus

  • Create a checker program
    • Takes the stack as argv
    • If the stack has any problem then exits with "Error\n" to STDERR_FILENO
    • Takes the operations from STDIN up to EOF (Ctrl+D) and saves them to a linked list.
    • If the operations have any problem then exits with "Error\n" to STDERR_FILENO
    • If the operations sort the stack then exits with "OK\n" to STDOUT_FILENO
    • If the operations doesn’t sort the stack then exits with "KO\n" to STDOUT_FILENO

🏁 Getting Started

🖥️ Installing

Clone the repo and build with make:

$ git clone --recurse-submodules https://github.com/librity/ft_push_swap.git
$ cd ft_push_swap
$ make

Sorter

You give it a list of integers and it prints the sorting instructions to STDOUT:

$ ./push_swap 7 1 8 9
ra
pb
rra
pa

You can also compile it with verbose mode for more details:

$ ./push_swap 7 1 8 9
|A: (top) 7 1 8 9 (bottom)|
|B: (top) (bottom)|
Is sorted: NO

NORMALIZED STACK
|A: (top) 1 0 2 3 (bottom)|
|B: (top) (bottom)|
Is sorted: NO

ra
|A: (top) 0 2 3 1 (bottom)|
|B: (top) (bottom)|
Is sorted: NO

pb
|A: (top) 2 3 1 (bottom)|
|B: (top) 0 (bottom)|
Is sorted: NO

rra
|A: (top) 1 2 3 (bottom)|
|B: (top) 0 (bottom)|
Is sorted: NO

pa
|A: (top) 0 1 2 3 (bottom)|
|B: (top) (bottom)|
Is sorted: YES

For more examples see the tester script:

$ ./scripts/run.sh

📝 Notes

I implemented many sorting algorithms before I found a good enough solution for a full grade.

Due to the nature of this problem something like insertion sort with chunks is one of the best options. The final sorter sends all elements to stack b by group/chunk, so the biggest are at the top and the smallest are at the bottom. They're not completely sorted but they're roughly were they need to be.

It then pushes all the numbers back to stack a while maintaining stack a in order. That means we need to rotate some number to the top of stack b and then rotate the greatest closest number to it to the top of stack a. It finds the number of stack b that will take the least amount of rotations before pushing it to stack a, then executes that rotation and pushes it.

Calculating the rotations is pretty straight forward:

  • The number of normal rotations (ra and rb) is the index of the number.
  • The number of reverse rotations (rra and rrb) is the size of the stack minus the index.
  • If we're doing the same type of rotation on both stacks we can optimize it further with rr and rrr.
  • The total number of operations is the sum of all the rotations.

With a few while loops we can find the best rotation for each number, and then find the best rotation for all numbers.

🛸 42 São Paulo

Part of the larger 42 Network, 42 São Paulo is a software engineering school that offers a healthy alternative to traditional education:

  • It doesn't have any teachers and classes.
  • Students learn by cooperating and correcting each other's work (peer-to-peer learning).
  • Its focus is as much on social skills as it is on technical skills.
  • It's completely free to anyone that passes its selection process - The Piscine

It's an amazing school, and I'm grateful for the opportunity.

📚 Resources

Linked Lists

Stacks

Tutorials

Sorting Algorithms

Quick Sort

Radix Sort

INT_MIN & INT_MAX

Value of INT_MAX is +2147483647.
Value of INT_MIN is -2147483648.

STDERR_FILENO

./push_swap > /dev/null 2>&1
./push_swap 1 2 3 > /dev/null 2>&1
./push_swap &> /dev/null
./push_swap 1 2 3 &> /dev/null

./push_swap 1 2 3 >stdout 2>stderr
./push_swap 3 14 -41 >stdout 2>stderr

Testers

Visualizers

Random Numbers

LIST=$(ruby -e "puts (1..500).to_a.shuffle.join(' ')"); ./push_swap $LIST | ./checker_linux $LIST
LIST=$(ruby -e "puts (1..500).to_a.shuffle.join(' ')"); ./push_swap $LIST | wc -l

Pseudo Random Number Generator

scp

cheat scp
scp -r -P 31280 coder@codeserver.42sp.org.br:~/path/to/folder ~/path/to/destination

lalloc - Listed Memory Allocation

A linked list in the control structure with all the allocated memory pointers. The interface function ft_lalloc allocates memory and adds the pointer to the list. The interface function ft_free_lalloc frees all pointers and the list.

Git submodule

Git Quirks

GDB Quirks

VSCode

C quirks

C Function Pointers

float (*function_name(unsigned id))(float value) {}

void (*function_name(char *operation))(void) {}
typedef void	(*t_name_cb)(void);
t_name_cb	function_name(char *operation)

Make Quirks

Bash Quirks

Send EOF to STDIN

SSH Key Fingerprint

Videos